diff options
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/Kconfig | 17 | ||||
-rw-r--r-- | drivers/video/Makefile | 1 | ||||
-rw-r--r-- | drivers/video/console/softcursor.c | 3 | ||||
-rw-r--r-- | drivers/video/da8xx-fb.c | 170 | ||||
-rw-r--r-- | drivers/video/exynos/exynos_dp_core.c | 697 | ||||
-rw-r--r-- | drivers/video/exynos/exynos_dp_core.h | 21 | ||||
-rw-r--r-- | drivers/video/exynos/exynos_dp_reg.c | 77 | ||||
-rw-r--r-- | drivers/video/exynos/exynos_dp_reg.h | 3 | ||||
-rw-r--r-- | drivers/video/fsl-diu-fb.c | 201 | ||||
-rw-r--r-- | drivers/video/s3c-fb.c | 24 | ||||
-rw-r--r-- | drivers/video/sh_mipi_dsi.c | 69 | ||||
-rw-r--r-- | drivers/video/sh_mobile_lcdcfb.c | 74 | ||||
-rw-r--r-- | drivers/video/sh_mobile_lcdcfb.h | 1 | ||||
-rw-r--r-- | drivers/video/ssd1307fb.c | 396 |
14 files changed, 1198 insertions, 556 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index d08d7998a4a..9c31277b3a8 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -2045,7 +2045,7 @@ config FB_S3C_DEBUG_REGWRITE bool "Debug register writes" depends on FB_S3C ---help--- - Show all register writes via printk(KERN_DEBUG) + Show all register writes via pr_debug() config FB_S3C2410 tristate "S3C2410 LCD framebuffer support" @@ -2442,4 +2442,19 @@ config FB_SH_MOBILE_MERAM Up to 4 memory channels can be configured, allowing 4 RGB or 2 YCbCr framebuffers to be configured. +config FB_SSD1307 + tristate "Solomon SSD1307 framebuffer support" + depends on FB && I2C + depends on OF + depends on GENERIC_GPIO + select FB_SYS_FOPS + select FB_SYS_FILLRECT + select FB_SYS_COPYAREA + select FB_SYS_IMAGEBLIT + select FB_DEFERRED_IO + select PWM + help + This driver implements support for the Solomon SSD1307 + OLED controller over I2C. + endmenu diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 23e948ebfab..768a137a1ba 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -161,6 +161,7 @@ obj-$(CONFIG_FB_BFIN_7393) += bfin_adv7393fb.o obj-$(CONFIG_FB_MX3) += mx3fb.o obj-$(CONFIG_FB_DA8XX) += da8xx-fb.o obj-$(CONFIG_FB_MXS) += mxsfb.o +obj-$(CONFIG_FB_SSD1307) += ssd1307fb.o # the test framebuffer is last obj-$(CONFIG_FB_VIRTUAL) += vfb.o diff --git a/drivers/video/console/softcursor.c b/drivers/video/console/softcursor.c index 25f835bf3d7..46dd8f5d2e9 100644 --- a/drivers/video/console/softcursor.c +++ b/drivers/video/console/softcursor.c @@ -35,8 +35,7 @@ int soft_cursor(struct fb_info *info, struct fb_cursor *cursor) dsize = s_pitch * cursor->image.height; if (dsize + sizeof(struct fb_image) != ops->cursor_size) { - if (ops->cursor_src != NULL) - kfree(ops->cursor_src); + kfree(ops->cursor_src); ops->cursor_size = dsize + sizeof(struct fb_image); ops->cursor_src = kmalloc(ops->cursor_size, GFP_ATOMIC); diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c index 80665f66ac1..46534e00fe0 100644 --- a/drivers/video/da8xx-fb.c +++ b/drivers/video/da8xx-fb.c @@ -213,62 +213,51 @@ static struct fb_fix_screeninfo da8xx_fb_fix __devinitdata = { .accel = FB_ACCEL_NONE }; -struct da8xx_panel { - const char name[25]; /* Full name <vendor>_<model> */ - unsigned short width; - unsigned short height; - int hfp; /* Horizontal front porch */ - int hbp; /* Horizontal back porch */ - int hsw; /* Horizontal Sync Pulse Width */ - int vfp; /* Vertical front porch */ - int vbp; /* Vertical back porch */ - int vsw; /* Vertical Sync Pulse Width */ - unsigned int pxl_clk; /* Pixel clock */ - unsigned char invert_pxl_clk; /* Invert Pixel clock */ -}; - -static struct da8xx_panel known_lcd_panels[] = { +static struct fb_videomode known_lcd_panels[] = { /* Sharp LCD035Q3DG01 */ [0] = { - .name = "Sharp_LCD035Q3DG01", - .width = 320, - .height = 240, - .hfp = 8, - .hbp = 6, - .hsw = 0, - .vfp = 2, - .vbp = 2, - .vsw = 0, - .pxl_clk = 4608000, - .invert_pxl_clk = 1, + .name = "Sharp_LCD035Q3DG01", + .xres = 320, + .yres = 240, + .pixclock = 4608000, + .left_margin = 6, + .right_margin = 8, + .upper_margin = 2, + .lower_margin = 2, + .hsync_len = 0, + .vsync_len = 0, + .sync = FB_SYNC_CLK_INVERT | + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, }, /* Sharp LK043T1DG01 */ [1] = { - .name = "Sharp_LK043T1DG01", - .width = 480, - .height = 272, - .hfp = 2, - .hbp = 2, - .hsw = 41, - .vfp = 2, - .vbp = 2, - .vsw = 10, - .pxl_clk = 7833600, - .invert_pxl_clk = 0, + .name = "Sharp_LK043T1DG01", + .xres = 480, + .yres = 272, + .pixclock = 7833600, + .left_margin = 2, + .right_margin = 2, + .upper_margin = 2, + .lower_margin = 2, + .hsync_len = 41, + .vsync_len = 10, + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .flag = 0, }, [2] = { /* Hitachi SP10Q010 */ - .name = "SP10Q010", - .width = 320, - .height = 240, - .hfp = 10, - .hbp = 10, - .hsw = 10, - .vfp = 10, - .vbp = 10, - .vsw = 10, - .pxl_clk = 7833600, - .invert_pxl_clk = 0, + .name = "SP10Q010", + .xres = 320, + .yres = 240, + .pixclock = 7833600, + .left_margin = 10, + .right_margin = 10, + .upper_margin = 10, + .lower_margin = 10, + .hsync_len = 10, + .vsync_len = 10, + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .flag = 0, }, }; @@ -399,10 +388,9 @@ static int lcd_cfg_dma(int burst_size, int fifo_th) reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_8); break; case 16: + default: reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_16); break; - default: - return -EINVAL; } reg |= (fifo_th << 8); @@ -447,7 +435,8 @@ static void lcd_cfg_vertical_sync(int back_porch, int pulse_width, lcdc_write(reg, LCD_RASTER_TIMING_1_REG); } -static int lcd_cfg_display(const struct lcd_ctrl_config *cfg) +static int lcd_cfg_display(const struct lcd_ctrl_config *cfg, + struct fb_videomode *panel) { u32 reg; u32 reg_int; @@ -456,7 +445,7 @@ static int lcd_cfg_display(const struct lcd_ctrl_config *cfg) LCD_MONO_8BIT_MODE | LCD_MONOCHROME_MODE); - switch (cfg->p_disp_panel->panel_shade) { + switch (cfg->panel_shade) { case MONOCHROME: reg |= LCD_MONOCHROME_MODE; if (cfg->mono_8bit_mode) @@ -469,7 +458,9 @@ static int lcd_cfg_display(const struct lcd_ctrl_config *cfg) break; case COLOR_PASSIVE: - if (cfg->stn_565_mode) + /* AC bias applicable only for Pasive panels */ + lcd_cfg_ac_bias(cfg->ac_bias, cfg->ac_bias_intrpt); + if (cfg->bpp == 12 && cfg->stn_565_mode) reg |= LCD_STN_565_ENABLE; break; @@ -490,22 +481,19 @@ static int lcd_cfg_display(const struct lcd_ctrl_config *cfg) reg = lcdc_read(LCD_RASTER_TIMING_2_REG); - if (cfg->sync_ctrl) - reg |= LCD_SYNC_CTRL; - else - reg &= ~LCD_SYNC_CTRL; + reg |= LCD_SYNC_CTRL; if (cfg->sync_edge) reg |= LCD_SYNC_EDGE; else reg &= ~LCD_SYNC_EDGE; - if (cfg->invert_line_clock) + if (panel->sync & FB_SYNC_HOR_HIGH_ACT) reg |= LCD_INVERT_LINE_CLOCK; else reg &= ~LCD_INVERT_LINE_CLOCK; - if (cfg->invert_frm_clock) + if (panel->sync & FB_SYNC_VERT_HIGH_ACT) reg |= LCD_INVERT_FRAME_CLOCK; else reg &= ~LCD_INVERT_FRAME_CLOCK; @@ -728,7 +716,7 @@ static void lcd_calc_clk_divider(struct da8xx_fb_par *par) } static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg, - struct da8xx_panel *panel) + struct fb_videomode *panel) { u32 bpp; int ret = 0; @@ -738,7 +726,7 @@ static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg, /* Calculate the divider */ lcd_calc_clk_divider(par); - if (panel->invert_pxl_clk) + if (panel->sync & FB_SYNC_CLK_INVERT) lcdc_write((lcdc_read(LCD_RASTER_TIMING_2_REG) | LCD_INVERT_PIXEL_CLOCK), LCD_RASTER_TIMING_2_REG); else @@ -750,30 +738,23 @@ static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg, if (ret < 0) return ret; - /* Configure the AC bias properties. */ - lcd_cfg_ac_bias(cfg->ac_bias, cfg->ac_bias_intrpt); - /* Configure the vertical and horizontal sync properties. */ - lcd_cfg_vertical_sync(panel->vbp, panel->vsw, panel->vfp); - lcd_cfg_horizontal_sync(panel->hbp, panel->hsw, panel->hfp); + lcd_cfg_vertical_sync(panel->lower_margin, panel->vsync_len, + panel->upper_margin); + lcd_cfg_horizontal_sync(panel->right_margin, panel->hsync_len, + panel->left_margin); /* Configure for disply */ - ret = lcd_cfg_display(cfg); + ret = lcd_cfg_display(cfg, panel); if (ret < 0) return ret; - if (QVGA != cfg->p_disp_panel->panel_type) - return -EINVAL; + bpp = cfg->bpp; - if (cfg->bpp <= cfg->p_disp_panel->max_bpp && - cfg->bpp >= cfg->p_disp_panel->min_bpp) - bpp = cfg->bpp; - else - bpp = cfg->p_disp_panel->max_bpp; if (bpp == 12) bpp = 16; - ret = lcd_cfg_frame_buffer(par, (unsigned int)panel->width, - (unsigned int)panel->height, bpp, + ret = lcd_cfg_frame_buffer(par, (unsigned int)panel->xres, + (unsigned int)panel->yres, bpp, cfg->raster_order); if (ret < 0) return ret; @@ -1235,7 +1216,7 @@ static int __devinit fb_probe(struct platform_device *device) struct da8xx_lcdc_platform_data *fb_pdata = device->dev.platform_data; struct lcd_ctrl_config *lcd_cfg; - struct da8xx_panel *lcdc_info; + struct fb_videomode *lcdc_info; struct fb_info *da8xx_fb_info; struct clk *fb_clk = NULL; struct da8xx_fb_par *par; @@ -1267,7 +1248,7 @@ static int __devinit fb_probe(struct platform_device *device) goto err_request_mem; } - fb_clk = clk_get(&device->dev, NULL); + fb_clk = clk_get(&device->dev, "fck"); if (IS_ERR(fb_clk)) { dev_err(&device->dev, "Can not get device clock\n"); ret = -ENODEV; @@ -1283,6 +1264,7 @@ static int __devinit fb_probe(struct platform_device *device) lcd_revision = LCD_VERSION_1; break; case 0x4F200800: + case 0x4F201000: lcd_revision = LCD_VERSION_2; break; default: @@ -1323,7 +1305,7 @@ static int __devinit fb_probe(struct platform_device *device) #ifdef CONFIG_CPU_FREQ par->lcd_fck_rate = clk_get_rate(fb_clk); #endif - par->pxl_clk = lcdc_info->pxl_clk; + par->pxl_clk = lcdc_info->pixclock; if (fb_pdata->panel_power_ctrl) { par->panel_power_ctrl = fb_pdata->panel_power_ctrl; par->panel_power_ctrl(1); @@ -1336,8 +1318,8 @@ static int __devinit fb_probe(struct platform_device *device) } /* allocate frame buffer */ - par->vram_size = lcdc_info->width * lcdc_info->height * lcd_cfg->bpp; - ulcm = lcm((lcdc_info->width * lcd_cfg->bpp)/8, PAGE_SIZE); + par->vram_size = lcdc_info->xres * lcdc_info->yres * lcd_cfg->bpp; + ulcm = lcm((lcdc_info->xres * lcd_cfg->bpp)/8, PAGE_SIZE); par->vram_size = roundup(par->vram_size/8, ulcm); par->vram_size = par->vram_size * LCD_NUM_BUFFERS; @@ -1355,10 +1337,10 @@ static int __devinit fb_probe(struct platform_device *device) da8xx_fb_info->screen_base = (char __iomem *) par->vram_virt; da8xx_fb_fix.smem_start = par->vram_phys; da8xx_fb_fix.smem_len = par->vram_size; - da8xx_fb_fix.line_length = (lcdc_info->width * lcd_cfg->bpp) / 8; + da8xx_fb_fix.line_length = (lcdc_info->xres * lcd_cfg->bpp) / 8; par->dma_start = par->vram_phys; - par->dma_end = par->dma_start + lcdc_info->height * + par->dma_end = par->dma_start + lcdc_info->yres * da8xx_fb_fix.line_length - 1; /* allocate palette buffer */ @@ -1384,22 +1366,22 @@ static int __devinit fb_probe(struct platform_device *device) /* Initialize par */ da8xx_fb_info->var.bits_per_pixel = lcd_cfg->bpp; - da8xx_fb_var.xres = lcdc_info->width; - da8xx_fb_var.xres_virtual = lcdc_info->width; + da8xx_fb_var.xres = lcdc_info->xres; + da8xx_fb_var.xres_virtual = lcdc_info->xres; - da8xx_fb_var.yres = lcdc_info->height; - da8xx_fb_var.yres_virtual = lcdc_info->height * LCD_NUM_BUFFERS; + da8xx_fb_var.yres = lcdc_info->yres; + da8xx_fb_var.yres_virtual = lcdc_info->yres * LCD_NUM_BUFFERS; da8xx_fb_var.grayscale = - lcd_cfg->p_disp_panel->panel_shade == MONOCHROME ? 1 : 0; + lcd_cfg->panel_shade == MONOCHROME ? 1 : 0; da8xx_fb_var.bits_per_pixel = lcd_cfg->bpp; - da8xx_fb_var.hsync_len = lcdc_info->hsw; - da8xx_fb_var.vsync_len = lcdc_info->vsw; - da8xx_fb_var.right_margin = lcdc_info->hfp; - da8xx_fb_var.left_margin = lcdc_info->hbp; - da8xx_fb_var.lower_margin = lcdc_info->vfp; - da8xx_fb_var.upper_margin = lcdc_info->vbp; + da8xx_fb_var.hsync_len = lcdc_info->hsync_len; + da8xx_fb_var.vsync_len = lcdc_info->vsync_len; + da8xx_fb_var.right_margin = lcdc_info->right_margin; + da8xx_fb_var.left_margin = lcdc_info->left_margin; + da8xx_fb_var.lower_margin = lcdc_info->lower_margin; + da8xx_fb_var.upper_margin = lcdc_info->upper_margin; da8xx_fb_var.pixclock = da8xxfb_pixel_clk_period(par); /* Initialize fbinfo */ diff --git a/drivers/video/exynos/exynos_dp_core.c b/drivers/video/exynos/exynos_dp_core.c index d55470e7541..28fd686c6b8 100644 --- a/drivers/video/exynos/exynos_dp_core.c +++ b/drivers/video/exynos/exynos_dp_core.c @@ -18,6 +18,7 @@ #include <linux/io.h> #include <linux/interrupt.h> #include <linux/delay.h> +#include <linux/of.h> #include <video/exynos_dp.h> @@ -48,10 +49,6 @@ static int exynos_dp_detect_hpd(struct exynos_dp_device *dp) { int timeout_loop = 0; - exynos_dp_init_hpd(dp); - - usleep_range(200, 210); - while (exynos_dp_get_plug_in_status(dp) != 0) { timeout_loop++; if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) { @@ -90,9 +87,11 @@ static int exynos_dp_read_edid(struct exynos_dp_device *dp) */ /* Read Extension Flag, Number of 128-byte EDID extension blocks */ - exynos_dp_read_byte_from_i2c(dp, I2C_EDID_DEVICE_ADDR, + retval = exynos_dp_read_byte_from_i2c(dp, I2C_EDID_DEVICE_ADDR, EDID_EXTENSION_FLAG, &extend_block); + if (retval) + return retval; if (extend_block > 0) { dev_dbg(dp->dev, "EDID data includes a single extension!\n"); @@ -181,14 +180,15 @@ static int exynos_dp_handle_edid(struct exynos_dp_device *dp) int retval; /* Read DPCD DPCD_ADDR_DPCD_REV~RECEIVE_PORT1_CAP_1 */ - exynos_dp_read_bytes_from_dpcd(dp, - DPCD_ADDR_DPCD_REV, - 12, buf); + retval = exynos_dp_read_bytes_from_dpcd(dp, DPCD_ADDR_DPCD_REV, + 12, buf); + if (retval) + return retval; /* Read EDID */ for (i = 0; i < 3; i++) { retval = exynos_dp_read_edid(dp); - if (retval == 0) + if (!retval) break; } @@ -261,11 +261,10 @@ static void exynos_dp_set_lane_lane_pre_emphasis(struct exynos_dp_device *dp, } } -static void exynos_dp_link_start(struct exynos_dp_device *dp) +static int exynos_dp_link_start(struct exynos_dp_device *dp) { u8 buf[4]; - int lane; - int lane_count; + int lane, lane_count, pll_tries, retval; lane_count = dp->link_train.lane_count; @@ -275,10 +274,6 @@ static void exynos_dp_link_start(struct exynos_dp_device *dp) for (lane = 0; lane < lane_count; lane++) dp->link_train.cr_loop[lane] = 0; - /* Set sink to D0 (Sink Not Ready) mode. */ - exynos_dp_write_byte_to_dpcd(dp, DPCD_ADDR_SINK_POWER_STATE, - DPCD_SET_POWER_STATE_D0); - /* Set link rate and count as you want to establish*/ exynos_dp_set_link_bandwidth(dp, dp->link_train.link_rate); exynos_dp_set_lane_count(dp, dp->link_train.lane_count); @@ -286,29 +281,46 @@ static void exynos_dp_link_start(struct exynos_dp_device *dp) /* Setup RX configuration */ buf[0] = dp->link_train.link_rate; buf[1] = dp->link_train.lane_count; - exynos_dp_write_bytes_to_dpcd(dp, DPCD_ADDR_LINK_BW_SET, + retval = exynos_dp_write_bytes_to_dpcd(dp, DPCD_ADDR_LINK_BW_SET, 2, buf); + if (retval) + return retval; /* Set TX pre-emphasis to minimum */ for (lane = 0; lane < lane_count; lane++) exynos_dp_set_lane_lane_pre_emphasis(dp, PRE_EMPHASIS_LEVEL_0, lane); + /* Wait for PLL lock */ + pll_tries = 0; + while (exynos_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) { + if (pll_tries == DP_TIMEOUT_LOOP_COUNT) { + dev_err(dp->dev, "Wait for PLL lock timed out\n"); + return -ETIMEDOUT; + } + + pll_tries++; + usleep_range(90, 120); + } + /* Set training pattern 1 */ exynos_dp_set_training_pattern(dp, TRAINING_PTN1); /* Set RX training pattern */ - exynos_dp_write_byte_to_dpcd(dp, - DPCD_ADDR_TRAINING_PATTERN_SET, - DPCD_SCRAMBLING_DISABLED | - DPCD_TRAINING_PATTERN_1); + retval = exynos_dp_write_byte_to_dpcd(dp, + DPCD_ADDR_TRAINING_PATTERN_SET, + DPCD_SCRAMBLING_DISABLED | DPCD_TRAINING_PATTERN_1); + if (retval) + return retval; for (lane = 0; lane < lane_count; lane++) buf[lane] = DPCD_PRE_EMPHASIS_PATTERN2_LEVEL0 | DPCD_VOLTAGE_SWING_PATTERN1_LEVEL0; - exynos_dp_write_bytes_to_dpcd(dp, - DPCD_ADDR_TRAINING_LANE0_SET, - lane_count, buf); + + retval = exynos_dp_write_bytes_to_dpcd(dp, DPCD_ADDR_TRAINING_LANE0_SET, + lane_count, buf); + + return retval; } static unsigned char exynos_dp_get_lane_status(u8 link_status[2], int lane) @@ -332,18 +344,17 @@ static int exynos_dp_clock_recovery_ok(u8 link_status[2], int lane_count) return 0; } -static int exynos_dp_channel_eq_ok(u8 link_align[3], int lane_count) +static int exynos_dp_channel_eq_ok(u8 link_status[2], u8 link_align, + int lane_count) { int lane; - u8 lane_align; u8 lane_status; - lane_align = link_align[2]; - if ((lane_align & DPCD_INTERLANE_ALIGN_DONE) == 0) + if ((link_align & DPCD_INTERLANE_ALIGN_DONE) == 0) return -EINVAL; for (lane = 0; lane < lane_count; lane++) { - lane_status = exynos_dp_get_lane_status(link_align, lane); + lane_status = exynos_dp_get_lane_status(link_status, lane); lane_status &= DPCD_CHANNEL_EQ_BITS; if (lane_status != DPCD_CHANNEL_EQ_BITS) return -EINVAL; @@ -427,60 +438,60 @@ static void exynos_dp_reduce_link_rate(struct exynos_dp_device *dp) dp->link_train.lt_state = FAILED; } -static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp) +static void exynos_dp_get_adjust_training_lane(struct exynos_dp_device *dp, + u8 adjust_request[2]) { - u8 link_status[2]; - int lane; - int lane_count; + int lane, lane_count; + u8 voltage_swing, pre_emphasis, training_lane; - u8 adjust_request[2]; - u8 voltage_swing; - u8 pre_emphasis; - u8 training_lane; + lane_count = dp->link_train.lane_count; + for (lane = 0; lane < lane_count; lane++) { + voltage_swing = exynos_dp_get_adjust_request_voltage( + adjust_request, lane); + pre_emphasis = exynos_dp_get_adjust_request_pre_emphasis( + adjust_request, lane); + training_lane = DPCD_VOLTAGE_SWING_SET(voltage_swing) | + DPCD_PRE_EMPHASIS_SET(pre_emphasis); + + if (voltage_swing == VOLTAGE_LEVEL_3) + training_lane |= DPCD_MAX_SWING_REACHED; + if (pre_emphasis == PRE_EMPHASIS_LEVEL_3) + training_lane |= DPCD_MAX_PRE_EMPHASIS_REACHED; + + dp->link_train.training_lane[lane] = training_lane; + } +} + +static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp) +{ + int lane, lane_count, retval; + u8 voltage_swing, pre_emphasis, training_lane; + u8 link_status[2], adjust_request[2]; usleep_range(100, 101); lane_count = dp->link_train.lane_count; - exynos_dp_read_bytes_from_dpcd(dp, DPCD_ADDR_LANE0_1_STATUS, - 2, link_status); + retval = exynos_dp_read_bytes_from_dpcd(dp, + DPCD_ADDR_LANE0_1_STATUS, 2, link_status); + if (retval) + return retval; + + retval = exynos_dp_read_bytes_from_dpcd(dp, + DPCD_ADDR_ADJUST_REQUEST_LANE0_1, 2, adjust_request); + if (retval) + return retval; if (exynos_dp_clock_recovery_ok(link_status, lane_count) == 0) { /* set training pattern 2 for EQ */ exynos_dp_set_training_pattern(dp, TRAINING_PTN2); - for (lane = 0; lane < lane_count; lane++) { - exynos_dp_read_bytes_from_dpcd(dp, - DPCD_ADDR_ADJUST_REQUEST_LANE0_1, - 2, adjust_request); - voltage_swing = exynos_dp_get_adjust_request_voltage( - adjust_request, lane); - pre_emphasis = exynos_dp_get_adjust_request_pre_emphasis( - adjust_request, lane); - training_lane = DPCD_VOLTAGE_SWING_SET(voltage_swing) | - DPCD_PRE_EMPHASIS_SET(pre_emphasis); - - if (voltage_swing == VOLTAGE_LEVEL_3) - training_lane |= DPCD_MAX_SWING_REACHED; - if (pre_emphasis == PRE_EMPHASIS_LEVEL_3) - training_lane |= DPCD_MAX_PRE_EMPHASIS_REACHED; - - dp->link_train.training_lane[lane] = training_lane; - - exynos_dp_set_lane_link_training(dp, - dp->link_train.training_lane[lane], - lane); - } - - exynos_dp_write_byte_to_dpcd(dp, - DPCD_ADDR_TRAINING_PATTERN_SET, - DPCD_SCRAMBLING_DISABLED | - DPCD_TRAINING_PATTERN_2); - - exynos_dp_write_bytes_to_dpcd(dp, - DPCD_ADDR_TRAINING_LANE0_SET, - lane_count, - dp->link_train.training_lane); + retval = exynos_dp_write_byte_to_dpcd(dp, + DPCD_ADDR_TRAINING_PATTERN_SET, + DPCD_SCRAMBLING_DISABLED | + DPCD_TRAINING_PATTERN_2); + if (retval) + return retval; dev_info(dp->dev, "Link Training Clock Recovery success\n"); dp->link_train.lt_state = EQUALIZER_TRAINING; @@ -488,152 +499,116 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp) for (lane = 0; lane < lane_count; lane++) { training_lane = exynos_dp_get_lane_link_training( dp, lane); - exynos_dp_read_bytes_from_dpcd(dp, - DPCD_ADDR_ADJUST_REQUEST_LANE0_1, - 2, adjust_request); voltage_swing = exynos_dp_get_adjust_request_voltage( adjust_request, lane); pre_emphasis = exynos_dp_get_adjust_request_pre_emphasis( adjust_request, lane); - if (voltage_swing == VOLTAGE_LEVEL_3 || - pre_emphasis == PRE_EMPHASIS_LEVEL_3) { - dev_err(dp->dev, "voltage or pre emphasis reached max level\n"); - goto reduce_link_rate; - } - - if ((DPCD_VOLTAGE_SWING_GET(training_lane) == - voltage_swing) && - (DPCD_PRE_EMPHASIS_GET(training_lane) == - pre_emphasis)) { + if (DPCD_VOLTAGE_SWING_GET(training_lane) == + voltage_swing && + DPCD_PRE_EMPHASIS_GET(training_lane) == + pre_emphasis) dp->link_train.cr_loop[lane]++; - if (dp->link_train.cr_loop[lane] == MAX_CR_LOOP) { - dev_err(dp->dev, "CR Max loop\n"); - goto reduce_link_rate; - } - } - - training_lane = DPCD_VOLTAGE_SWING_SET(voltage_swing) | - DPCD_PRE_EMPHASIS_SET(pre_emphasis); - if (voltage_swing == VOLTAGE_LEVEL_3) - training_lane |= DPCD_MAX_SWING_REACHED; - if (pre_emphasis == PRE_EMPHASIS_LEVEL_3) - training_lane |= DPCD_MAX_PRE_EMPHASIS_REACHED; + if (dp->link_train.cr_loop[lane] == MAX_CR_LOOP || + voltage_swing == VOLTAGE_LEVEL_3 || + pre_emphasis == PRE_EMPHASIS_LEVEL_3) { + dev_err(dp->dev, "CR Max reached (%d,%d,%d)\n", + dp->link_train.cr_loop[lane], + voltage_swing, pre_emphasis); + exynos_dp_reduce_link_rate(dp); + return -EIO; + } + } + } - dp->link_train.training_lane[lane] = training_lane; + exynos_dp_get_adjust_training_lane(dp, adjust_request); - exynos_dp_set_lane_link_training(dp, - dp->link_train.training_lane[lane], lane); - } + for (lane = 0; lane < lane_count; lane++) + exynos_dp_set_lane_link_training(dp, + dp->link_train.training_lane[lane], lane); - exynos_dp_write_bytes_to_dpcd(dp, - DPCD_ADDR_TRAINING_LANE0_SET, - lane_count, + retval = exynos_dp_write_bytes_to_dpcd(dp, + DPCD_ADDR_TRAINING_LANE0_SET, lane_count, dp->link_train.training_lane); - } - - return 0; + if (retval) + return retval; -reduce_link_rate: - exynos_dp_reduce_link_rate(dp); - return -EIO; + return retval; } static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp) { - u8 link_status[2]; - u8 link_align[3]; - int lane; - int lane_count; + int lane, lane_count, retval; u32 reg; - - u8 adjust_request[2]; - u8 voltage_swing; - u8 pre_emphasis; - u8 training_lane; + u8 link_align, link_status[2], adjust_request[2]; usleep_range(400, 401); lane_count = dp->link_train.lane_count; - exynos_dp_read_bytes_from_dpcd(dp, DPCD_ADDR_LANE0_1_STATUS, - 2, link_status); + retval = exynos_dp_read_bytes_from_dpcd(dp, + DPCD_ADDR_LANE0_1_STATUS, 2, link_status); + if (retval) + return retval; - if (exynos_dp_clock_recovery_ok(link_status, lane_count) == 0) { - link_align[0] = link_status[0]; - link_align[1] = link_status[1]; + if (exynos_dp_clock_recovery_ok(link_status, lane_count)) { + exynos_dp_reduce_link_rate(dp); + return -EIO; + } - exynos_dp_read_byte_from_dpcd(dp, - DPCD_ADDR_LANE_ALIGN_STATUS_UPDATED, - &link_align[2]); + retval = exynos_dp_read_bytes_from_dpcd(dp, + DPCD_ADDR_ADJUST_REQUEST_LANE0_1, 2, adjust_request); + if (retval) + return retval; - for (lane = 0; lane < lane_count; lane++) { - exynos_dp_read_bytes_from_dpcd(dp, - DPCD_ADDR_ADJUST_REQUEST_LANE0_1, - 2, adjust_request); - voltage_swing = exynos_dp_get_adjust_request_voltage( - adjust_request, lane); - pre_emphasis = exynos_dp_get_adjust_request_pre_emphasis( - adjust_request, lane); - training_lane = DPCD_VOLTAGE_SWING_SET(voltage_swing) | - DPCD_PRE_EMPHASIS_SET(pre_emphasis); + retval = exynos_dp_read_byte_from_dpcd(dp, + DPCD_ADDR_LANE_ALIGN_STATUS_UPDATED, &link_align); + if (retval) + return retval; - if (voltage_swing == VOLTAGE_LEVEL_3) - training_lane |= DPCD_MAX_SWING_REACHED; - if (pre_emphasis == PRE_EMPHASIS_LEVEL_3) - training_lane |= DPCD_MAX_PRE_EMPHASIS_REACHED; + exynos_dp_get_adjust_training_lane(dp, adjust_request); - dp->link_train.training_lane[lane] = training_lane; - } + if (!exynos_dp_channel_eq_ok(link_status, link_align, lane_count)) { + /* traing pattern Set to Normal */ + exynos_dp_training_pattern_dis(dp); - if (exynos_dp_channel_eq_ok(link_align, lane_count) == 0) { - /* traing pattern Set to Normal */ - exynos_dp_training_pattern_dis(dp); + dev_info(dp->dev, "Link Training success!\n"); - dev_info(dp->dev, "Link Training success!\n"); - - exynos_dp_get_link_bandwidth(dp, ®); - dp->link_train.link_rate = reg; - dev_dbg(dp->dev, "final bandwidth = %.2x\n", - dp->link_train.link_rate); + exynos_dp_get_link_bandwidth(dp, ®); + dp->link_train.link_rate = reg; + dev_dbg(dp->dev, "final bandwidth = %.2x\n", + dp->link_train.link_rate); - exynos_dp_get_lane_count(dp, ®); - dp->link_train.lane_count = reg; - dev_dbg(dp->dev, "final lane count = %.2x\n", - dp->link_train.lane_count); + exynos_dp_get_lane_count(dp, ®); + dp->link_train.lane_count = reg; + dev_dbg(dp->dev, "final lane count = %.2x\n", + dp->link_train.lane_count); - /* set enhanced mode if available */ - exynos_dp_set_enhanced_mode(dp); - dp->link_train.lt_state = FINISHED; - } else { - /* not all locked */ - dp->link_train.eq_loop++; + /* set enhanced mode if available */ + exynos_dp_set_enhanced_mode(dp); + dp->link_train.lt_state = FINISHED; - if (dp->link_train.eq_loop > MAX_EQ_LOOP) { - dev_err(dp->dev, "EQ Max loop\n"); - goto reduce_link_rate; - } + return 0; + } - for (lane = 0; lane < lane_count; lane++) - exynos_dp_set_lane_link_training(dp, - dp->link_train.training_lane[lane], - lane); + /* not all locked */ + dp->link_train.eq_loop++; - exynos_dp_write_bytes_to_dpcd(dp, - DPCD_ADDR_TRAINING_LANE0_SET, - lane_count, - dp->link_train.training_lane); - } - } else { - goto reduce_link_rate; + if (dp->link_train.eq_loop > MAX_EQ_LOOP) { + dev_err(dp->dev, "EQ Max loop\n"); + exynos_dp_reduce_link_rate(dp); + return -EIO; } - return 0; + for (lane = 0; lane < lane_count; lane++) + exynos_dp_set_lane_link_training(dp, + dp->link_train.training_lane[lane], lane); + + retval = exynos_dp_write_bytes_to_dpcd(dp, DPCD_ADDR_TRAINING_LANE0_SET, + lane_count, dp->link_train.training_lane); -reduce_link_rate: - exynos_dp_reduce_link_rate(dp); - return -EIO; + return retval; } static void exynos_dp_get_max_rx_bandwidth(struct exynos_dp_device *dp, @@ -701,16 +676,17 @@ static void exynos_dp_init_training(struct exynos_dp_device *dp, static int exynos_dp_sw_link_training(struct exynos_dp_device *dp) { - int retval = 0; - int training_finished = 0; + int retval = 0, training_finished = 0; dp->link_train.lt_state = START; /* Process here */ - while (!training_finished) { + while (!retval && !training_finished) { switch (dp->link_train.lt_state) { case START: - exynos_dp_link_start(dp); + retval = exynos_dp_link_start(dp); + if (retval) + dev_err(dp->dev, "LT link start failed!\n"); break; case CLOCK_RECOVERY: retval = exynos_dp_process_clock_recovery(dp); @@ -729,6 +705,8 @@ static int exynos_dp_sw_link_training(struct exynos_dp_device *dp) return -EREMOTEIO; } } + if (retval) + dev_err(dp->dev, "eDP link training failed (%d)\n", retval); return retval; } @@ -752,19 +730,15 @@ static int exynos_dp_set_link_train(struct exynos_dp_device *dp, return retval; } -static int exynos_dp_config_video(struct exynos_dp_device *dp, - struct video_info *video_info) +static int exynos_dp_config_video(struct exynos_dp_device *dp) { int retval = 0; int timeout_loop = 0; int done_count = 0; - exynos_dp_config_video_slave_mode(dp, video_info); + exynos_dp_config_video_slave_mode(dp); - exynos_dp_set_video_color_format(dp, video_info->color_depth, - video_info->color_space, - video_info->dynamic_range, - video_info->ycbcr_coeff); + exynos_dp_set_video_color_format(dp); if (exynos_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) { dev_err(dp->dev, "PLL is not locked yet.\n"); @@ -852,10 +826,213 @@ static irqreturn_t exynos_dp_irq_handler(int irq, void *arg) { struct exynos_dp_device *dp = arg; - dev_err(dp->dev, "exynos_dp_irq_handler\n"); + enum dp_irq_type irq_type; + + irq_type = exynos_dp_get_irq_type(dp); + switch (irq_type) { + case DP_IRQ_TYPE_HP_CABLE_IN: + dev_dbg(dp->dev, "Received irq - cable in\n"); + schedule_work(&dp->hotplug_work); + exynos_dp_clear_hotplug_interrupts(dp); + break; + case DP_IRQ_TYPE_HP_CABLE_OUT: + dev_dbg(dp->dev, "Received irq - cable out\n"); + exynos_dp_clear_hotplug_interrupts(dp); + break; + case DP_IRQ_TYPE_HP_CHANGE: + /* + * We get these change notifications once in a while, but there + * is nothing we can do with them. Just ignore it for now and + * only handle cable changes. + */ + dev_dbg(dp->dev, "Received irq - hotplug change; ignoring.\n"); + exynos_dp_clear_hotplug_interrupts(dp); + break; + default: + dev_err(dp->dev, "Received irq - unknown type!\n"); + break; + } return IRQ_HANDLED; } +static void exynos_dp_hotplug(struct work_struct *work) +{ + struct exynos_dp_device *dp; + int ret; + + dp = container_of(work, struct exynos_dp_device, hotplug_work); + + ret = exynos_dp_detect_hpd(dp); + if (ret) { + /* Cable has been disconnected, we're done */ + return; + } + + ret = exynos_dp_handle_edid(dp); + if (ret) { + dev_err(dp->dev, "unable to handle edid\n"); + return; + } + + ret = exynos_dp_set_link_train(dp, dp->video_info->lane_count, + dp->video_info->link_rate); + if (ret) { + dev_err(dp->dev, "unable to do link train\n"); + return; + } + + exynos_dp_enable_scramble(dp, 1); + exynos_dp_enable_rx_to_enhanced_mode(dp, 1); + exynos_dp_enable_enhanced_mode(dp, 1); + + exynos_dp_set_lane_count(dp, dp->video_info->lane_count); + exynos_dp_set_link_bandwidth(dp, dp->video_info->link_rate); + + exynos_dp_init_video(dp); + ret = exynos_dp_config_video(dp); + if (ret) + dev_err(dp->dev, "unable to config video\n"); +} + +#ifdef CONFIG_OF +static struct exynos_dp_platdata *exynos_dp_dt_parse_pdata(struct device *dev) +{ + struct device_node *dp_node = dev->of_node; + struct exynos_dp_platdata *pd; + struct video_info *dp_video_config; + + pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); + if (!pd) { + dev_err(dev, "memory allocation for pdata failed\n"); + return ERR_PTR(-ENOMEM); + } + dp_video_config = devm_kzalloc(dev, + sizeof(*dp_video_config), GFP_KERNEL); + + if (!dp_video_config) { + dev_err(dev, "memory allocation for video config failed\n"); + return ERR_PTR(-ENOMEM); + } + pd->video_info = dp_video_config; + + dp_video_config->h_sync_polarity = + of_property_read_bool(dp_node, "hsync-active-high"); + + dp_video_config->v_sync_polarity = + of_property_read_bool(dp_node, "vsync-active-high"); + + dp_video_config->interlaced = + of_property_read_bool(dp_node, "interlaced"); + + if (of_property_read_u32(dp_node, "samsung,color-space", + &dp_video_config->color_space)) { + dev_err(dev, "failed to get color-space\n"); + return ERR_PTR(-EINVAL); + } + + if (of_property_read_u32(dp_node, "samsung,dynamic-range", + &dp_video_config->dynamic_range)) { + dev_err(dev, "failed to get dynamic-range\n"); + return ERR_PTR(-EINVAL); + } + + if (of_property_read_u32(dp_node, "samsung,ycbcr-coeff", + &dp_video_config->ycbcr_coeff)) { + dev_err(dev, "failed to get ycbcr-coeff\n"); + return ERR_PTR(-EINVAL); + } + + if (of_property_read_u32(dp_node, "samsung,color-depth", + &dp_video_config->color_depth)) { + dev_err(dev, "failed to get color-depth\n"); + return ERR_PTR(-EINVAL); + } + + if (of_property_read_u32(dp_node, "samsung,link-rate", + &dp_video_config->link_rate)) { + dev_err(dev, "failed to get link-rate\n"); + return ERR_PTR(-EINVAL); + } + + if (of_property_read_u32(dp_node, "samsung,lane-count", + &dp_video_config->lane_count)) { + dev_err(dev, "failed to get lane-count\n"); + return ERR_PTR(-EINVAL); + } + + return pd; +} + +static int exynos_dp_dt_parse_phydata(struct exynos_dp_device *dp) +{ + struct device_node *dp_phy_node; + u32 phy_base; + + dp_phy_node = of_find_node_by_name(dp->dev->of_node, "dptx-phy"); + if (!dp_phy_node) { + dev_err(dp->dev, "could not find dptx-phy node\n"); + return -ENODEV; + } + + if (of_property_read_u32(dp_phy_node, "reg", &phy_base)) { + dev_err(dp->dev, "faild to get reg for dptx-phy\n"); + return -EINVAL; + } + + if (of_property_read_u32(dp_phy_node, "samsung,enable-mask", + &dp->enable_mask)) { + dev_err(dp->dev, "faild to get enable-mask for dptx-phy\n"); + return -EINVAL; + } + + dp->phy_addr = ioremap(phy_base, SZ_4); + if (!dp->phy_addr) { + dev_err(dp->dev, "failed to ioremap dp-phy\n"); + return -ENOMEM; + } + + return 0; +} + +static void exynos_dp_phy_init(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = __raw_readl(dp->phy_addr); + reg |= dp->enable_mask; + __raw_writel(reg, dp->phy_addr); +} + +static void exynos_dp_phy_exit(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = __raw_readl(dp->phy_addr); + reg &= ~(dp->enable_mask); + __raw_writel(reg, dp->phy_addr); +} +#else +static struct exynos_dp_platdata *exynos_dp_dt_parse_pdata(struct device *dev) +{ + return NULL; +} + +static int exynos_dp_dt_parse_phydata(struct exynos_dp_device *dp) +{ + return -EINVAL; +} + +static void exynos_dp_phy_init(struct exynos_dp_device *dp) +{ + return; +} + +static void exynos_dp_phy_exit(struct exynos_dp_device *dp) +{ + return; +} +#endif /* CONFIG_OF */ + static int __devinit exynos_dp_probe(struct platform_device *pdev) { struct resource *res; @@ -864,12 +1041,6 @@ static int __devinit exynos_dp_probe(struct platform_device *pdev) int ret = 0; - pdata = pdev->dev.platform_data; - if (!pdata) { - dev_err(&pdev->dev, "no platform data\n"); - return -EINVAL; - } - dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device), GFP_KERNEL); if (!dp) { @@ -879,6 +1050,22 @@ static int __devinit exynos_dp_probe(struct platform_device *pdev) dp->dev = &pdev->dev; + if (pdev->dev.of_node) { + pdata = exynos_dp_dt_parse_pdata(&pdev->dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + + ret = exynos_dp_dt_parse_phydata(dp); + if (ret) + return ret; + } else { + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "no platform data\n"); + return -EINVAL; + } + } + dp->clock = devm_clk_get(&pdev->dev, "dp"); if (IS_ERR(dp->clock)) { dev_err(&pdev->dev, "failed to get clock\n"); @@ -896,50 +1083,29 @@ static int __devinit exynos_dp_probe(struct platform_device *pdev) } dp->irq = platform_get_irq(pdev, 0); - if (!dp->irq) { + if (dp->irq == -ENXIO) { dev_err(&pdev->dev, "failed to get irq\n"); return -ENODEV; } - ret = devm_request_irq(&pdev->dev, dp->irq, exynos_dp_irq_handler, 0, - "exynos-dp", dp); - if (ret) { - dev_err(&pdev->dev, "failed to request irq\n"); - return ret; - } + INIT_WORK(&dp->hotplug_work, exynos_dp_hotplug); dp->video_info = pdata->video_info; - if (pdata->phy_init) - pdata->phy_init(); - - exynos_dp_init_dp(dp); - - ret = exynos_dp_detect_hpd(dp); - if (ret) { - dev_err(&pdev->dev, "unable to detect hpd\n"); - return ret; - } - exynos_dp_handle_edid(dp); - - ret = exynos_dp_set_link_train(dp, dp->video_info->lane_count, - dp->video_info->link_rate); - if (ret) { - dev_err(&pdev->dev, "unable to do link train\n"); - return ret; + if (pdev->dev.of_node) { + if (dp->phy_addr) + exynos_dp_phy_init(dp); + } else { + if (pdata->phy_init) + pdata->phy_init(); } - exynos_dp_enable_scramble(dp, 1); - exynos_dp_enable_rx_to_enhanced_mode(dp, 1); - exynos_dp_enable_enhanced_mode(dp, 1); - - exynos_dp_set_lane_count(dp, dp->video_info->lane_count); - exynos_dp_set_link_bandwidth(dp, dp->video_info->link_rate); + exynos_dp_init_dp(dp); - exynos_dp_init_video(dp); - ret = exynos_dp_config_video(dp, dp->video_info); + ret = devm_request_irq(&pdev->dev, dp->irq, exynos_dp_irq_handler, 0, + "exynos-dp", dp); if (ret) { - dev_err(&pdev->dev, "unable to config video\n"); + dev_err(&pdev->dev, "failed to request irq\n"); return ret; } @@ -953,23 +1119,41 @@ static int __devexit exynos_dp_remove(struct platform_device *pdev) struct exynos_dp_platdata *pdata = pdev->dev.platform_data; struct exynos_dp_device *dp = platform_get_drvdata(pdev); - if (pdata && pdata->phy_exit) - pdata->phy_exit(); + disable_irq(dp->irq); + + if (work_pending(&dp->hotplug_work)) + flush_work(&dp->hotplug_work); + + if (pdev->dev.of_node) { + if (dp->phy_addr) + exynos_dp_phy_exit(dp); + } else { + if (pdata->phy_exit) + pdata->phy_exit(); + } clk_disable_unprepare(dp->clock); + return 0; } #ifdef CONFIG_PM_SLEEP static int exynos_dp_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct exynos_dp_platdata *pdata = pdev->dev.platform_data; - struct exynos_dp_device *dp = platform_get_drvdata(pdev); + struct exynos_dp_platdata *pdata = dev->platform_data; + struct exynos_dp_device *dp = dev_get_drvdata(dev); - if (pdata && pdata->phy_exit) - pdata->phy_exit(); + if (work_pending(&dp->hotplug_work)) + flush_work(&dp->hotplug_work); + + if (dev->of_node) { + if (dp->phy_addr) + exynos_dp_phy_exit(dp); + } else { + if (pdata->phy_exit) + pdata->phy_exit(); + } clk_disable_unprepare(dp->clock); @@ -978,32 +1162,22 @@ static int exynos_dp_suspend(struct device *dev) static int exynos_dp_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct exynos_dp_platdata *pdata = pdev->dev.platform_data; - struct exynos_dp_device *dp = platform_get_drvdata(pdev); + struct exynos_dp_platdata *pdata = dev->platform_data; + struct exynos_dp_device *dp = dev_get_drvdata(dev); - if (pdata && pdata->phy_init) - pdata->phy_init(); + if (dev->of_node) { + if (dp->phy_addr) + exynos_dp_phy_init(dp); + } else { + if (pdata->phy_init) + pdata->phy_init(); + } clk_prepare_enable(dp->clock); exynos_dp_init_dp(dp); - exynos_dp_detect_hpd(dp); - exynos_dp_handle_edid(dp); - - exynos_dp_set_link_train(dp, dp->video_info->lane_count, - dp->video_info->link_rate); - - exynos_dp_enable_scramble(dp, 1); - exynos_dp_enable_rx_to_enhanced_mode(dp, 1); - exynos_dp_enable_enhanced_mode(dp, 1); - - exynos_dp_set_lane_count(dp, dp->video_info->lane_count); - exynos_dp_set_link_bandwidth(dp, dp->video_info->link_rate); - - exynos_dp_init_video(dp); - exynos_dp_config_video(dp, dp->video_info); + enable_irq(dp->irq); return 0; } @@ -1013,6 +1187,12 @@ static const struct dev_pm_ops exynos_dp_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(exynos_dp_suspend, exynos_dp_resume) }; +static const struct of_device_id exynos_dp_match[] = { + { .compatible = "samsung,exynos5-dp" }, + {}, +}; +MODULE_DEVICE_TABLE(of, exynos_dp_match); + static struct platform_driver exynos_dp_driver = { .probe = exynos_dp_probe, .remove = __devexit_p(exynos_dp_remove), @@ -1020,6 +1200,7 @@ static struct platform_driver exynos_dp_driver = { .name = "exynos-dp", .owner = THIS_MODULE, .pm = &exynos_dp_pm_ops, + .of_match_table = of_match_ptr(exynos_dp_match), }, }; diff --git a/drivers/video/exynos/exynos_dp_core.h b/drivers/video/exynos/exynos_dp_core.h index 57b8a6531c0..6c567bbf2fb 100644 --- a/drivers/video/exynos/exynos_dp_core.h +++ b/drivers/video/exynos/exynos_dp_core.h @@ -13,6 +13,13 @@ #ifndef _EXYNOS_DP_CORE_H #define _EXYNOS_DP_CORE_H +enum dp_irq_type { + DP_IRQ_TYPE_HP_CABLE_IN, + DP_IRQ_TYPE_HP_CABLE_OUT, + DP_IRQ_TYPE_HP_CHANGE, + DP_IRQ_TYPE_UNKNOWN, +}; + struct link_train { int eq_loop; int cr_loop[4]; @@ -29,9 +36,12 @@ struct exynos_dp_device { struct clk *clock; unsigned int irq; void __iomem *reg_base; + void __iomem *phy_addr; + unsigned int enable_mask; struct video_info *video_info; struct link_train link_train; + struct work_struct hotplug_work; }; /* exynos_dp_reg.c */ @@ -50,6 +60,8 @@ void exynos_dp_set_analog_power_down(struct exynos_dp_device *dp, bool enable); void exynos_dp_init_analog_func(struct exynos_dp_device *dp); void exynos_dp_init_hpd(struct exynos_dp_device *dp); +enum dp_irq_type exynos_dp_get_irq_type(struct exynos_dp_device *dp); +void exynos_dp_clear_hotplug_interrupts(struct exynos_dp_device *dp); void exynos_dp_reset_aux(struct exynos_dp_device *dp); void exynos_dp_init_aux(struct exynos_dp_device *dp); int exynos_dp_get_plug_in_status(struct exynos_dp_device *dp); @@ -107,11 +119,7 @@ u32 exynos_dp_get_lane3_link_training(struct exynos_dp_device *dp); void exynos_dp_reset_macro(struct exynos_dp_device *dp); void exynos_dp_init_video(struct exynos_dp_device *dp); -void exynos_dp_set_video_color_format(struct exynos_dp_device *dp, - u32 color_depth, - u32 color_space, - u32 dynamic_range, - u32 ycbcr_coeff); +void exynos_dp_set_video_color_format(struct exynos_dp_device *dp); int exynos_dp_is_slave_video_stream_clock_on(struct exynos_dp_device *dp); void exynos_dp_set_video_cr_mn(struct exynos_dp_device *dp, enum clock_recovery_m_value_type type, @@ -121,8 +129,7 @@ void exynos_dp_set_video_timing_mode(struct exynos_dp_device *dp, u32 type); void exynos_dp_enable_video_master(struct exynos_dp_device *dp, bool enable); void exynos_dp_start_video(struct exynos_dp_device *dp); int exynos_dp_is_video_stream_on(struct exynos_dp_device *dp); -void exynos_dp_config_video_slave_mode(struct exynos_dp_device *dp, - struct video_info *video_info); +void exynos_dp_config_video_slave_mode(struct exynos_dp_device *dp); void exynos_dp_enable_scrambling(struct exynos_dp_device *dp); void exynos_dp_disable_scrambling(struct exynos_dp_device *dp); diff --git a/drivers/video/exynos/exynos_dp_reg.c b/drivers/video/exynos/exynos_dp_reg.c index 3f5ca8a0d5e..29d9d035c73 100644 --- a/drivers/video/exynos/exynos_dp_reg.c +++ b/drivers/video/exynos/exynos_dp_reg.c @@ -19,11 +19,11 @@ #include "exynos_dp_core.h" #include "exynos_dp_reg.h" -#define COMMON_INT_MASK_1 (0) -#define COMMON_INT_MASK_2 (0) -#define COMMON_INT_MASK_3 (0) -#define COMMON_INT_MASK_4 (0) -#define INT_STA_MASK (0) +#define COMMON_INT_MASK_1 0 +#define COMMON_INT_MASK_2 0 +#define COMMON_INT_MASK_3 0 +#define COMMON_INT_MASK_4 (HOTPLUG_CHG | HPD_LOST | PLUG) +#define INT_STA_MASK INT_HPD void exynos_dp_enable_video_mute(struct exynos_dp_device *dp, bool enable) { @@ -88,7 +88,7 @@ void exynos_dp_init_analog_param(struct exynos_dp_device *dp) void exynos_dp_init_interrupt(struct exynos_dp_device *dp) { /* Set interrupt pin assertion polarity as high */ - writel(INT_POL, dp->reg_base + EXYNOS_DP_INT_CTL); + writel(INT_POL1 | INT_POL0, dp->reg_base + EXYNOS_DP_INT_CTL); /* Clear pending regisers */ writel(0xff, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_1); @@ -324,7 +324,7 @@ void exynos_dp_init_analog_func(struct exynos_dp_device *dp) writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_2); } -void exynos_dp_init_hpd(struct exynos_dp_device *dp) +void exynos_dp_clear_hotplug_interrupts(struct exynos_dp_device *dp) { u32 reg; @@ -333,12 +333,38 @@ void exynos_dp_init_hpd(struct exynos_dp_device *dp) reg = INT_HPD; writel(reg, dp->reg_base + EXYNOS_DP_INT_STA); +} + +void exynos_dp_init_hpd(struct exynos_dp_device *dp) +{ + u32 reg; + + exynos_dp_clear_hotplug_interrupts(dp); reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3); reg &= ~(F_HPD | HPD_CTRL); writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_3); } +enum dp_irq_type exynos_dp_get_irq_type(struct exynos_dp_device *dp) +{ + u32 reg; + + /* Parse hotplug interrupt status register */ + reg = readl(dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4); + + if (reg & PLUG) + return DP_IRQ_TYPE_HP_CABLE_IN; + + if (reg & HPD_LOST) + return DP_IRQ_TYPE_HP_CABLE_OUT; + + if (reg & HOTPLUG_CHG) + return DP_IRQ_TYPE_HP_CHANGE; + + return DP_IRQ_TYPE_UNKNOWN; +} + void exynos_dp_reset_aux(struct exynos_dp_device *dp) { u32 reg; @@ -491,7 +517,7 @@ int exynos_dp_read_byte_from_dpcd(struct exynos_dp_device *dp, int i; int retval; - for (i = 0; i < 10; i++) { + for (i = 0; i < 3; i++) { /* Clear AUX CH data buffer */ reg = BUF_CLR; writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL); @@ -552,7 +578,7 @@ int exynos_dp_write_bytes_to_dpcd(struct exynos_dp_device *dp, else cur_data_count = count - start_offset; - for (i = 0; i < 10; i++) { + for (i = 0; i < 3; i++) { /* Select DPCD device address */ reg = AUX_ADDR_7_0(reg_addr + start_offset); writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_7_0); @@ -617,7 +643,7 @@ int exynos_dp_read_bytes_from_dpcd(struct exynos_dp_device *dp, cur_data_count = count - start_offset; /* AUX CH Request Transaction process */ - for (i = 0; i < 10; i++) { + for (i = 0; i < 3; i++) { /* Select DPCD device address */ reg = AUX_ADDR_7_0(reg_addr + start_offset); writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_7_0); @@ -700,17 +726,15 @@ int exynos_dp_read_byte_from_i2c(struct exynos_dp_device *dp, int i; int retval; - for (i = 0; i < 10; i++) { + for (i = 0; i < 3; i++) { /* Clear AUX CH data buffer */ reg = BUF_CLR; writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL); /* Select EDID device */ retval = exynos_dp_select_i2c_device(dp, device_addr, reg_addr); - if (retval != 0) { - dev_err(dp->dev, "Select EDID device fail!\n"); + if (retval != 0) continue; - } /* * Set I2C transaction and read data @@ -750,7 +774,7 @@ int exynos_dp_read_bytes_from_i2c(struct exynos_dp_device *dp, int retval = 0; for (i = 0; i < count; i += 16) { - for (j = 0; j < 100; j++) { + for (j = 0; j < 3; j++) { /* Clear AUX CH data buffer */ reg = BUF_CLR; writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL); @@ -1034,24 +1058,20 @@ void exynos_dp_init_video(struct exynos_dp_device *dp) writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_8); } -void exynos_dp_set_video_color_format(struct exynos_dp_device *dp, - u32 color_depth, - u32 color_space, - u32 dynamic_range, - u32 ycbcr_coeff) +void exynos_dp_set_video_color_format(struct exynos_dp_device *dp) { u32 reg; /* Configure the input color depth, color space, dynamic range */ - reg = (dynamic_range << IN_D_RANGE_SHIFT) | - (color_depth << IN_BPC_SHIFT) | - (color_space << IN_COLOR_F_SHIFT); + reg = (dp->video_info->dynamic_range << IN_D_RANGE_SHIFT) | + (dp->video_info->color_depth << IN_BPC_SHIFT) | + (dp->video_info->color_space << IN_COLOR_F_SHIFT); writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_2); /* Set Input Color YCbCr Coefficients to ITU601 or ITU709 */ reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_3); reg &= ~IN_YC_COEFFI_MASK; - if (ycbcr_coeff) + if (dp->video_info->ycbcr_coeff) reg |= IN_YC_COEFFI_ITU709; else reg |= IN_YC_COEFFI_ITU601; @@ -1178,8 +1198,7 @@ int exynos_dp_is_video_stream_on(struct exynos_dp_device *dp) return 0; } -void exynos_dp_config_video_slave_mode(struct exynos_dp_device *dp, - struct video_info *video_info) +void exynos_dp_config_video_slave_mode(struct exynos_dp_device *dp) { u32 reg; @@ -1190,17 +1209,17 @@ void exynos_dp_config_video_slave_mode(struct exynos_dp_device *dp, reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); reg &= ~INTERACE_SCAN_CFG; - reg |= (video_info->interlaced << 2); + reg |= (dp->video_info->interlaced << 2); writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); reg &= ~VSYNC_POLARITY_CFG; - reg |= (video_info->v_sync_polarity << 1); + reg |= (dp->video_info->v_sync_polarity << 1); writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); reg &= ~HSYNC_POLARITY_CFG; - reg |= (video_info->h_sync_polarity << 0); + reg |= (dp->video_info->h_sync_polarity << 0); writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); reg = AUDIO_MODE_SPDIF_MODE | VIDEO_MODE_SLAVE_MODE; diff --git a/drivers/video/exynos/exynos_dp_reg.h b/drivers/video/exynos/exynos_dp_reg.h index 1f2f014cfe8..2e9bd0e0b9f 100644 --- a/drivers/video/exynos/exynos_dp_reg.h +++ b/drivers/video/exynos/exynos_dp_reg.h @@ -242,7 +242,8 @@ /* EXYNOS_DP_INT_CTL */ #define SOFT_INT_CTRL (0x1 << 2) -#define INT_POL (0x1 << 0) +#define INT_POL1 (0x1 << 1) +#define INT_POL0 (0x1 << 0) /* EXYNOS_DP_SYS_CTL_1 */ #define DET_STA (0x1 << 2) diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c index ede9e55413f..d3fc92eaee8 100644 --- a/drivers/video/fsl-diu-fb.c +++ b/drivers/video/fsl-diu-fb.c @@ -337,13 +337,11 @@ struct mfb_info { int registered; unsigned long pseudo_palette[16]; struct diu_ad *ad; - int cursor_reset; unsigned char g_alpha; unsigned int count; int x_aoi_d; /* aoi display x offset to physical screen */ int y_aoi_d; /* aoi display y offset to physical screen */ struct fsl_diu_data *parent; - u8 *edid_data; }; /** @@ -378,6 +376,8 @@ struct fsl_diu_data { struct diu_ad ad[NUM_AOIS] __aligned(8); u8 gamma[256 * 3] __aligned(32); u8 cursor[MAX_CURS * MAX_CURS * 2] __aligned(32); + uint8_t edid_data[EDID_LENGTH]; + bool has_edid; } __aligned(32); /* Determine the DMA address of a member of the fsl_diu_data structure */ @@ -430,6 +430,22 @@ static struct mfb_info mfb_template[] = { }, }; +#ifdef DEBUG +static void __attribute__ ((unused)) fsl_diu_dump(struct diu __iomem *hw) +{ + mb(); + pr_debug("DIU: desc=%08x,%08x,%08x, gamma=%08x pallete=%08x " + "cursor=%08x curs_pos=%08x diu_mode=%08x bgnd=%08x " + "disp_size=%08x hsyn_para=%08x vsyn_para=%08x syn_pol=%08x " + "thresholds=%08x int_mask=%08x plut=%08x\n", + hw->desc[0], hw->desc[1], hw->desc[2], hw->gamma, + hw->pallete, hw->cursor, hw->curs_pos, hw->diu_mode, + hw->bgnd, hw->disp_size, hw->hsyn_para, hw->vsyn_para, + hw->syn_pol, hw->thresholds, hw->int_mask, hw->plut); + rmb(); +} +#endif + /** * fsl_diu_name_to_port - convert a port name to a monitor port enum * @@ -481,8 +497,7 @@ static void fsl_diu_enable_panel(struct fb_info *info) switch (mfbi->index) { case PLANE0: - if (hw->desc[0] != ad->paddr) - wr_reg_wa(&hw->desc[0], ad->paddr); + wr_reg_wa(&hw->desc[0], ad->paddr); break; case PLANE1_AOI0: cmfbi = &data->mfb[2]; @@ -534,8 +549,7 @@ static void fsl_diu_disable_panel(struct fb_info *info) switch (mfbi->index) { case PLANE0: - if (hw->desc[0] != data->dummy_ad.paddr) - wr_reg_wa(&hw->desc[0], data->dummy_ad.paddr); + wr_reg_wa(&hw->desc[0], 0); break; case PLANE1_AOI0: cmfbi = &data->mfb[2]; @@ -792,7 +806,8 @@ static void update_lcdc(struct fb_info *info) hw = data->diu_reg; - diu_ops.set_monitor_port(data->monitor_port); + if (diu_ops.set_monitor_port) + diu_ops.set_monitor_port(data->monitor_port); gamma_table_base = data->gamma; /* Prep for DIU init - gamma table, cursor table */ @@ -811,12 +826,8 @@ static void update_lcdc(struct fb_info *info) out_be32(&hw->gamma, DMA_ADDR(data, gamma)); out_be32(&hw->cursor, DMA_ADDR(data, cursor)); - out_be32(&hw->bgnd, 0x007F7F7F); /* BGND */ - out_be32(&hw->bgnd_wb, 0); /* BGND_WB */ - out_be32(&hw->disp_size, (var->yres << 16 | var->xres)); - /* DISP SIZE */ - out_be32(&hw->wb_size, 0); /* WB SIZE */ - out_be32(&hw->wb_mem_addr, 0); /* WB MEM ADDR */ + out_be32(&hw->bgnd, 0x007F7F7F); /* Set background to grey */ + out_be32(&hw->disp_size, (var->yres << 16) | var->xres); /* Horizontal and vertical configuration register */ temp = var->left_margin << 22 | /* BP_H */ @@ -833,9 +844,20 @@ static void update_lcdc(struct fb_info *info) diu_ops.set_pixel_clock(var->pixclock); - out_be32(&hw->syn_pol, 0); /* SYNC SIGNALS POLARITY */ - out_be32(&hw->int_status, 0); /* INTERRUPT STATUS */ +#ifndef CONFIG_PPC_MPC512x + /* + * The PLUT register is defined differently on the MPC5121 than it + * is on other SOCs. Unfortunately, there's no documentation that + * explains how it's supposed to be programmed, so for now, we leave + * it at the default value on the MPC5121. + * + * For other SOCs, program it for the highest priority, which will + * reduce the chance of underrun. Technically, we should scale the + * priority to match the screen resolution, but doing that properly + * requires delicate fine-tuning for each use-case. + */ out_be32(&hw->plut, 0x01F5F666); +#endif /* Enable the DIU */ enable_lcdc(info); @@ -965,7 +987,6 @@ static int fsl_diu_set_par(struct fb_info *info) hw = data->diu_reg; set_fix(info); - mfbi->cursor_reset = 1; len = info->var.yres_virtual * info->fix.line_length; /* Alloc & dealloc each time resolution/bpp change */ @@ -1107,6 +1128,12 @@ static int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd, if (!arg) return -EINVAL; + + dev_dbg(info->dev, "ioctl %08x (dir=%s%s type=%u nr=%u size=%u)\n", cmd, + _IOC_DIR(cmd) & _IOC_READ ? "R" : "", + _IOC_DIR(cmd) & _IOC_WRITE ? "W" : "", + _IOC_TYPE(cmd), _IOC_NR(cmd), _IOC_SIZE(cmd)); + switch (cmd) { case MFB_SET_PIXFMT_OLD: dev_warn(info->dev, @@ -1180,6 +1207,23 @@ static int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd, ad->ckmin_b = ck.blue_min; } break; +#ifdef CONFIG_PPC_MPC512x + case MFB_SET_GAMMA: { + struct fsl_diu_data *data = mfbi->parent; + + if (copy_from_user(data->gamma, buf, sizeof(data->gamma))) + return -EFAULT; + setbits32(&data->diu_reg->gamma, 0); /* Force table reload */ + break; + } + case MFB_GET_GAMMA: { + struct fsl_diu_data *data = mfbi->parent; + + if (copy_to_user(buf, data->gamma, sizeof(data->gamma))) + return -EFAULT; + break; + } +#endif default: dev_err(info->dev, "unknown ioctl command (0x%08X)\n", cmd); return -ENOIOCTLCMD; @@ -1206,8 +1250,22 @@ static int fsl_diu_open(struct fb_info *info, int user) res = fsl_diu_set_par(info); if (res < 0) mfbi->count--; - else + else { + struct fsl_diu_data *data = mfbi->parent; + +#ifdef CONFIG_NOT_COHERENT_CACHE + /* + * Enable underrun detection and vertical sync + * interrupts. + */ + clrbits32(&data->diu_reg->int_mask, + INT_UNDRUN | INT_VSYNC); +#else + /* Enable underrun detection */ + clrbits32(&data->diu_reg->int_mask, INT_UNDRUN); +#endif fsl_diu_enable_panel(info); + } } spin_unlock(&diu_lock); @@ -1223,8 +1281,13 @@ static int fsl_diu_release(struct fb_info *info, int user) spin_lock(&diu_lock); mfbi->count--; - if (mfbi->count == 0) + if (mfbi->count == 0) { + struct fsl_diu_data *data = mfbi->parent; + + /* Disable interrupts */ + out_be32(&data->diu_reg->int_mask, 0xffffffff); fsl_diu_disable_panel(info); + } spin_unlock(&diu_lock); return res; @@ -1248,6 +1311,7 @@ static int __devinit install_fb(struct fb_info *info) { int rc; struct mfb_info *mfbi = info->par; + struct fsl_diu_data *data = mfbi->parent; const char *aoi_mode, *init_aoi_mode = "320x240"; struct fb_videomode *db = fsl_diu_mode_db; unsigned int dbsize = ARRAY_SIZE(fsl_diu_mode_db); @@ -1264,9 +1328,9 @@ static int __devinit install_fb(struct fb_info *info) return rc; if (mfbi->index == PLANE0) { - if (mfbi->edid_data) { + if (data->has_edid) { /* Now build modedb from EDID */ - fb_edid_to_monspecs(mfbi->edid_data, &info->monspecs); + fb_edid_to_monspecs(data->edid_data, &info->monspecs); fb_videomode_to_modelist(info->monspecs.modedb, info->monspecs.modedb_len, &info->modelist); @@ -1284,7 +1348,7 @@ static int __devinit install_fb(struct fb_info *info) * For plane 0 we continue and look into * driver's internal modedb. */ - if ((mfbi->index == PLANE0) && mfbi->edid_data) + if ((mfbi->index == PLANE0) && data->has_edid) has_default_mode = 0; else return -EINVAL; @@ -1348,9 +1412,6 @@ static void uninstall_fb(struct fb_info *info) if (!mfbi->registered) return; - if (mfbi->index == PLANE0) - kfree(mfbi->edid_data); - unregister_framebuffer(info); unmap_video_memory(info); if (&info->cmap) @@ -1362,7 +1423,7 @@ static void uninstall_fb(struct fb_info *info) static irqreturn_t fsl_diu_isr(int irq, void *dev_id) { struct diu __iomem *hw = dev_id; - unsigned int status = in_be32(&hw->int_status); + uint32_t status = in_be32(&hw->int_status); if (status) { /* This is the workaround for underrun */ @@ -1387,40 +1448,6 @@ static irqreturn_t fsl_diu_isr(int irq, void *dev_id) return IRQ_NONE; } -static int request_irq_local(struct fsl_diu_data *data) -{ - struct diu __iomem *hw = data->diu_reg; - u32 ints; - int ret; - - /* Read to clear the status */ - in_be32(&hw->int_status); - - ret = request_irq(data->irq, fsl_diu_isr, 0, "fsl-diu-fb", hw); - if (!ret) { - ints = INT_PARERR | INT_LS_BF_VS; -#if !defined(CONFIG_NOT_COHERENT_CACHE) - ints |= INT_VSYNC; -#endif - - /* Read to clear the status */ - in_be32(&hw->int_status); - out_be32(&hw->int_mask, ints); - } - - return ret; -} - -static void free_irq_local(struct fsl_diu_data *data) -{ - struct diu __iomem *hw = data->diu_reg; - - /* Disable all LCDC interrupt */ - out_be32(&hw->int_mask, 0x1f); - - free_irq(data->irq, NULL); -} - #ifdef CONFIG_PM /* * Power management hooks. Note that we won't be called from IRQ context, @@ -1496,8 +1523,8 @@ static int __devinit fsl_diu_probe(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; struct mfb_info *mfbi; struct fsl_diu_data *data; - int diu_mode; dma_addr_t dma_addr; /* DMA addr of fsl_diu_data struct */ + const void *prop; unsigned int i; int ret; @@ -1541,17 +1568,13 @@ static int __devinit fsl_diu_probe(struct platform_device *pdev) memcpy(mfbi, &mfb_template[i], sizeof(struct mfb_info)); mfbi->parent = data; mfbi->ad = &data->ad[i]; + } - if (mfbi->index == PLANE0) { - const u8 *prop; - int len; - - /* Get EDID */ - prop = of_get_property(np, "edid", &len); - if (prop && len == EDID_LENGTH) - mfbi->edid_data = kmemdup(prop, EDID_LENGTH, - GFP_KERNEL); - } + /* Get the EDID data from the device tree, if present */ + prop = of_get_property(np, "edid", &ret); + if (prop && ret == EDID_LENGTH) { + memcpy(data->edid_data, prop, EDID_LENGTH); + data->has_edid = true; } data->diu_reg = of_iomap(np, 0); @@ -1561,10 +1584,6 @@ static int __devinit fsl_diu_probe(struct platform_device *pdev) goto error; } - diu_mode = in_be32(&data->diu_reg->diu_mode); - if (diu_mode == MFB_MODE0) - out_be32(&data->diu_reg->diu_mode, 0); /* disable DIU */ - /* Get the IRQ of the DIU */ data->irq = irq_of_parse_and_map(np, 0); @@ -1586,11 +1605,11 @@ static int __devinit fsl_diu_probe(struct platform_device *pdev) data->dummy_ad.paddr = DMA_ADDR(data, dummy_ad); /* - * Let DIU display splash screen if it was pre-initialized - * by the bootloader, set dummy area descriptor otherwise. + * Let DIU continue to display splash screen if it was pre-initialized + * by the bootloader; otherwise, clear the display. */ - if (diu_mode == MFB_MODE0) - out_be32(&data->diu_reg->desc[0], data->dummy_ad.paddr); + if (in_be32(&data->diu_reg->diu_mode) == MFB_MODE0) + out_be32(&data->diu_reg->desc[0], 0); out_be32(&data->diu_reg->desc[1], data->dummy_ad.paddr); out_be32(&data->diu_reg->desc[2], data->dummy_ad.paddr); @@ -1603,7 +1622,16 @@ static int __devinit fsl_diu_probe(struct platform_device *pdev) } } - if (request_irq_local(data)) { + /* + * Older versions of U-Boot leave interrupts enabled, so disable + * all of them and clear the status register. + */ + out_be32(&data->diu_reg->int_mask, 0xffffffff); + in_be32(&data->diu_reg->int_status); + + ret = request_irq(data->irq, fsl_diu_isr, 0, "fsl-diu-fb", + &data->diu_reg); + if (ret) { dev_err(&pdev->dev, "could not claim irq\n"); goto error; } @@ -1638,7 +1666,8 @@ static int fsl_diu_remove(struct platform_device *pdev) data = dev_get_drvdata(&pdev->dev); disable_lcdc(&data->fsl_diu_info[0]); - free_irq_local(data); + + free_irq(data->irq, &data->diu_reg); for (i = 0; i < NUM_AOIS; i++) uninstall_fb(&data->fsl_diu_info[i]); @@ -1741,6 +1770,9 @@ static int __init fsl_diu_init(void) coherence_data_size = be32_to_cpup(prop) * 13; coherence_data_size /= 8; + pr_debug("fsl-diu-fb: coherence data size is %zu bytes\n", + coherence_data_size); + prop = of_get_property(np, "d-cache-line-size", NULL); if (prop == NULL) { pr_err("fsl-diu-fb: missing 'd-cache-line-size' property' " @@ -1750,10 +1782,17 @@ static int __init fsl_diu_init(void) } d_cache_line_size = be32_to_cpup(prop); + pr_debug("fsl-diu-fb: cache lines size is %u bytes\n", + d_cache_line_size); + of_node_put(np); coherence_data = vmalloc(coherence_data_size); - if (!coherence_data) + if (!coherence_data) { + pr_err("fsl-diu-fb: could not allocate coherence data " + "(size=%zu)\n", coherence_data_size); return -ENOMEM; + } + #endif ret = platform_driver_register(&fsl_diu_driver); diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c index 2ed7b633bbd..1a00ad241ed 100644 --- a/drivers/video/s3c-fb.c +++ b/drivers/video/s3c-fb.c @@ -189,7 +189,7 @@ struct s3c_fb_vsync { /** * struct s3c_fb - overall hardware state of the hardware - * @slock: The spinlock protection for this data sturucture. + * @slock: The spinlock protection for this data structure. * @dev: The device that we bound to, for printing, etc. * @bus_clk: The clk (hclk) feeding our interface and possibly pixclk. * @lcd_clk: The clk (sclk) feeding pixclk. @@ -268,10 +268,10 @@ static int s3c_fb_check_var(struct fb_var_screeninfo *var, case 8: if (sfb->variant.palette[win->index] != 0) { /* non palletised, A:1,R:2,G:3,B:2 mode */ - var->red.offset = 4; + var->red.offset = 5; var->green.offset = 2; var->blue.offset = 0; - var->red.length = 5; + var->red.length = 2; var->green.length = 3; var->blue.length = 2; var->transp.offset = 7; @@ -288,6 +288,7 @@ static int s3c_fb_check_var(struct fb_var_screeninfo *var, /* 666 with one bit alpha/transparency */ var->transp.offset = 18; var->transp.length = 1; + /* drop through */ case 18: var->bits_per_pixel = 32; @@ -329,6 +330,7 @@ static int s3c_fb_check_var(struct fb_var_screeninfo *var, default: dev_err(sfb->dev, "invalid bpp\n"); + return -EINVAL; } dev_dbg(sfb->dev, "%s: verified parameters\n", __func__); @@ -1544,8 +1546,7 @@ static int __devexit s3c_fb_remove(struct platform_device *pdev) #ifdef CONFIG_PM_SLEEP static int s3c_fb_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct s3c_fb *sfb = platform_get_drvdata(pdev); + struct s3c_fb *sfb = dev_get_drvdata(dev); struct s3c_fb_win *win; int win_no; @@ -1572,8 +1573,7 @@ static int s3c_fb_suspend(struct device *dev) static int s3c_fb_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct s3c_fb *sfb = platform_get_drvdata(pdev); + struct s3c_fb *sfb = dev_get_drvdata(dev); struct s3c_fb_platdata *pd = sfb->pdata; struct s3c_fb_win *win; int win_no; @@ -1623,7 +1623,7 @@ static int s3c_fb_resume(struct device *dev) if (!win) continue; - dev_dbg(&pdev->dev, "resuming window %d\n", win_no); + dev_dbg(dev, "resuming window %d\n", win_no); s3c_fb_set_par(win->fbinfo); } @@ -1636,8 +1636,7 @@ static int s3c_fb_resume(struct device *dev) #ifdef CONFIG_PM_RUNTIME static int s3c_fb_runtime_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct s3c_fb *sfb = platform_get_drvdata(pdev); + struct s3c_fb *sfb = dev_get_drvdata(dev); if (!sfb->variant.has_clksel) clk_disable_unprepare(sfb->lcd_clk); @@ -1649,8 +1648,7 @@ static int s3c_fb_runtime_suspend(struct device *dev) static int s3c_fb_runtime_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct s3c_fb *sfb = platform_get_drvdata(pdev); + struct s3c_fb *sfb = dev_get_drvdata(dev); struct s3c_fb_platdata *pd = sfb->pdata; clk_prepare_enable(sfb->bus_clk); @@ -1910,7 +1908,7 @@ static struct s3c_fb_driverdata s3c_fb_data_exynos4 = { static struct s3c_fb_driverdata s3c_fb_data_exynos5 = { .variant = { .nr_windows = 5, - .vidtcon = VIDTCON0, + .vidtcon = FIMD_V8_VIDTCON0, .wincon = WINCON(0), .winmap = WINxMAP(0), .keycon = WKEYCON, diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c index 3951fdae5f6..f4962292792 100644 --- a/drivers/video/sh_mipi_dsi.c +++ b/drivers/video/sh_mipi_dsi.c @@ -127,13 +127,12 @@ static void sh_mipi_shutdown(struct platform_device *pdev) sh_mipi_dsi_enable(mipi, false); } -static int sh_mipi_setup(struct sh_mipi *mipi, struct sh_mipi_dsi_info *pdata) +static int sh_mipi_setup(struct sh_mipi *mipi, const struct fb_videomode *mode) { void __iomem *base = mipi->base; - struct sh_mobile_lcdc_chan_cfg *ch = pdata->lcd_chan; + struct sh_mipi_dsi_info *pdata = mipi->pdev->dev.platform_data; u32 pctype, datatype, pixfmt, linelength, vmctr2; u32 tmp, top, bottom, delay, div; - bool yuv; int bpp; /* @@ -146,95 +145,79 @@ static int sh_mipi_setup(struct sh_mipi *mipi, struct sh_mipi_dsi_info *pdata) pctype = 0; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24; pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; - linelength = ch->lcd_modes[0].xres * 3; - yuv = false; + linelength = mode->xres * 3; break; case MIPI_RGB565: pctype = 1; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16; pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; - linelength = ch->lcd_modes[0].xres * 2; - yuv = false; + linelength = mode->xres * 2; break; case MIPI_RGB666_LP: pctype = 2; datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18; pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; - linelength = ch->lcd_modes[0].xres * 3; - yuv = false; + linelength = mode->xres * 3; break; case MIPI_RGB666: pctype = 3; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18; pixfmt = MIPI_DCS_PIXEL_FMT_18BIT; - linelength = (ch->lcd_modes[0].xres * 18 + 7) / 8; - yuv = false; + linelength = (mode->xres * 18 + 7) / 8; break; case MIPI_BGR888: pctype = 8; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24; pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; - linelength = ch->lcd_modes[0].xres * 3; - yuv = false; + linelength = mode->xres * 3; break; case MIPI_BGR565: pctype = 9; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16; pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; - linelength = ch->lcd_modes[0].xres * 2; - yuv = false; + linelength = mode->xres * 2; break; case MIPI_BGR666_LP: pctype = 0xa; datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18; pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; - linelength = ch->lcd_modes[0].xres * 3; - yuv = false; + linelength = mode->xres * 3; break; case MIPI_BGR666: pctype = 0xb; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18; pixfmt = MIPI_DCS_PIXEL_FMT_18BIT; - linelength = (ch->lcd_modes[0].xres * 18 + 7) / 8; - yuv = false; + linelength = (mode->xres * 18 + 7) / 8; break; case MIPI_YUYV: pctype = 4; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16; pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; - linelength = ch->lcd_modes[0].xres * 2; - yuv = true; + linelength = mode->xres * 2; break; case MIPI_UYVY: pctype = 5; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16; pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; - linelength = ch->lcd_modes[0].xres * 2; - yuv = true; + linelength = mode->xres * 2; break; case MIPI_YUV420_L: pctype = 6; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12; pixfmt = MIPI_DCS_PIXEL_FMT_12BIT; - linelength = (ch->lcd_modes[0].xres * 12 + 7) / 8; - yuv = true; + linelength = (mode->xres * 12 + 7) / 8; break; case MIPI_YUV420: pctype = 7; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12; pixfmt = MIPI_DCS_PIXEL_FMT_12BIT; /* Length of U/V line */ - linelength = (ch->lcd_modes[0].xres + 1) / 2; - yuv = true; + linelength = (mode->xres + 1) / 2; break; default: return -EINVAL; } - if ((yuv && ch->interface_type != YUV422) || - (!yuv && ch->interface_type != RGB24)) - return -EINVAL; - if (!pdata->lane) return -EINVAL; @@ -293,7 +276,7 @@ static int sh_mipi_setup(struct sh_mipi *mipi, struct sh_mipi_dsi_info *pdata) */ iowrite32(0x00000006, mipi->linkbase + DTCTR); /* VSYNC width = 2 (<< 17) */ - iowrite32((ch->lcd_modes[0].vsync_len << pdata->vsynw_offset) | + iowrite32((mode->vsync_len << pdata->vsynw_offset) | (pdata->clksrc << 16) | (pctype << 12) | datatype, mipi->linkbase + VMCTR1); @@ -327,7 +310,7 @@ static int sh_mipi_setup(struct sh_mipi *mipi, struct sh_mipi_dsi_info *pdata) top = linelength << 16; /* RGBLEN */ bottom = 0x00000001; if (pdata->flags & SH_MIPI_DSI_HSABM) /* HSALEN */ - bottom = (pdata->lane * ch->lcd_modes[0].hsync_len) - 10; + bottom = (pdata->lane * mode->hsync_len) - 10; iowrite32(top | bottom , mipi->linkbase + VMLEN1); /* @@ -347,18 +330,18 @@ static int sh_mipi_setup(struct sh_mipi *mipi, struct sh_mipi_dsi_info *pdata) div = 2; if (pdata->flags & SH_MIPI_DSI_HFPBM) { /* HBPLEN */ - top = ch->lcd_modes[0].hsync_len + ch->lcd_modes[0].left_margin; + top = mode->hsync_len + mode->left_margin; top = ((pdata->lane * top / div) - 10) << 16; } if (pdata->flags & SH_MIPI_DSI_HBPBM) { /* HFPLEN */ - bottom = ch->lcd_modes[0].right_margin; + bottom = mode->right_margin; bottom = (pdata->lane * bottom / div) - 12; } - bpp = linelength / ch->lcd_modes[0].xres; /* byte / pixel */ + bpp = linelength / mode->xres; /* byte / pixel */ if ((pdata->lane / div) > bpp) { - tmp = ch->lcd_modes[0].xres / bpp; /* output cycle */ - tmp = ch->lcd_modes[0].xres - tmp; /* (input - output) cycle */ + tmp = mode->xres / bpp; /* output cycle */ + tmp = mode->xres - tmp; /* (input - output) cycle */ delay = (pdata->lane * tmp); } @@ -369,7 +352,7 @@ static int sh_mipi_setup(struct sh_mipi *mipi, struct sh_mipi_dsi_info *pdata) /* setup LCD panel */ /* cf. drivers/video/omap/lcd_mipid.c */ - sh_mipi_dcs(ch->chan, MIPI_DCS_EXIT_SLEEP_MODE); + sh_mipi_dcs(pdata->channel, MIPI_DCS_EXIT_SLEEP_MODE); msleep(120); /* * [7] - Page Address Mode @@ -381,11 +364,11 @@ static int sh_mipi_setup(struct sh_mipi *mipi, struct sh_mipi_dsi_info *pdata) * [1] - Flip Horizontal * [0] - Flip Vertical */ - sh_mipi_dcs_param(ch->chan, MIPI_DCS_SET_ADDRESS_MODE, 0x00); + sh_mipi_dcs_param(pdata->channel, MIPI_DCS_SET_ADDRESS_MODE, 0x00); /* cf. set_data_lines() */ - sh_mipi_dcs_param(ch->chan, MIPI_DCS_SET_PIXEL_FORMAT, + sh_mipi_dcs_param(pdata->channel, MIPI_DCS_SET_PIXEL_FORMAT, pixfmt << 4); - sh_mipi_dcs(ch->chan, MIPI_DCS_SET_DISPLAY_ON); + sh_mipi_dcs(pdata->channel, MIPI_DCS_SET_DISPLAY_ON); /* Enable timeout counters */ iowrite32(0x00000f00, base + DSICTRL); @@ -405,7 +388,7 @@ static int mipi_display_on(struct sh_mobile_lcdc_entity *entity) if (ret < 0) goto mipi_display_on_fail1; - ret = sh_mipi_setup(mipi, pdata); + ret = sh_mipi_setup(mipi, &entity->def_mode); if (ret < 0) goto mipi_display_on_fail2; diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index 699487c287b..e78fe4bc152 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c @@ -438,7 +438,7 @@ static unsigned long lcdc_sys_read_data(void *handle) return lcdc_read(ch->lcdc, _LDDRDR) & LDDRDR_DRD_MASK; } -struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = { +static struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = { lcdc_sys_write_index, lcdc_sys_write_data, lcdc_sys_read_data, @@ -586,8 +586,8 @@ static int sh_mobile_lcdc_display_notify(struct sh_mobile_lcdc_chan *ch, * Just turn on, if we run a resume here, the * logo disappears. */ - info->var.width = monspec->max_x * 10; - info->var.height = monspec->max_y * 10; + info->var.width = ch->display.width; + info->var.height = ch->display.height; sh_mobile_lcdc_display_on(ch); } else { /* New monitor or have to wake up */ @@ -1614,6 +1614,15 @@ static int sh_mobile_lcdc_overlay_blank(int blank, struct fb_info *info) return 1; } +static int +sh_mobile_lcdc_overlay_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + struct sh_mobile_lcdc_overlay *ovl = info->par; + + return dma_mmap_coherent(ovl->channel->lcdc->dev, vma, ovl->fb_mem, + ovl->dma_handle, ovl->fb_size); +} + static struct fb_ops sh_mobile_lcdc_overlay_ops = { .owner = THIS_MODULE, .fb_read = fb_sys_read, @@ -1626,6 +1635,7 @@ static struct fb_ops sh_mobile_lcdc_overlay_ops = { .fb_ioctl = sh_mobile_lcdc_overlay_ioctl, .fb_check_var = sh_mobile_lcdc_overlay_check_var, .fb_set_par = sh_mobile_lcdc_overlay_set_par, + .fb_mmap = sh_mobile_lcdc_overlay_mmap, }; static void @@ -2093,6 +2103,15 @@ static int sh_mobile_lcdc_blank(int blank, struct fb_info *info) return 0; } +static int +sh_mobile_lcdc_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + struct sh_mobile_lcdc_chan *ch = info->par; + + return dma_mmap_coherent(ch->lcdc->dev, vma, ch->fb_mem, + ch->dma_handle, ch->fb_size); +} + static struct fb_ops sh_mobile_lcdc_ops = { .owner = THIS_MODULE, .fb_setcolreg = sh_mobile_lcdc_setcolreg, @@ -2108,6 +2127,7 @@ static struct fb_ops sh_mobile_lcdc_ops = { .fb_release = sh_mobile_lcdc_release, .fb_check_var = sh_mobile_lcdc_check_var, .fb_set_par = sh_mobile_lcdc_set_par, + .fb_mmap = sh_mobile_lcdc_mmap, }; static void @@ -2167,7 +2187,7 @@ sh_mobile_lcdc_channel_fb_cleanup(struct sh_mobile_lcdc_chan *ch) static int __devinit sh_mobile_lcdc_channel_fb_init(struct sh_mobile_lcdc_chan *ch, - const struct fb_videomode *mode, + const struct fb_videomode *modes, unsigned int num_modes) { struct sh_mobile_lcdc_priv *priv = ch->lcdc; @@ -2193,7 +2213,7 @@ sh_mobile_lcdc_channel_fb_init(struct sh_mobile_lcdc_chan *ch, info->pseudo_palette = &ch->pseudo_palette; info->par = ch; - fb_videomode_to_modelist(mode, num_modes, &info->modelist); + fb_videomode_to_modelist(modes, num_modes, &info->modelist); ret = fb_alloc_cmap(&info->cmap, PALETTE_NR, 0); if (ret < 0) { @@ -2227,9 +2247,9 @@ sh_mobile_lcdc_channel_fb_init(struct sh_mobile_lcdc_chan *ch, * default. */ var = &info->var; - fb_videomode_to_var(var, mode); - var->width = ch->cfg->panel_cfg.width; - var->height = ch->cfg->panel_cfg.height; + fb_videomode_to_var(var, modes); + var->width = ch->display.width; + var->height = ch->display.height; var->xres_virtual = ch->xres_virtual; var->yres_virtual = ch->yres_virtual; var->activate = FB_ACTIVATE_NOW; @@ -2262,6 +2282,7 @@ static int sh_mobile_lcdc_update_bl(struct backlight_device *bdev) bdev->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK)) brightness = 0; + ch->bl_brightness = brightness; return ch->cfg->bl_info.set_brightness(brightness); } @@ -2269,7 +2290,7 @@ static int sh_mobile_lcdc_get_brightness(struct backlight_device *bdev) { struct sh_mobile_lcdc_chan *ch = bl_get_data(bdev); - return ch->cfg->bl_info.get_brightness(); + return ch->bl_brightness; } static int sh_mobile_lcdc_check_fb(struct backlight_device *bdev, @@ -2516,10 +2537,10 @@ static int __devinit sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan * } static int __devinit -sh_mobile_lcdc_overlay_init(struct sh_mobile_lcdc_priv *priv, - struct sh_mobile_lcdc_overlay *ovl) +sh_mobile_lcdc_overlay_init(struct sh_mobile_lcdc_overlay *ovl) { const struct sh_mobile_lcdc_format_info *format; + struct device *dev = ovl->channel->lcdc->dev; int ret; if (ovl->cfg->fourcc == 0) @@ -2528,7 +2549,7 @@ sh_mobile_lcdc_overlay_init(struct sh_mobile_lcdc_priv *priv, /* Validate the format. */ format = sh_mobile_format_info(ovl->cfg->fourcc); if (format == NULL) { - dev_err(priv->dev, "Invalid FOURCC %08x\n", ovl->cfg->fourcc); + dev_err(dev, "Invalid FOURCC %08x\n", ovl->cfg->fourcc); return -EINVAL; } @@ -2556,10 +2577,10 @@ sh_mobile_lcdc_overlay_init(struct sh_mobile_lcdc_priv *priv, /* Allocate frame buffer memory. */ ovl->fb_size = ovl->cfg->max_xres * ovl->cfg->max_yres * format->bpp / 8 * 2; - ovl->fb_mem = dma_alloc_coherent(priv->dev, ovl->fb_size, - &ovl->dma_handle, GFP_KERNEL); + ovl->fb_mem = dma_alloc_coherent(dev, ovl->fb_size, &ovl->dma_handle, + GFP_KERNEL); if (!ovl->fb_mem) { - dev_err(priv->dev, "unable to allocate buffer\n"); + dev_err(dev, "unable to allocate buffer\n"); return -ENOMEM; } @@ -2571,11 +2592,11 @@ sh_mobile_lcdc_overlay_init(struct sh_mobile_lcdc_priv *priv, } static int __devinit -sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv, - struct sh_mobile_lcdc_chan *ch) +sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_chan *ch) { const struct sh_mobile_lcdc_format_info *format; const struct sh_mobile_lcdc_chan_cfg *cfg = ch->cfg; + struct device *dev = ch->lcdc->dev; const struct fb_videomode *max_mode; const struct fb_videomode *mode; unsigned int num_modes; @@ -2588,7 +2609,7 @@ sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv, /* Validate the format. */ format = sh_mobile_format_info(cfg->fourcc); if (format == NULL) { - dev_err(priv->dev, "Invalid FOURCC %08x.\n", cfg->fourcc); + dev_err(dev, "Invalid FOURCC %08x.\n", cfg->fourcc); return -EINVAL; } @@ -2604,7 +2625,7 @@ sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv, /* NV12/NV21 buffers must have even number of lines */ if ((cfg->fourcc == V4L2_PIX_FMT_NV12 || cfg->fourcc == V4L2_PIX_FMT_NV21) && (mode->yres & 0x1)) { - dev_err(priv->dev, "yres must be multiple of 2 for " + dev_err(dev, "yres must be multiple of 2 for " "YCbCr420 mode.\n"); return -EINVAL; } @@ -2618,7 +2639,7 @@ sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv, if (!max_size) max_size = MAX_XRES * MAX_YRES; else - dev_dbg(priv->dev, "Found largest videomode %ux%u\n", + dev_dbg(dev, "Found largest videomode %ux%u\n", max_mode->xres, max_mode->yres); if (cfg->lcd_modes == NULL) { @@ -2652,10 +2673,10 @@ sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv, /* Allocate frame buffer memory. */ ch->fb_size = max_size * format->bpp / 8 * 2; - ch->fb_mem = dma_alloc_coherent(priv->dev, ch->fb_size, &ch->dma_handle, + ch->fb_mem = dma_alloc_coherent(dev, ch->fb_size, &ch->dma_handle, GFP_KERNEL); if (ch->fb_mem == NULL) { - dev_err(priv->dev, "unable to allocate buffer\n"); + dev_err(dev, "unable to allocate buffer\n"); return -ENOMEM; } @@ -2663,8 +2684,7 @@ sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv, if (cfg->tx_dev) { if (!cfg->tx_dev->dev.driver || !try_module_get(cfg->tx_dev->dev.driver->owner)) { - dev_warn(priv->dev, - "unable to get transmitter device\n"); + dev_warn(dev, "unable to get transmitter device\n"); return -EINVAL; } ch->tx_dev = platform_get_drvdata(cfg->tx_dev); @@ -2772,9 +2792,9 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); for (i = 0; i < num_channels; i++) { - struct sh_mobile_lcdc_chan *ch = priv->ch + i; + struct sh_mobile_lcdc_chan *ch = &priv->ch[i]; - error = sh_mobile_lcdc_channel_init(priv, ch); + error = sh_mobile_lcdc_channel_init(ch); if (error) goto err1; } @@ -2785,7 +2805,7 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) ovl->cfg = &pdata->overlays[i]; ovl->channel = &priv->ch[0]; - error = sh_mobile_lcdc_overlay_init(priv, ovl); + error = sh_mobile_lcdc_overlay_init(ovl); if (error) goto err1; } diff --git a/drivers/video/sh_mobile_lcdcfb.h b/drivers/video/sh_mobile_lcdcfb.h index 0f92f6544b9..f839adef1d9 100644 --- a/drivers/video/sh_mobile_lcdcfb.h +++ b/drivers/video/sh_mobile_lcdcfb.h @@ -94,6 +94,7 @@ struct sh_mobile_lcdc_chan { /* Backlight */ struct backlight_device *bl; + unsigned int bl_brightness; /* FB */ struct fb_info *info; diff --git a/drivers/video/ssd1307fb.c b/drivers/video/ssd1307fb.c new file mode 100644 index 00000000000..6101f5c2f62 --- /dev/null +++ b/drivers/video/ssd1307fb.c @@ -0,0 +1,396 @@ +/* + * Driver for the Solomon SSD1307 OLED controler + * + * Copyright 2012 Free Electrons + * + * Licensed under the GPLv2 or later. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/i2c.h> +#include <linux/fb.h> +#include <linux/uaccess.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/pwm.h> +#include <linux/delay.h> + +#define SSD1307FB_WIDTH 96 +#define SSD1307FB_HEIGHT 16 + +#define SSD1307FB_DATA 0x40 +#define SSD1307FB_COMMAND 0x80 + +#define SSD1307FB_CONTRAST 0x81 +#define SSD1307FB_SEG_REMAP_ON 0xa1 +#define SSD1307FB_DISPLAY_OFF 0xae +#define SSD1307FB_DISPLAY_ON 0xaf +#define SSD1307FB_START_PAGE_ADDRESS 0xb0 + +struct ssd1307fb_par { + struct i2c_client *client; + struct fb_info *info; + struct pwm_device *pwm; + u32 pwm_period; + int reset; +}; + +static struct fb_fix_screeninfo ssd1307fb_fix __devinitdata = { + .id = "Solomon SSD1307", + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_MONO10, + .xpanstep = 0, + .ypanstep = 0, + .ywrapstep = 0, + .line_length = SSD1307FB_WIDTH / 8, + .accel = FB_ACCEL_NONE, +}; + +static struct fb_var_screeninfo ssd1307fb_var __devinitdata = { + .xres = SSD1307FB_WIDTH, + .yres = SSD1307FB_HEIGHT, + .xres_virtual = SSD1307FB_WIDTH, + .yres_virtual = SSD1307FB_HEIGHT, + .bits_per_pixel = 1, +}; + +static int ssd1307fb_write_array(struct i2c_client *client, u8 type, u8 *cmd, u32 len) +{ + u8 *buf; + int ret = 0; + + buf = kzalloc(len + 1, GFP_KERNEL); + if (!buf) { + dev_err(&client->dev, "Couldn't allocate sending buffer.\n"); + return -ENOMEM; + } + + buf[0] = type; + memcpy(buf + 1, cmd, len); + + ret = i2c_master_send(client, buf, len + 1); + if (ret != len + 1) { + dev_err(&client->dev, "Couldn't send I2C command.\n"); + goto error; + } + +error: + kfree(buf); + return ret; +} + +static inline int ssd1307fb_write_cmd_array(struct i2c_client *client, u8 *cmd, u32 len) +{ + return ssd1307fb_write_array(client, SSD1307FB_COMMAND, cmd, len); +} + +static inline int ssd1307fb_write_cmd(struct i2c_client *client, u8 cmd) +{ + return ssd1307fb_write_cmd_array(client, &cmd, 1); +} + +static inline int ssd1307fb_write_data_array(struct i2c_client *client, u8 *cmd, u32 len) +{ + return ssd1307fb_write_array(client, SSD1307FB_DATA, cmd, len); +} + +static inline int ssd1307fb_write_data(struct i2c_client *client, u8 data) +{ + return ssd1307fb_write_data_array(client, &data, 1); +} + +static void ssd1307fb_update_display(struct ssd1307fb_par *par) +{ + u8 *vmem = par->info->screen_base; + int i, j, k; + + /* + * The screen is divided in pages, each having a height of 8 + * pixels, and the width of the screen. When sending a byte of + * data to the controller, it gives the 8 bits for the current + * column. I.e, the first byte are the 8 bits of the first + * column, then the 8 bits for the second column, etc. + * + * + * Representation of the screen, assuming it is 5 bits + * wide. Each letter-number combination is a bit that controls + * one pixel. + * + * A0 A1 A2 A3 A4 + * B0 B1 B2 B3 B4 + * C0 C1 C2 C3 C4 + * D0 D1 D2 D3 D4 + * E0 E1 E2 E3 E4 + * F0 F1 F2 F3 F4 + * G0 G1 G2 G3 G4 + * H0 H1 H2 H3 H4 + * + * If you want to update this screen, you need to send 5 bytes: + * (1) A0 B0 C0 D0 E0 F0 G0 H0 + * (2) A1 B1 C1 D1 E1 F1 G1 H1 + * (3) A2 B2 C2 D2 E2 F2 G2 H2 + * (4) A3 B3 C3 D3 E3 F3 G3 H3 + * (5) A4 B4 C4 D4 E4 F4 G4 H4 + */ + + for (i = 0; i < (SSD1307FB_HEIGHT / 8); i++) { + ssd1307fb_write_cmd(par->client, SSD1307FB_START_PAGE_ADDRESS + (i + 1)); + ssd1307fb_write_cmd(par->client, 0x00); + ssd1307fb_write_cmd(par->client, 0x10); + + for (j = 0; j < SSD1307FB_WIDTH; j++) { + u8 buf = 0; + for (k = 0; k < 8; k++) { + u32 page_length = SSD1307FB_WIDTH * i; + u32 index = page_length + (SSD1307FB_WIDTH * k + j) / 8; + u8 byte = *(vmem + index); + u8 bit = byte & (1 << (7 - (j % 8))); + bit = bit >> (7 - (j % 8)); + buf |= bit << k; + } + ssd1307fb_write_data(par->client, buf); + } + } +} + + +static ssize_t ssd1307fb_write(struct fb_info *info, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct ssd1307fb_par *par = info->par; + unsigned long total_size; + unsigned long p = *ppos; + u8 __iomem *dst; + + total_size = info->fix.smem_len; + + if (p > total_size) + return -EINVAL; + + if (count + p > total_size) + count = total_size - p; + + if (!count) + return -EINVAL; + + dst = (void __force *) (info->screen_base + p); + + if (copy_from_user(dst, buf, count)) + return -EFAULT; + + ssd1307fb_update_display(par); + + *ppos += count; + + return count; +} + +static void ssd1307fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) +{ + struct ssd1307fb_par *par = info->par; + sys_fillrect(info, rect); + ssd1307fb_update_display(par); +} + +static void ssd1307fb_copyarea(struct fb_info *info, const struct fb_copyarea *area) +{ + struct ssd1307fb_par *par = info->par; + sys_copyarea(info, area); + ssd1307fb_update_display(par); +} + +static void ssd1307fb_imageblit(struct fb_info *info, const struct fb_image *image) +{ + struct ssd1307fb_par *par = info->par; + sys_imageblit(info, image); + ssd1307fb_update_display(par); +} + +static struct fb_ops ssd1307fb_ops = { + .owner = THIS_MODULE, + .fb_read = fb_sys_read, + .fb_write = ssd1307fb_write, + .fb_fillrect = ssd1307fb_fillrect, + .fb_copyarea = ssd1307fb_copyarea, + .fb_imageblit = ssd1307fb_imageblit, +}; + +static void ssd1307fb_deferred_io(struct fb_info *info, + struct list_head *pagelist) +{ + ssd1307fb_update_display(info->par); +} + +static struct fb_deferred_io ssd1307fb_defio = { + .delay = HZ, + .deferred_io = ssd1307fb_deferred_io, +}; + +static int __devinit ssd1307fb_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct fb_info *info; + u32 vmem_size = SSD1307FB_WIDTH * SSD1307FB_HEIGHT / 8; + struct ssd1307fb_par *par; + u8 *vmem; + int ret; + + if (!client->dev.of_node) { + dev_err(&client->dev, "No device tree data found!\n"); + return -EINVAL; + } + + info = framebuffer_alloc(sizeof(struct ssd1307fb_par), &client->dev); + if (!info) { + dev_err(&client->dev, "Couldn't allocate framebuffer.\n"); + return -ENOMEM; + } + + vmem = devm_kzalloc(&client->dev, vmem_size, GFP_KERNEL); + if (!vmem) { + dev_err(&client->dev, "Couldn't allocate graphical memory.\n"); + ret = -ENOMEM; + goto fb_alloc_error; + } + + info->fbops = &ssd1307fb_ops; + info->fix = ssd1307fb_fix; + info->fbdefio = &ssd1307fb_defio; + + info->var = ssd1307fb_var; + info->var.red.length = 1; + info->var.red.offset = 0; + info->var.green.length = 1; + info->var.green.offset = 0; + info->var.blue.length = 1; + info->var.blue.offset = 0; + + info->screen_base = (u8 __force __iomem *)vmem; + info->fix.smem_start = (unsigned long)vmem; + info->fix.smem_len = vmem_size; + + fb_deferred_io_init(info); + + par = info->par; + par->info = info; + par->client = client; + + par->reset = of_get_named_gpio(client->dev.of_node, + "reset-gpios", 0); + if (!gpio_is_valid(par->reset)) { + ret = -EINVAL; + goto reset_oled_error; + } + + ret = devm_gpio_request_one(&client->dev, par->reset, + GPIOF_OUT_INIT_HIGH, + "oled-reset"); + if (ret) { + dev_err(&client->dev, + "failed to request gpio %d: %d\n", + par->reset, ret); + goto reset_oled_error; + } + + par->pwm = pwm_get(&client->dev, NULL); + if (IS_ERR(par->pwm)) { + dev_err(&client->dev, "Could not get PWM from device tree!\n"); + ret = PTR_ERR(par->pwm); + goto pwm_error; + } + + par->pwm_period = pwm_get_period(par->pwm); + + dev_dbg(&client->dev, "Using PWM%d with a %dns period.\n", par->pwm->pwm, par->pwm_period); + + ret = register_framebuffer(info); + if (ret) { + dev_err(&client->dev, "Couldn't register the framebuffer\n"); + goto fbreg_error; + } + + i2c_set_clientdata(client, info); + + /* Reset the screen */ + gpio_set_value(par->reset, 0); + udelay(4); + gpio_set_value(par->reset, 1); + udelay(4); + + /* Enable the PWM */ + pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period); + pwm_enable(par->pwm); + + /* Map column 127 of the OLED to segment 0 */ + ret = ssd1307fb_write_cmd(client, SSD1307FB_SEG_REMAP_ON); + if (ret < 0) { + dev_err(&client->dev, "Couldn't remap the screen.\n"); + goto remap_error; + } + + /* Turn on the display */ + ret = ssd1307fb_write_cmd(client, SSD1307FB_DISPLAY_ON); + if (ret < 0) { + dev_err(&client->dev, "Couldn't turn the display on.\n"); + goto remap_error; + } + + dev_info(&client->dev, "fb%d: %s framebuffer device registered, using %d bytes of video memory\n", info->node, info->fix.id, vmem_size); + + return 0; + +remap_error: + unregister_framebuffer(info); + pwm_disable(par->pwm); +fbreg_error: + pwm_put(par->pwm); +pwm_error: +reset_oled_error: + fb_deferred_io_cleanup(info); +fb_alloc_error: + framebuffer_release(info); + return ret; +} + +static int __devexit ssd1307fb_remove(struct i2c_client *client) +{ + struct fb_info *info = i2c_get_clientdata(client); + struct ssd1307fb_par *par = info->par; + + unregister_framebuffer(info); + pwm_disable(par->pwm); + pwm_put(par->pwm); + fb_deferred_io_cleanup(info); + framebuffer_release(info); + + return 0; +} + +static const struct i2c_device_id ssd1307fb_i2c_id[] = { + { "ssd1307fb", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ssd1307fb_i2c_id); + +static const struct of_device_id ssd1307fb_of_match[] = { + { .compatible = "solomon,ssd1307fb-i2c" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ssd1307fb_of_match); + +static struct i2c_driver ssd1307fb_driver = { + .probe = ssd1307fb_probe, + .remove = __devexit_p(ssd1307fb_remove), + .id_table = ssd1307fb_i2c_id, + .driver = { + .name = "ssd1307fb", + .of_match_table = of_match_ptr(ssd1307fb_of_match), + .owner = THIS_MODULE, + }, +}; + +module_i2c_driver(ssd1307fb_driver); + +MODULE_DESCRIPTION("FB driver for the Solomon SSD1307 OLED controler"); +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); +MODULE_LICENSE("GPL"); |