diff options
author | Florian Tobias Schandinat <FlorianSchandinat@gmx.de> | 2012-03-13 23:17:43 +0000 |
---|---|---|
committer | Florian Tobias Schandinat <FlorianSchandinat@gmx.de> | 2012-03-13 23:17:43 +0000 |
commit | f9b4a5ce1a6b3154ff857e17d031473e0bac72d2 (patch) | |
tree | 247e94804b1abcfdfbd623b9b919ed9e2c76a994 /drivers/video | |
parent | afb0499b0e43e2ee71a92bec9331635dc1173a07 (diff) | |
parent | af89956be14ae5bb304872756a47309edc2c94fb (diff) |
Merge branch 'for-next' of git://linuxtv.org/pinchartl/fbdev into fbdev-next
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/Kconfig | 1 | ||||
-rw-r--r-- | drivers/video/sh_mipi_dsi.c | 97 | ||||
-rw-r--r-- | drivers/video/sh_mobile_hdmi.c | 297 | ||||
-rw-r--r-- | drivers/video/sh_mobile_lcdcfb.c | 1280 | ||||
-rw-r--r-- | drivers/video/sh_mobile_lcdcfb.h | 84 | ||||
-rw-r--r-- | drivers/video/sh_mobile_meram.c | 690 |
6 files changed, 1297 insertions, 1152 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 8951cbd2d2fc..a43594243186 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -2016,6 +2016,7 @@ config FB_SH_MOBILE_HDMI config FB_SH_MOBILE_MERAM tristate "SuperH Mobile MERAM read ahead support for LCDC" depends on FB_SH_MOBILE_LCDC + select GENERIC_ALLOCATOR default y ---help--- Enable MERAM support for the SH-Mobile LCD controller. diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c index 05151b82f40f..42ad0f707e88 100644 --- a/drivers/video/sh_mipi_dsi.c +++ b/drivers/video/sh_mipi_dsi.c @@ -24,6 +24,8 @@ #include <video/sh_mipi_dsi.h> #include <video/sh_mobile_lcdc.h> +#include "sh_mobile_lcdcfb.h" + #define SYSCTRL 0x0000 #define SYSCONF 0x0004 #define TIMSET 0x0008 @@ -50,16 +52,16 @@ #define MAX_SH_MIPI_DSI 2 struct sh_mipi { + struct sh_mobile_lcdc_entity entity; + void __iomem *base; void __iomem *linkbase; struct clk *dsit_clk; struct platform_device *pdev; - - void *next_board_data; - void (*next_display_on)(void *board_data, struct fb_info *info); - void (*next_display_off)(void *board_data); }; +#define to_sh_mipi(e) container_of(e, struct sh_mipi, entity) + static struct sh_mipi *mipi_dsi[MAX_SH_MIPI_DSI]; /* Protect the above array */ @@ -120,7 +122,7 @@ static void sh_mipi_dsi_enable(struct sh_mipi *mipi, bool enable) static void sh_mipi_shutdown(struct platform_device *pdev) { - struct sh_mipi *mipi = platform_get_drvdata(pdev); + struct sh_mipi *mipi = to_sh_mipi(platform_get_drvdata(pdev)); sh_mipi_dsi_enable(mipi, false); } @@ -145,77 +147,77 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, pctype = 0; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24; pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; - linelength = ch->lcd_cfg[0].xres * 3; + linelength = ch->lcd_modes[0].xres * 3; yuv = false; break; case MIPI_RGB565: pctype = 1; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16; pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; - linelength = ch->lcd_cfg[0].xres * 2; + linelength = ch->lcd_modes[0].xres * 2; yuv = false; break; case MIPI_RGB666_LP: pctype = 2; datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18; pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; - linelength = ch->lcd_cfg[0].xres * 3; + linelength = ch->lcd_modes[0].xres * 3; yuv = false; break; case MIPI_RGB666: pctype = 3; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18; pixfmt = MIPI_DCS_PIXEL_FMT_18BIT; - linelength = (ch->lcd_cfg[0].xres * 18 + 7) / 8; + linelength = (ch->lcd_modes[0].xres * 18 + 7) / 8; yuv = false; break; case MIPI_BGR888: pctype = 8; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24; pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; - linelength = ch->lcd_cfg[0].xres * 3; + linelength = ch->lcd_modes[0].xres * 3; yuv = false; break; case MIPI_BGR565: pctype = 9; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16; pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; - linelength = ch->lcd_cfg[0].xres * 2; + linelength = ch->lcd_modes[0].xres * 2; yuv = false; break; case MIPI_BGR666_LP: pctype = 0xa; datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18; pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; - linelength = ch->lcd_cfg[0].xres * 3; + linelength = ch->lcd_modes[0].xres * 3; yuv = false; break; case MIPI_BGR666: pctype = 0xb; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18; pixfmt = MIPI_DCS_PIXEL_FMT_18BIT; - linelength = (ch->lcd_cfg[0].xres * 18 + 7) / 8; + linelength = (ch->lcd_modes[0].xres * 18 + 7) / 8; yuv = false; break; case MIPI_YUYV: pctype = 4; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16; pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; - linelength = ch->lcd_cfg[0].xres * 2; + linelength = ch->lcd_modes[0].xres * 2; yuv = true; break; case MIPI_UYVY: pctype = 5; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16; pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; - linelength = ch->lcd_cfg[0].xres * 2; + linelength = ch->lcd_modes[0].xres * 2; yuv = true; break; case MIPI_YUV420_L: pctype = 6; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12; pixfmt = MIPI_DCS_PIXEL_FMT_12BIT; - linelength = (ch->lcd_cfg[0].xres * 12 + 7) / 8; + linelength = (ch->lcd_modes[0].xres * 12 + 7) / 8; yuv = true; break; case MIPI_YUV420: @@ -223,7 +225,7 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12; pixfmt = MIPI_DCS_PIXEL_FMT_12BIT; /* Length of U/V line */ - linelength = (ch->lcd_cfg[0].xres + 1) / 2; + linelength = (ch->lcd_modes[0].xres + 1) / 2; yuv = true; break; default: @@ -292,7 +294,7 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, */ iowrite32(0x00000006, mipi->linkbase + DTCTR); /* VSYNC width = 2 (<< 17) */ - iowrite32((ch->lcd_cfg[0].vsync_len << pdata->vsynw_offset) | + iowrite32((ch->lcd_modes[0].vsync_len << pdata->vsynw_offset) | (pdata->clksrc << 16) | (pctype << 12) | datatype, mipi->linkbase + VMCTR1); @@ -326,7 +328,7 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, top = linelength << 16; /* RGBLEN */ bottom = 0x00000001; if (pdata->flags & SH_MIPI_DSI_HSABM) /* HSALEN */ - bottom = (pdata->lane * ch->lcd_cfg[0].hsync_len) - 10; + bottom = (pdata->lane * ch->lcd_modes[0].hsync_len) - 10; iowrite32(top | bottom , mipi->linkbase + VMLEN1); /* @@ -346,18 +348,18 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, div = 2; if (pdata->flags & SH_MIPI_DSI_HFPBM) { /* HBPLEN */ - top = ch->lcd_cfg[0].hsync_len + ch->lcd_cfg[0].left_margin; + top = ch->lcd_modes[0].hsync_len + ch->lcd_modes[0].left_margin; top = ((pdata->lane * top / div) - 10) << 16; } if (pdata->flags & SH_MIPI_DSI_HBPBM) { /* HFPLEN */ - bottom = ch->lcd_cfg[0].right_margin; + bottom = ch->lcd_modes[0].right_margin; bottom = (pdata->lane * bottom / div) - 12; } - bpp = linelength / ch->lcd_cfg[0].xres; /* byte / pixel */ + bpp = linelength / ch->lcd_modes[0].xres; /* byte / pixel */ if ((pdata->lane / div) > bpp) { - tmp = ch->lcd_cfg[0].xres / bpp; /* output cycle */ - tmp = ch->lcd_cfg[0].xres - tmp; /* (input - output) cycle */ + tmp = ch->lcd_modes[0].xres / bpp; /* output cycle */ + tmp = ch->lcd_modes[0].xres - tmp; /* (input - output) cycle */ delay = (pdata->lane * tmp); } @@ -392,9 +394,9 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, return 0; } -static void mipi_display_on(void *arg, struct fb_info *info) +static int mipi_display_on(struct sh_mobile_lcdc_entity *entity) { - struct sh_mipi *mipi = arg; + struct sh_mipi *mipi = to_sh_mipi(entity); struct sh_mipi_dsi_info *pdata = mipi->pdev->dev.platform_data; int ret; @@ -410,25 +412,21 @@ static void mipi_display_on(void *arg, struct fb_info *info) sh_mipi_dsi_enable(mipi, true); - if (mipi->next_display_on) - mipi->next_display_on(mipi->next_board_data, info); - - return; + return SH_MOBILE_LCDC_DISPLAY_CONNECTED; mipi_display_on_fail1: pm_runtime_put_sync(&mipi->pdev->dev); mipi_display_on_fail2: pdata->set_dot_clock(mipi->pdev, mipi->base, 0); + + return ret; } -static void mipi_display_off(void *arg) +static void mipi_display_off(struct sh_mobile_lcdc_entity *entity) { - struct sh_mipi *mipi = arg; + struct sh_mipi *mipi = to_sh_mipi(entity); struct sh_mipi_dsi_info *pdata = mipi->pdev->dev.platform_data; - if (mipi->next_display_off) - mipi->next_display_off(mipi->next_board_data); - sh_mipi_dsi_enable(mipi, false); pdata->set_dot_clock(mipi->pdev, mipi->base, 0); @@ -436,6 +434,11 @@ static void mipi_display_off(void *arg) pm_runtime_put_sync(&mipi->pdev->dev); } +static const struct sh_mobile_lcdc_entity_ops mipi_ops = { + .display_on = mipi_display_on, + .display_off = mipi_display_off, +}; + static int __init sh_mipi_probe(struct platform_device *pdev) { struct sh_mipi *mipi; @@ -467,6 +470,9 @@ static int __init sh_mipi_probe(struct platform_device *pdev) goto ealloc; } + mipi->entity.owner = THIS_MODULE; + mipi->entity.ops = &mipi_ops; + if (!request_mem_region(res->start, resource_size(res), pdev->name)) { dev_err(&pdev->dev, "MIPI register region already claimed\n"); ret = -EBUSY; @@ -521,18 +527,7 @@ static int __init sh_mipi_probe(struct platform_device *pdev) pm_runtime_resume(&pdev->dev); mutex_unlock(&array_lock); - platform_set_drvdata(pdev, mipi); - - /* Save original LCDC callbacks */ - mipi->next_board_data = pdata->lcd_chan->board_cfg.board_data; - mipi->next_display_on = pdata->lcd_chan->board_cfg.display_on; - mipi->next_display_off = pdata->lcd_chan->board_cfg.display_off; - - /* Set up LCDC callbacks */ - pdata->lcd_chan->board_cfg.board_data = mipi; - pdata->lcd_chan->board_cfg.display_on = mipi_display_on; - pdata->lcd_chan->board_cfg.display_off = mipi_display_off; - pdata->lcd_chan->board_cfg.owner = THIS_MODULE; + platform_set_drvdata(pdev, &mipi->entity); return 0; @@ -558,10 +553,9 @@ efindslot: static int __exit sh_mipi_remove(struct platform_device *pdev) { - struct sh_mipi_dsi_info *pdata = pdev->dev.platform_data; struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); struct resource *res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); - struct sh_mipi *mipi = platform_get_drvdata(pdev); + struct sh_mipi *mipi = to_sh_mipi(platform_get_drvdata(pdev)); int i, ret; mutex_lock(&array_lock); @@ -581,11 +575,6 @@ static int __exit sh_mipi_remove(struct platform_device *pdev) if (ret < 0) return ret; - pdata->lcd_chan->board_cfg.owner = NULL; - pdata->lcd_chan->board_cfg.display_on = NULL; - pdata->lcd_chan->board_cfg.display_off = NULL; - pdata->lcd_chan->board_cfg.board_data = NULL; - pm_runtime_disable(&pdev->dev); clk_disable(mipi->dsit_clk); clk_put(mipi->dsit_clk); diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c index 647ba984f00f..eafb19da2c07 100644 --- a/drivers/video/sh_mobile_hdmi.c +++ b/drivers/video/sh_mobile_hdmi.c @@ -208,6 +208,8 @@ enum hotplug_state { }; struct sh_hdmi { + struct sh_mobile_lcdc_entity entity; + void __iomem *base; enum hotplug_state hp_state; /* hot-plug status */ u8 preprogrammed_vic; /* use a pre-programmed VIC or @@ -217,14 +219,13 @@ struct sh_hdmi { u8 edid_blocks; struct clk *hdmi_clk; struct device *dev; - struct fb_info *info; - struct mutex mutex; /* Protect the info pointer */ struct delayed_work edid_work; - struct fb_var_screeninfo var; + struct fb_videomode mode; struct fb_monspecs monspec; - struct notifier_block notifier; }; +#define entity_to_sh_hdmi(e) container_of(e, struct sh_hdmi, entity) + static void hdmi_write(struct sh_hdmi *hdmi, u8 data, u8 reg) { iowrite8(data, hdmi->base + reg); @@ -290,24 +291,24 @@ static struct snd_soc_codec_driver soc_codec_dev_sh_hdmi = { /* External video parameter settings */ static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi) { - struct fb_var_screeninfo *var = &hdmi->var; + struct fb_videomode *mode = &hdmi->mode; u16 htotal, hblank, hdelay, vtotal, vblank, vdelay, voffset; u8 sync = 0; - htotal = var->xres + var->right_margin + var->left_margin + var->hsync_len; - - hdelay = var->hsync_len + var->left_margin; - hblank = var->right_margin + hdelay; + htotal = mode->xres + mode->right_margin + mode->left_margin + + mode->hsync_len; + hdelay = mode->hsync_len + mode->left_margin; + hblank = mode->right_margin + hdelay; /* * Vertical timing looks a bit different in Figure 18, * but let's try the same first by setting offset = 0 */ - vtotal = var->yres + var->upper_margin + var->lower_margin + var->vsync_len; - - vdelay = var->vsync_len + var->upper_margin; - vblank = var->lower_margin + vdelay; - voffset = min(var->upper_margin / 2, 6U); + vtotal = mode->yres + mode->upper_margin + mode->lower_margin + + mode->vsync_len; + vdelay = mode->vsync_len + mode->upper_margin; + vblank = mode->lower_margin + vdelay; + voffset = min(mode->upper_margin / 2, 6U); /* * [3]: VSYNC polarity: Positive @@ -315,14 +316,14 @@ static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi) * [1]: Interlace/Progressive: Progressive * [0]: External video settings enable: used. */ - if (var->sync & FB_SYNC_HOR_HIGH_ACT) + if (mode->sync & FB_SYNC_HOR_HIGH_ACT) sync |= 4; - if (var->sync & FB_SYNC_VERT_HIGH_ACT) + if (mode->sync & FB_SYNC_VERT_HIGH_ACT) sync |= 8; dev_dbg(hdmi->dev, "H: %u, %u, %u, %u; V: %u, %u, %u, %u; sync 0x%x\n", - htotal, hblank, hdelay, var->hsync_len, - vtotal, vblank, vdelay, var->vsync_len, sync); + htotal, hblank, hdelay, mode->hsync_len, + vtotal, vblank, vdelay, mode->vsync_len, sync); hdmi_write(hdmi, sync | (voffset << 4), HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS); @@ -335,8 +336,8 @@ static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi) hdmi_write(hdmi, hdelay, HDMI_EXTERNAL_H_DELAY_7_0); hdmi_write(hdmi, hdelay >> 8, HDMI_EXTERNAL_H_DELAY_9_8); - hdmi_write(hdmi, var->hsync_len, HDMI_EXTERNAL_H_DURATION_7_0); - hdmi_write(hdmi, var->hsync_len >> 8, HDMI_EXTERNAL_H_DURATION_9_8); + hdmi_write(hdmi, mode->hsync_len, HDMI_EXTERNAL_H_DURATION_7_0); + hdmi_write(hdmi, mode->hsync_len >> 8, HDMI_EXTERNAL_H_DURATION_9_8); hdmi_write(hdmi, vtotal, HDMI_EXTERNAL_V_TOTAL_7_0); hdmi_write(hdmi, vtotal >> 8, HDMI_EXTERNAL_V_TOTAL_9_8); @@ -345,7 +346,7 @@ static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi) hdmi_write(hdmi, vdelay, HDMI_EXTERNAL_V_DELAY); - hdmi_write(hdmi, var->vsync_len, HDMI_EXTERNAL_V_DURATION); + hdmi_write(hdmi, mode->vsync_len, HDMI_EXTERNAL_V_DURATION); /* Set bit 0 of HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS here for external mode */ if (!hdmi->preprogrammed_vic) @@ -472,7 +473,7 @@ static void sh_hdmi_audio_config(struct sh_hdmi *hdmi) */ static void sh_hdmi_phy_config(struct sh_hdmi *hdmi) { - if (hdmi->var.pixclock < 10000) { + if (hdmi->mode.pixclock < 10000) { /* for 1080p8bit 148MHz */ hdmi_write(hdmi, 0x1d, HDMI_SLIPHDMIT_PARAM_SETTINGS_1); hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_2); @@ -483,7 +484,7 @@ static void sh_hdmi_phy_config(struct sh_hdmi *hdmi) hdmi_write(hdmi, 0x0e, HDMI_SLIPHDMIT_PARAM_SETTINGS_8); hdmi_write(hdmi, 0x25, HDMI_SLIPHDMIT_PARAM_SETTINGS_9); hdmi_write(hdmi, 0x04, HDMI_SLIPHDMIT_PARAM_SETTINGS_10); - } else if (hdmi->var.pixclock < 30000) { + } else if (hdmi->mode.pixclock < 30000) { /* 720p, 8bit, 74.25MHz. Might need to be adjusted for other formats */ /* * [1:0] Speed_A @@ -732,14 +733,12 @@ static unsigned long sh_hdmi_rate_error(struct sh_hdmi *hdmi, static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, unsigned long *parent_rate) { - struct fb_var_screeninfo tmpvar; - struct fb_var_screeninfo *var = &tmpvar; + struct sh_mobile_lcdc_chan *ch = hdmi->entity.lcdc; const struct fb_videomode *mode, *found = NULL; - struct fb_info *info = hdmi->info; - struct fb_modelist *modelist = NULL; unsigned int f_width = 0, f_height = 0, f_refresh = 0; unsigned long found_rate_error = ULONG_MAX; /* silly compiler... */ bool scanning = false, preferred_bad = false; + bool use_edid_mode = false; u8 edid[128]; char *forced; int i; @@ -854,12 +853,9 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, } /* Check if supported: sufficient fb memory, supported clock-rate */ - fb_videomode_to_var(var, mode); - - var->bits_per_pixel = info->var.bits_per_pixel; - - if (info && info->fbops->fb_check_var && - info->fbops->fb_check_var(var, info)) { + if (ch && ch->notify && + ch->notify(ch, SH_MOBILE_LCDC_EVENT_DISPLAY_MODE, mode, + NULL)) { scanning = true; preferred_bad = true; continue; @@ -867,28 +863,19 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, found = mode; found_rate_error = rate_error; + use_edid_mode = true; } - hdmi->var.width = hdmi->monspec.max_x * 10; - hdmi->var.height = hdmi->monspec.max_y * 10; - /* - * TODO 1: if no ->info is present, postpone running the config until - * after ->info first gets registered. + * TODO 1: if no default mode is present, postpone running the config + * until after the LCDC channel is initialized. * TODO 2: consider registering the HDMI platform device from the LCDC - * driver, and passing ->info with HDMI platform data. + * driver. */ - if (info && !found) { - modelist = info->modelist.next && - !list_empty(&info->modelist) ? - list_entry(info->modelist.next, - struct fb_modelist, list) : - NULL; - - if (modelist) { - found = &modelist->mode; - found_rate_error = sh_hdmi_rate_error(hdmi, found, hdmi_rate, parent_rate); - } + if (!found && hdmi->entity.def_mode.xres != 0) { + found = &hdmi->entity.def_mode; + found_rate_error = sh_hdmi_rate_error(hdmi, found, hdmi_rate, + parent_rate); } /* No cookie today */ @@ -912,12 +899,13 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, else hdmi->preprogrammed_vic = 0; - dev_dbg(hdmi->dev, "Using %s %s mode %ux%u@%uHz (%luHz), clock error %luHz\n", - modelist ? "default" : "EDID", hdmi->preprogrammed_vic ? "VIC" : "external", - found->xres, found->yres, found->refresh, - PICOS2KHZ(found->pixclock) * 1000, found_rate_error); + dev_dbg(hdmi->dev, "Using %s %s mode %ux%u@%uHz (%luHz), " + "clock error %luHz\n", use_edid_mode ? "EDID" : "default", + hdmi->preprogrammed_vic ? "VIC" : "external", found->xres, + found->yres, found->refresh, PICOS2KHZ(found->pixclock) * 1000, + found_rate_error); - fb_videomode_to_var(&hdmi->var, found); + hdmi->mode = *found; sh_hdmi_external_video_param(hdmi); return 0; @@ -998,22 +986,12 @@ static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id) return IRQ_HANDLED; } -/* locking: called with info->lock held, or before register_framebuffer() */ -static void sh_hdmi_display_on(void *arg, struct fb_info *info) +static int sh_hdmi_display_on(struct sh_mobile_lcdc_entity *entity) { - /* - * info is guaranteed to be valid, when we are called, because our - * FB_EVENT_FB_UNBIND notify is also called with info->lock held - */ - struct sh_hdmi *hdmi = arg; - struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; - struct sh_mobile_lcdc_chan *ch = info->par; + struct sh_hdmi *hdmi = entity_to_sh_hdmi(entity); - dev_dbg(hdmi->dev, "%s(%p): state %x\n", __func__, - pdata->lcd_dev, info->state); - - /* No need to lock */ - hdmi->info = info; + dev_dbg(hdmi->dev, "%s(%p): state %x\n", __func__, hdmi, + hdmi->hp_state); /* * hp_state can be set to @@ -1021,56 +999,30 @@ static void sh_hdmi_display_on(void *arg, struct fb_info *info) * HDMI_HOTPLUG_CONNECTED: on monitor plug-in * HDMI_HOTPLUG_EDID_DONE: on EDID read completion */ - switch (hdmi->hp_state) { - case HDMI_HOTPLUG_EDID_DONE: + if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) { /* PS mode d->e. All functions are active */ hdmi_write(hdmi, 0x80, HDMI_SYSTEM_CTRL); dev_dbg(hdmi->dev, "HDMI running\n"); - break; - case HDMI_HOTPLUG_DISCONNECTED: - info->state = FBINFO_STATE_SUSPENDED; - default: - hdmi->var = ch->display_var; } + + return hdmi->hp_state == HDMI_HOTPLUG_DISCONNECTED + ? SH_MOBILE_LCDC_DISPLAY_DISCONNECTED + : SH_MOBILE_LCDC_DISPLAY_CONNECTED; } -/* locking: called with info->lock held */ -static void sh_hdmi_display_off(void *arg) +static void sh_hdmi_display_off(struct sh_mobile_lcdc_entity *entity) { - struct sh_hdmi *hdmi = arg; - struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; + struct sh_hdmi *hdmi = entity_to_sh_hdmi(entity); - dev_dbg(hdmi->dev, "%s(%p)\n", __func__, pdata->lcd_dev); + dev_dbg(hdmi->dev, "%s(%p)\n", __func__, hdmi); /* PS mode e->a */ hdmi_write(hdmi, 0x10, HDMI_SYSTEM_CTRL); } -static bool sh_hdmi_must_reconfigure(struct sh_hdmi *hdmi) -{ - struct fb_info *info = hdmi->info; - struct sh_mobile_lcdc_chan *ch = info->par; - struct fb_var_screeninfo *new_var = &hdmi->var, *old_var = &ch->display_var; - struct fb_videomode mode1, mode2; - - fb_var_to_videomode(&mode1, old_var); - fb_var_to_videomode(&mode2, new_var); - - dev_dbg(info->dev, "Old %ux%u, new %ux%u\n", - mode1.xres, mode1.yres, mode2.xres, mode2.yres); - - if (fb_mode_is_equal(&mode1, &mode2)) { - /* It can be a different monitor with an equal video-mode */ - old_var->width = new_var->width; - old_var->height = new_var->height; - return false; - } - - dev_dbg(info->dev, "Switching %u -> %u lines\n", - mode1.yres, mode2.yres); - *old_var = *new_var; - - return true; -} +static const struct sh_mobile_lcdc_entity_ops sh_hdmi_ops = { + .display_on = sh_hdmi_display_on, + .display_off = sh_hdmi_display_off, +}; /** * sh_hdmi_clk_configure() - set HDMI clock frequency and enable the clock @@ -1111,20 +1063,11 @@ static long sh_hdmi_clk_configure(struct sh_hdmi *hdmi, unsigned long hdmi_rate, static void sh_hdmi_edid_work_fn(struct work_struct *work) { struct sh_hdmi *hdmi = container_of(work, struct sh_hdmi, edid_work.work); - struct fb_info *info; - struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; - struct sh_mobile_lcdc_chan *ch; + struct sh_mobile_lcdc_chan *ch = hdmi->entity.lcdc; int ret; - dev_dbg(hdmi->dev, "%s(%p): begin, hotplug status %d\n", __func__, - pdata->lcd_dev, hdmi->hp_state); - - if (!pdata->lcd_dev) - return; - - mutex_lock(&hdmi->mutex); - - info = hdmi->info; + dev_dbg(hdmi->dev, "%s(%p): begin, hotplug status %d\n", __func__, hdmi, + hdmi->hp_state); if (hdmi->hp_state == HDMI_HOTPLUG_CONNECTED) { unsigned long parent_rate = 0, hdmi_rate; @@ -1145,103 +1088,32 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work) /* Switched to another (d) power-save mode */ msleep(10); - if (!info) - goto out; - - ch = info->par; - - if (lock_fb_info(info)) { - console_lock(); - - /* HDMI plug in */ - if (!sh_hdmi_must_reconfigure(hdmi) && - info->state == FBINFO_STATE_RUNNING) { - /* - * First activation with the default monitor - just turn - * on, if we run a resume here, the logo disappears - */ - info->var.width = hdmi->var.width; - info->var.height = hdmi->var.height; - sh_hdmi_display_on(hdmi, info); - } else { - /* New monitor or have to wake up */ - fb_set_suspend(info, 0); - } - - console_unlock(); - unlock_fb_info(info); - } + if (ch && ch->notify) + ch->notify(ch, SH_MOBILE_LCDC_EVENT_DISPLAY_CONNECT, + &hdmi->mode, &hdmi->monspec); } else { - ret = 0; - if (!info) - goto out; - hdmi->monspec.modedb_len = 0; fb_destroy_modedb(hdmi->monspec.modedb); hdmi->monspec.modedb = NULL; - if (lock_fb_info(info)) { - console_lock(); + if (ch && ch->notify) + ch->notify(ch, SH_MOBILE_LCDC_EVENT_DISPLAY_DISCONNECT, + NULL, NULL); - /* HDMI disconnect */ - fb_set_suspend(info, 1); - - console_unlock(); - unlock_fb_info(info); - } + ret = 0; } out: if (ret < 0 && ret != -EAGAIN) hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; - mutex_unlock(&hdmi->mutex); - dev_dbg(hdmi->dev, "%s(%p): end\n", __func__, pdata->lcd_dev); -} - -static int sh_hdmi_notify(struct notifier_block *nb, - unsigned long action, void *data) -{ - struct fb_event *event = data; - struct fb_info *info = event->info; - struct sh_mobile_lcdc_chan *ch = info->par; - struct sh_mobile_lcdc_board_cfg *board_cfg = &ch->cfg.board_cfg; - struct sh_hdmi *hdmi = board_cfg->board_data; - - if (!hdmi || nb != &hdmi->notifier || hdmi->info != info) - return NOTIFY_DONE; - - switch(action) { - case FB_EVENT_FB_REGISTERED: - /* Unneeded, activation taken care by sh_hdmi_display_on() */ - break; - case FB_EVENT_FB_UNREGISTERED: - /* - * We are called from unregister_framebuffer() with the - * info->lock held. This is bad for us, because we can race with - * the scheduled work, which has to call fb_set_suspend(), which - * takes info->lock internally, so, sh_hdmi_edid_work_fn() - * cannot take and hold info->lock for the whole function - * duration. Using an additional lock creates a classical AB-BA - * lock up. Therefore, we have to release the info->lock - * temporarily, synchronise with the work queue and re-acquire - * the info->lock. - */ - unlock_fb_info(info); - mutex_lock(&hdmi->mutex); - hdmi->info = NULL; - mutex_unlock(&hdmi->mutex); - lock_fb_info(info); - return NOTIFY_OK; - } - return NOTIFY_DONE; + dev_dbg(hdmi->dev, "%s(%p): end\n", __func__, hdmi); } static int __init sh_hdmi_probe(struct platform_device *pdev) { struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - struct sh_mobile_lcdc_board_cfg *board_cfg; int irq = platform_get_irq(pdev, 0), ret; struct sh_hdmi *hdmi; long rate; @@ -1255,9 +1127,9 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) return -ENOMEM; } - mutex_init(&hdmi->mutex); - hdmi->dev = &pdev->dev; + hdmi->entity.owner = THIS_MODULE; + hdmi->entity.ops = &sh_hdmi_ops; hdmi->hdmi_clk = clk_get(&pdev->dev, "ick"); if (IS_ERR(hdmi->hdmi_clk)) { @@ -1297,14 +1169,7 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) goto emap; } - platform_set_drvdata(pdev, hdmi); - - /* Set up LCDC callbacks */ - board_cfg = &pdata->lcd_chan->board_cfg; - board_cfg->owner = THIS_MODULE; - board_cfg->board_data = hdmi; - board_cfg->display_on = sh_hdmi_display_on; - board_cfg->display_off = sh_hdmi_display_off; + platform_set_drvdata(pdev, &hdmi->entity); INIT_DELAYED_WORK(&hdmi->edid_work, sh_hdmi_edid_work_fn); @@ -1329,9 +1194,6 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) goto ecodec; } - hdmi->notifier.notifier_call = sh_hdmi_notify; - fb_register_client(&hdmi->notifier); - return 0; ecodec: @@ -1347,7 +1209,6 @@ ereqreg: erate: clk_put(hdmi->hdmi_clk); egetclk: - mutex_destroy(&hdmi->mutex); kfree(hdmi); return ret; @@ -1355,21 +1216,12 @@ egetclk: static int __exit sh_hdmi_remove(struct platform_device *pdev) { - struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; - struct sh_hdmi *hdmi = platform_get_drvdata(pdev); + struct sh_hdmi *hdmi = entity_to_sh_hdmi(platform_get_drvdata(pdev)); struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - struct sh_mobile_lcdc_board_cfg *board_cfg = &pdata->lcd_chan->board_cfg; int irq = platform_get_irq(pdev, 0); snd_soc_unregister_codec(&pdev->dev); - fb_unregister_client(&hdmi->notifier); - - board_cfg->display_on = NULL; - board_cfg->display_off = NULL; - board_cfg->board_data = NULL; - board_cfg->owner = NULL; - /* No new work will be scheduled, wait for running ISR */ free_irq(irq, hdmi); /* Wait for already scheduled work */ @@ -1380,7 +1232,6 @@ static int __exit sh_hdmi_remove(struct platform_device *pdev) clk_put(hdmi->hdmi_clk); iounmap(hdmi->base); release_mem_region(res->start, resource_size(res)); - mutex_destroy(&hdmi->mutex); kfree(hdmi); return 0; diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index aac5b369d73c..7a0b301587f6 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c @@ -8,26 +8,27 @@ * for more details. */ -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/mm.h> +#include <linux/atomic.h> +#include <linux/backlight.h> #include <linux/clk.h> -#include <linux/pm_runtime.h> -#include <linux/platform_device.h> +#include <linux/console.h> #include <linux/dma-mapping.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/init.h> #include <linux/interrupt.h> -#include <linux/videodev2.h> -#include <linux/vmalloc.h> #include <linux/ioctl.h> -#include <linux/slab.h> -#include <linux/console.h> -#include <linux/backlight.h> -#include <linux/gpio.h> +#include <linux/kernel.h> +#include <linux/mm.h> #include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/videodev2.h> +#include <linux/vmalloc.h> + #include <video/sh_mobile_lcdc.h> #include <video/sh_mobile_meram.h> -#include <linux/atomic.h> #include "sh_mobile_lcdcfb.h" @@ -37,6 +38,24 @@ #define MAX_XRES 1920 #define MAX_YRES 1080 +struct sh_mobile_lcdc_priv { + void __iomem *base; + int irq; + atomic_t hw_usecnt; + struct device *dev; + struct clk *dot_clk; + unsigned long lddckr; + struct sh_mobile_lcdc_chan ch[2]; + struct notifier_block notifier; + int started; + int forced_fourcc; /* 2 channel LCDC must share fourcc setting */ + struct sh_mobile_meram_info *meram_dev; +}; + +/* ----------------------------------------------------------------------------- + * Registers access + */ + static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = { [LDDCKPAT1R] = 0x400, [LDDCKPAT2R] = 0x404, @@ -75,38 +94,6 @@ static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = { [LDPMR] = 0x63c, }; -static const struct fb_videomode default_720p = { - .name = "HDMI 720p", - .xres = 1280, - .yres = 720, - - .left_margin = 220, - .right_margin = 110, - .hsync_len = 40, - - .upper_margin = 20, - .lower_margin = 5, - .vsync_len = 5, - - .pixclock = 13468, - .refresh = 60, - .sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_HOR_HIGH_ACT, -}; - -struct sh_mobile_lcdc_priv { - void __iomem *base; - int irq; - atomic_t hw_usecnt; - struct device *dev; - struct clk *dot_clk; - unsigned long lddckr; - struct sh_mobile_lcdc_chan ch[2]; - struct notifier_block notifier; - int started; - int forced_fourcc; /* 2 channel LCDC must share fourcc setting */ - struct sh_mobile_meram_info *meram_dev; -}; - static bool banked(int reg_nr) { switch (reg_nr) { @@ -127,6 +114,11 @@ static bool banked(int reg_nr) return false; } +static int lcdc_chan_is_sublcd(struct sh_mobile_lcdc_chan *chan) +{ + return chan->cfg->chan == LCDC_CHAN_SUBLCD; +} + static void lcdc_write_chan(struct sh_mobile_lcdc_chan *chan, int reg_nr, unsigned long data) { @@ -169,11 +161,72 @@ static void lcdc_wait_bit(struct sh_mobile_lcdc_priv *priv, cpu_relax(); } -static int lcdc_chan_is_sublcd(struct sh_mobile_lcdc_chan *chan) +/* ----------------------------------------------------------------------------- + * Clock management + */ + +static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv) +{ + if (atomic_inc_and_test(&priv->hw_usecnt)) { + if (priv->dot_clk) + clk_enable(priv->dot_clk); + pm_runtime_get_sync(priv->dev); + if (priv->meram_dev && priv->meram_dev->pdev) + pm_runtime_get_sync(&priv->meram_dev->pdev->dev); + } +} + +static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv) { - return chan->cfg.chan == LCDC_CHAN_SUBLCD; + if (atomic_sub_return(1, &priv->hw_usecnt) == -1) { + if (priv->meram_dev && priv->meram_dev->pdev) + pm_runtime_put_sync(&priv->meram_dev->pdev->dev); + pm_runtime_put(priv->dev); + if (priv->dot_clk) + clk_disable(priv->dot_clk); + } } +static int sh_mobile_lcdc_setup_clocks(struct sh_mobile_lcdc_priv *priv, + int clock_source) +{ + struct clk *clk; + char *str; + + switch (clock_source) { + case LCDC_CLK_BUS: + str = "bus_clk"; + priv->lddckr = LDDCKR_ICKSEL_BUS; + break; + case LCDC_CLK_PERIPHERAL: + str = "peripheral_clk"; + priv->lddckr = LDDCKR_ICKSEL_MIPI; + break; + case LCDC_CLK_EXTERNAL: + str = NULL; + priv->lddckr = LDDCKR_ICKSEL_HDMI; + break; + default: + return -EINVAL; + } + + if (str == NULL) |