diff options
Diffstat (limited to 'drivers/video')
61 files changed, 7804 insertions, 882 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index bed84b01ccd6..2c301f8441e9 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -2199,7 +2199,7 @@ config FB_XILINX config FB_GOLDFISH tristate "Goldfish Framebuffer" - depends on FB + depends on FB && HAS_DMA select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT @@ -2453,6 +2453,23 @@ config FB_HYPERV help This framebuffer driver supports Microsoft Hyper-V Synthetic Video. +config FB_SIMPLE + bool "Simple framebuffer support" + depends on (FB = y) && OF + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + Say Y if you want support for a simple frame-buffer. + + This driver assumes that the display hardware has been initialized + before the kernel boots, and the kernel will simply render to the + pre-allocated frame buffer surface. + + Configuration re: surface address, size, and format must be provided + through device tree, or potentially plain old platform data in the + future. + source "drivers/video/omap/Kconfig" source "drivers/video/omap2/Kconfig" source "drivers/video/exynos/Kconfig" diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 7234e4a959e8..e8bae8dd4804 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -166,6 +166,7 @@ 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 +obj-$(CONFIG_FB_SIMPLE) += simplefb.o # the test framebuffer is last obj-$(CONFIG_FB_VIRTUAL) += vfb.o diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c index 540909de6247..effdb373b8db 100644 --- a/drivers/video/atmel_lcdfb.c +++ b/drivers/video/atmel_lcdfb.c @@ -223,8 +223,14 @@ static void init_backlight(struct atmel_lcdfb_info *sinfo) static void exit_backlight(struct atmel_lcdfb_info *sinfo) { - if (sinfo->backlight) - backlight_device_unregister(sinfo->backlight); + if (!sinfo->backlight) + return; + + if (sinfo->backlight->ops) { + sinfo->backlight->props.power = FB_BLANK_POWERDOWN; + sinfo->backlight->ops->update_status(sinfo->backlight); + } + backlight_device_unregister(sinfo->backlight); } #else @@ -461,8 +467,11 @@ static int atmel_lcdfb_check_var(struct fb_var_screeninfo *var, if (info->fix.smem_len) { unsigned int smem_len = (var->xres_virtual * var->yres_virtual * ((var->bits_per_pixel + 7) / 8)); - if (smem_len > info->fix.smem_len) + if (smem_len > info->fix.smem_len) { + dev_err(dev, "Frame buffer is too small (%u) for screen size (need at least %u)\n", + info->fix.smem_len, smem_len); return -EINVAL; + } } /* Saturate vertical and horizontal timings at maximum values */ diff --git a/drivers/video/console/Makefile b/drivers/video/console/Makefile index a862e9173ebe..48da25c96cd3 100644 --- a/drivers/video/console/Makefile +++ b/drivers/video/console/Makefile @@ -18,6 +18,8 @@ font-objs-$(CONFIG_FONT_MINI_4x6) += font_mini_4x6.o font-objs += $(font-objs-y) +obj-$(CONFIG_FONTS) += font.o + # Each configuration option enables a list of files. obj-$(CONFIG_DUMMY_CONSOLE) += dummycon.o diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 9217be3d8f3e..36e1fe21b9b5 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -1883,7 +1883,7 @@ static int ofonly __read_mostly; * * NOTE: Needed to maintain backwards compatibility */ -int fb_get_options(char *name, char **option) +int fb_get_options(const char *name, char **option) { char *opt, *options = NULL; int retval = 0; diff --git a/drivers/video/mmp/hw/mmp_ctrl.c b/drivers/video/mmp/hw/mmp_ctrl.c index c46bf5a93d38..75dca19bf214 100644 --- a/drivers/video/mmp/hw/mmp_ctrl.c +++ b/drivers/video/mmp/hw/mmp_ctrl.c @@ -165,9 +165,9 @@ static void overlay_set_win(struct mmp_overlay *overlay, struct mmp_win *win) static void dmafetch_onoff(struct mmp_overlay *overlay, int on) { - u32 mask = overlay_is_vid(overlay) ? CFG_GRA_ENA_MASK : - CFG_DMA_ENA_MASK; - u32 enable = overlay_is_vid(overlay) ? CFG_GRA_ENA(1) : CFG_DMA_ENA(1); + u32 mask = overlay_is_vid(overlay) ? CFG_DMA_ENA_MASK : + CFG_GRA_ENA_MASK; + u32 enable = overlay_is_vid(overlay) ? CFG_DMA_ENA(1) : CFG_GRA_ENA(1); u32 tmp; struct mmp_path *path = overlay->path; @@ -238,7 +238,7 @@ static int overlay_set_addr(struct mmp_overlay *overlay, struct mmp_addr *addr) struct lcd_regs *regs = path_regs(overlay->path); /* FIXME: assert addr supported */ - memcpy(&overlay->addr, addr, sizeof(struct mmp_win)); + memcpy(&overlay->addr, addr, sizeof(struct mmp_addr)); writel(addr->phys[0], ®s->g_0); return overlay->addr.phys[0]; diff --git a/drivers/video/omap2/Kconfig b/drivers/video/omap2/Kconfig index b07b2b042e7e..56cad0f5386c 100644 --- a/drivers/video/omap2/Kconfig +++ b/drivers/video/omap2/Kconfig @@ -6,5 +6,6 @@ if ARCH_OMAP2PLUS source "drivers/video/omap2/dss/Kconfig" source "drivers/video/omap2/omapfb/Kconfig" source "drivers/video/omap2/displays/Kconfig" +source "drivers/video/omap2/displays-new/Kconfig" endif diff --git a/drivers/video/omap2/Makefile b/drivers/video/omap2/Makefile index 296e5c5281c5..86873c2fbb27 100644 --- a/drivers/video/omap2/Makefile +++ b/drivers/video/omap2/Makefile @@ -2,4 +2,5 @@ obj-$(CONFIG_OMAP2_VRFB) += vrfb.o obj-$(CONFIG_OMAP2_DSS) += dss/ obj-y += displays/ +obj-y += displays-new/ obj-$(CONFIG_FB_OMAP2) += omapfb/ diff --git a/drivers/video/omap2/displays-new/Kconfig b/drivers/video/omap2/displays-new/Kconfig new file mode 100644 index 000000000000..6c90885b0940 --- /dev/null +++ b/drivers/video/omap2/displays-new/Kconfig @@ -0,0 +1,73 @@ +menu "OMAP Display Device Drivers (new device model)" + depends on OMAP2_DSS + +config DISPLAY_ENCODER_TFP410 + tristate "TFP410 DPI to DVI Encoder" + help + Driver for TFP410 DPI to DVI encoder. + +config DISPLAY_ENCODER_TPD12S015 + tristate "TPD12S015 HDMI ESD protection and level shifter" + help + Driver for TPD12S015, which offers HDMI ESD protection and level + shifting. + +config DISPLAY_CONNECTOR_DVI + tristate "DVI Connector" + depends on I2C + help + Driver for a generic DVI connector. + +config DISPLAY_CONNECTOR_HDMI + tristate "HDMI Connector" + help + Driver for a generic HDMI connector. + +config DISPLAY_CONNECTOR_ANALOG_TV + tristate "Analog TV Connector" + help + Driver for a generic analog TV connector. + +config DISPLAY_PANEL_DPI + tristate "Generic DPI panel" + help + Driver for generic DPI panels. + +config DISPLAY_PANEL_DSI_CM + tristate "Generic DSI Command Mode Panel" + help + Driver for generic DSI command mode panels. + +config DISPLAY_PANEL_SONY_ACX565AKM + tristate "ACX565AKM Panel" + depends on SPI && BACKLIGHT_CLASS_DEVICE + help + This is the LCD panel used on Nokia N900 + +config DISPLAY_PANEL_LGPHILIPS_LB035Q02 + tristate "LG.Philips LB035Q02 LCD Panel" + depends on SPI + help + LCD Panel used on the Gumstix Overo Palo35 + +config DISPLAY_PANEL_SHARP_LS037V7DW01 + tristate "Sharp LS037V7DW01 LCD Panel" + depends on BACKLIGHT_CLASS_DEVICE + help + LCD Panel used in TI's SDP3430 and EVM boards + +config DISPLAY_PANEL_TPO_TD043MTEA1 + tristate "TPO TD043MTEA1 LCD Panel" + depends on SPI + help + LCD Panel used in OMAP3 Pandora + +config DISPLAY_PANEL_NEC_NL8048HL11 + tristate "NEC NL8048HL11 Panel" + depends on SPI + depends on BACKLIGHT_CLASS_DEVICE + help + This NEC NL8048HL11 panel is TFT LCD used in the + Zoom2/3/3630 sdp boards. + +endmenu diff --git a/drivers/video/omap2/displays-new/Makefile b/drivers/video/omap2/displays-new/Makefile new file mode 100644 index 000000000000..5aeb11b8fcd5 --- /dev/null +++ b/drivers/video/omap2/displays-new/Makefile @@ -0,0 +1,12 @@ +obj-$(CONFIG_DISPLAY_ENCODER_TFP410) += encoder-tfp410.o +obj-$(CONFIG_DISPLAY_ENCODER_TPD12S015) += encoder-tpd12s015.o +obj-$(CONFIG_DISPLAY_CONNECTOR_DVI) += connector-dvi.o +obj-$(CONFIG_DISPLAY_CONNECTOR_HDMI) += connector-hdmi.o +obj-$(CONFIG_DISPLAY_CONNECTOR_ANALOG_TV) += connector-analog-tv.o +obj-$(CONFIG_DISPLAY_PANEL_DPI) += panel-dpi.o +obj-$(CONFIG_DISPLAY_PANEL_DSI_CM) += panel-dsi-cm.o +obj-$(CONFIG_DISPLAY_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o +obj-$(CONFIG_DISPLAY_PANEL_LGPHILIPS_LB035Q02) += panel-lgphilips-lb035q02.o +obj-$(CONFIG_DISPLAY_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o +obj-$(CONFIG_DISPLAY_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o +obj-$(CONFIG_DISPLAY_PANEL_NEC_NL8048HL11) += panel-nec-nl8048hl11.o diff --git a/drivers/video/omap2/displays-new/connector-analog-tv.c b/drivers/video/omap2/displays-new/connector-analog-tv.c new file mode 100644 index 000000000000..5338f362293b --- /dev/null +++ b/drivers/video/omap2/displays-new/connector-analog-tv.c @@ -0,0 +1,265 @@ +/* + * Analog TV Connector driver + * + * Copyright (C) 2013 Texas Instruments + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <video/omapdss.h> +#include <video/omap-panel-data.h> + +struct panel_drv_data { + struct omap_dss_device dssdev; + struct omap_dss_device *in; + + struct device *dev; + + struct omap_video_timings timings; + + enum omap_dss_venc_type connector_type; + bool invert_polarity; +}; + +#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev) + +static int tvc_connect(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + dev_dbg(ddata->dev, "connect\n"); + + if (omapdss_device_is_connected(dssdev)) + return 0; + + r = in->ops.atv->connect(in, dssdev); + if (r) + return r; + + return 0; +} + +static void tvc_disconnect(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + dev_dbg(ddata->dev, "disconnect\n"); + + if (!omapdss_device_is_connected(dssdev)) + return; + + in->ops.atv->disconnect(in, dssdev); +} + +static int tvc_enable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + dev_dbg(ddata->dev, "enable\n"); + + if (!omapdss_device_is_connected(dssdev)) + return -ENODEV; + + if (omapdss_device_is_enabled(dssdev)) + return 0; + + in->ops.atv->set_timings(in, &ddata->timings); + + in->ops.atv->set_type(in, ddata->connector_type); + in->ops.atv->invert_vid_out_polarity(in, ddata->invert_polarity); + + r = in->ops.atv->enable(in); + if (r) + return r; + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + return r; +} + +static void tvc_disable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + dev_dbg(ddata->dev, "disable\n"); + + if (!omapdss_device_is_enabled(dssdev)) + return; + + in->ops.atv->disable(in); + + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; +} + +static void tvc_set_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + ddata->timings = *timings; + dssdev->panel.timings = *timings; + + in->ops.atv->set_timings(in, timings); +} + +static void tvc_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + + *timings = ddata->timings; +} + +static int tvc_check_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + return in->ops.atv->check_timings(in, timings); +} + +static u32 tvc_get_wss(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + return in->ops.atv->get_wss(in); +} + +static int tvc_set_wss(struct omap_dss_device *dssdev, u32 wss) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + return in->ops.atv->set_wss(in, wss); +} + +static struct omap_dss_driver tvc_driver = { + .connect = tvc_connect, + .disconnect = tvc_disconnect, + + .enable = tvc_enable, + .disable = tvc_disable, + + .set_timings = tvc_set_timings, + .get_timings = tvc_get_timings, + .check_timings = tvc_check_timings, + + .get_resolution = omapdss_default_get_resolution, + + .get_wss = tvc_get_wss, + .set_wss = tvc_set_wss, +}; + +static int tvc_probe_pdata(struct platform_device *pdev) +{ + struct panel_drv_data *ddata = platform_get_drvdata(pdev); + struct connector_atv_platform_data *pdata; + struct omap_dss_device *in, *dssdev; + + pdata = dev_get_platdata(&pdev->dev); + + in = omap_dss_find_output(pdata->source); + if (in == NULL) { + dev_err(&pdev->dev, "Failed to find video source\n"); + return -ENODEV; + } + + ddata->in = in; + + ddata->connector_type = pdata->connector_type; + ddata->invert_polarity = ddata->invert_polarity; + + dssdev = &ddata->dssdev; + dssdev->name = pdata->name; + + return 0; +} + +static int tvc_probe(struct platform_device *pdev) +{ + struct panel_drv_data *ddata; + struct omap_dss_device *dssdev; + int r; + + ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + + platform_set_drvdata(pdev, ddata); + ddata->dev = &pdev->dev; + + if (dev_get_platdata(&pdev->dev)) { + r = tvc_probe_pdata(pdev); + if (r) + return r; + } else { + return -ENODEV; + } + + ddata->timings = omap_dss_pal_timings; + + dssdev = &ddata->dssdev; + dssdev->driver = &tvc_driver; + dssdev->dev = &pdev->dev; + dssdev->type = OMAP_DISPLAY_TYPE_VENC; + dssdev->owner = THIS_MODULE; + dssdev->panel.timings = omap_dss_pal_timings; + + r = omapdss_register_display(dssdev); + if (r) { + dev_err(&pdev->dev, "Failed to register panel\n"); + goto err_reg; + } + + return 0; +err_reg: + omap_dss_put_device(ddata->in); + return r; +} + +static int __exit tvc_remove(struct platform_device *pdev) +{ + struct panel_drv_data *ddata = platform_get_drvdata(pdev); + struct omap_dss_device *dssdev = &ddata->dssdev; + struct omap_dss_device *in = ddata->in; + + omapdss_unregister_display(&ddata->dssdev); + + tvc_disable(dssdev); + tvc_disconnect(dssdev); + + omap_dss_put_device(in); + + return 0; +} + +static struct platform_driver tvc_connector_driver = { + .probe = tvc_probe, + .remove = __exit_p(tvc_remove), + .driver = { + .name = "connector-analog-tv", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(tvc_connector_driver); + +MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); +MODULE_DESCRIPTION("Analog TV Connector driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays-new/connector-dvi.c b/drivers/video/omap2/displays-new/connector-dvi.c new file mode 100644 index 000000000000..bc5f8ceda371 --- /dev/null +++ b/drivers/video/omap2/displays-new/connector-dvi.c @@ -0,0 +1,351 @@ +/* + * Generic DVI Connector driver + * + * Copyright (C) 2013 Texas Instruments + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include <drm/drm_edid.h> + +#include <video/omapdss.h> +#include <video/omap-panel-data.h> + +static const struct omap_video_timings dvic_default_timings = { + .x_res = 640, + .y_res = 480, + + .pixel_clock = 23500, + + .hfp = 48, + .hsw = 32, + .hbp = 80, + + .vfp = 3, + .vsw = 4, + .vbp = 7, + + .vsync_level = OMAPDSS_SIG_ACTIVE_HIGH, + .hsync_level = OMAPDSS_SIG_ACTIVE_HIGH, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, +}; + +struct panel_drv_data { + struct omap_dss_device dssdev; + struct omap_dss_device *in; + + struct omap_video_timings timings; + + struct i2c_adapter *i2c_adapter; +}; + +#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev) + +static int dvic_connect(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + if (omapdss_device_is_connected(dssdev)) + return 0; + + r = in->ops.dvi->connect(in, dssdev); + if (r) + return r; + + return 0; +} + +static void dvic_disconnect(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + if (!omapdss_device_is_connected(dssdev)) + return; + + in->ops.dvi->disconnect(in, dssdev); +} + +static int dvic_enable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + if (!omapdss_device_is_connected(dssdev)) + return -ENODEV; + + if (omapdss_device_is_enabled(dssdev)) + return 0; + + in->ops.dvi->set_timings(in, &ddata->timings); + + r = in->ops.dvi->enable(in); + if (r) + return r; + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + return 0; +} + +static void dvic_disable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + if (!omapdss_device_is_enabled(dssdev)) + return; + + in->ops.dvi->disable(in); + + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; +} + +static void dvic_set_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + ddata->timings = *timings; + dssdev->panel.timings = *timings; + + in->ops.dvi->set_timings(in, timings); +} + +static void dvic_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + + *timings = ddata->timings; +} + +static int dvic_check_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + return in->ops.dvi->check_timings(in, timings); +} + +static int dvic_ddc_read(struct i2c_adapter *adapter, + unsigned char *buf, u16 count, u8 offset) +{ + int r, retries; + + for (retries = 3; retries > 0; retries--) { + struct i2c_msg msgs[] = { + { + .addr = DDC_ADDR, + .flags = 0, + .len = 1, + .buf = &offset, + }, { + .addr = DDC_ADDR, + .flags = I2C_M_RD, + .len = count, + .buf = buf, + } + }; + + r = i2c_transfer(adapter, msgs, 2); + if (r == 2) + return 0; + + if (r != -EAGAIN) + break; + } + + return r < 0 ? r : -EIO; +} + +static int dvic_read_edid(struct omap_dss_device *dssdev, + u8 *edid, int len) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + int r, l, bytes_read; + + if (!ddata->i2c_adapter) + return -ENODEV; + + l = min(EDID_LENGTH, len); + r = dvic_ddc_read(ddata->i2c_adapter, edid, l, 0); + if (r) + return r; + + bytes_read = l; + + /* if there are extensions, read second block */ + if (len > EDID_LENGTH && edid[0x7e] > 0) { + l = min(EDID_LENGTH, len - EDID_LENGTH); + + r = dvic_ddc_read(ddata->i2c_adapter, edid + EDID_LENGTH, + l, EDID_LENGTH); + if (r) + return r; + + bytes_read += l; + } + + return bytes_read; +} + +static bool dvic_detect(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + unsigned char out; + int r; + + if (!ddata->i2c_adapter) + return true; + + r = dvic_ddc_read(ddata->i2c_adapter, &out, 1, 0); + + return r == 0; +} + +static struct omap_dss_driver dvic_driver = { + .connect = dvic_connect, + .disconnect = dvic_disconnect, + + .enable = dvic_enable, + .disable = dvic_disable, + + .set_timings = dvic_set_timings, + .get_timings = dvic_get_timings, + .check_timings = dvic_check_timings, + + .get_resolution = omapdss_default_get_resolution, + + .read_edid = dvic_read_edid, + .detect = dvic_detect, +}; + +static int dvic_probe_pdata(struct platform_device *pdev) +{ + struct panel_drv_data *ddata = platform_get_drvdata(pdev); + struct connector_dvi_platform_data *pdata; + struct omap_dss_device *in, *dssdev; + int i2c_bus_num; + + pdata = dev_get_platdata(&pdev->dev); + i2c_bus_num = pdata->i2c_bus_num; + + if (i2c_bus_num != -1) { + struct i2c_adapter *adapter; + + adapter = i2c_get_adapter(i2c_bus_num); + if (!adapter) { + dev_err(&pdev->dev, + "Failed to get I2C adapter, bus %d\n", + i2c_bus_num); + return -EPROBE_DEFER; + } + + ddata->i2c_adapter = adapter; + } + + in = omap_dss_find_output(pdata->source); + if (in == NULL) { + dev_err(&pdev->dev, "Failed to find video source\n"); + return -ENODEV; + } + + ddata->in = in; + + dssdev = &ddata->dssdev; + dssdev->name = pdata->name; + + return 0; +} + +static int dvic_probe(struct platform_device *pdev) +{ + struct panel_drv_data *ddata; + struct omap_dss_device *dssdev; + int r; + + ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + + platform_set_drvdata(pdev, ddata); + + if (dev_get_platdata(&pdev->dev)) { + r = dvic_probe_pdata(pdev); + if (r) + return r; + } else { + return -ENODEV; + } + + ddata->timings = dvic_default_timings; + + dssdev = &ddata->dssdev; + dssdev->driver = &dvic_driver; + dssdev->dev = &pdev->dev; + dssdev->type = OMAP_DISPLAY_TYPE_DVI; + dssdev->owner = THIS_MODULE; + dssdev->panel.timings = dvic_default_timings; + + r = omapdss_register_display(dssdev); + if (r) { + dev_err(&pdev->dev, "Failed to register panel\n"); + goto err_reg; + } + + return 0; + +err_reg: + omap_dss_put_device(ddata->in); + return r; +} + +static int __exit dvic_remove(struct platform_device *pdev) +{ + struct panel_drv_data *ddata = platform_get_drvdata(pdev); + struct omap_dss_device *dssdev = &ddata->dssdev; + struct omap_dss_device *in = ddata->in; + + omapdss_unregister_display(&ddata->dssdev); + + dvic_disable(dssdev); + dvic_disconnect(dssdev); + + omap_dss_put_device(in); + + if (ddata->i2c_adapter) + i2c_put_adapter(ddata->i2c_adapter); + + return 0; +} + +static struct platform_driver dvi_connector_driver = { + .probe = dvic_probe, + .remove = __exit_p(dvic_remove), + .driver = { + .name = "connector-dvi", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(dvi_connector_driver); + +MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); +MODULE_DESCRIPTION("Generic DVI Connector driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays-new/connector-hdmi.c b/drivers/video/omap2/displays-new/connector-hdmi.c new file mode 100644 index 000000000000..c5826716d6ab --- /dev/null +++ b/drivers/video/omap2/displays-new/connector-hdmi.c @@ -0,0 +1,375 @@ +/* + * HDMI Connector driver + * + * Copyright (C) 2013 Texas Instruments + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <drm/drm_edid.h> + +#include <video/omapdss.h> +#include <video/omap-panel-data.h> + +static const struct omap_video_timings hdmic_default_timings = { + .x_res = 640, + .y_res = 480, + .pixel_clock = 25175, + .hsw = 96, + .hfp = 16, + .hbp = 48, + .vsw = 2, + .vfp = 11, + .vbp = 31, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + + .interlace = false, +}; + +struct panel_drv_data { + struct omap_dss_device dssdev; + struct omap_dss_device *in; + + struct device *dev; + + struct omap_video_timings timings; +}; + +#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev) + +static int hdmic_connect(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + dev_dbg(ddata->dev, "connect\n"); + + if (omapdss_device_is_connected(dssdev)) + return 0; + + r = in->ops.hdmi->connect(in, dssdev); + if (r) + return r; + + return 0; +} + +static void hdmic_disconnect(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + dev_dbg(ddata->dev, "disconnect\n"); + + if (!omapdss_device_is_connected(dssdev)) + return; + + in->ops.hdmi->disconnect(in, dssdev); +} + +static int hdmic_enable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + dev_dbg(ddata->dev, "enable\n"); + + if (!omapdss_device_is_connected(dssdev)) + return -ENODEV; + + if (omapdss_device_is_enabled(dssdev)) + return 0; + + in->ops.hdmi->set_timings(in, &ddata->timings); + + r = in->ops.hdmi->enable(in); + if (r) + return r; + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + return r; +} + +static void hdmic_disable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + dev_dbg(ddata->dev, "disable\n"); + + if (!omapdss_device_is_enabled(dssdev)) + return; + + in->ops.hdmi->disable(in); + + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; +} + +static void hdmic_set_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + ddata->timings = *timings; + dssdev->panel.timings = *timings; + + in->ops.hdmi->set_timings(in, timings); +} + +static void hdmic_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + + *timings = ddata->timings; +} + +static int hdmic_check_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + return in->ops.hdmi->check_timings(in, timings); +} + +static int hdmic_read_edid(struct omap_dss_device *dssdev, + u8 *edid, int len) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + return in->ops.hdmi->read_edid(in, edid, len); +} + +static bool hdmic_detect(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + return in->ops.hdmi->detect(in); +} + +static int hdmic_audio_enable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + /* enable audio only if the display is active */ + if (!omapdss_device_is_enabled(dssdev)) + return -EPERM; + + r = in->ops.hdmi->audio_enable(in); + if (r) + return r; + + dssdev->audio_state = OMAP_DSS_AUDIO_ENABLED; + + return 0; +} + +static void hdmic_audio_disable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + in->ops.hdmi->audio_disable(in); + + dssdev->audio_state = OMAP_DSS_AUDIO_DISABLED; +} + +static int hdmic_audio_start(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + /* + * No need to check the panel state. It was checked when trasitioning + * to AUDIO_ENABLED. + */ + if (dssdev->audio_state != OMAP_DSS_AUDIO_ENABLED) + return -EPERM; + + r = in->ops.hdmi->audio_start(in); + if (r) + return r; + + dssdev->audio_state = OMAP_DSS_AUDIO_PLAYING; + + return 0; +} + +static void hdmic_audio_stop(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + in->ops.hdmi->audio_stop(in); + + dssdev->audio_state = OMAP_DSS_AUDIO_ENABLED; +} + +static bool hdmic_audio_supported(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + if (!omapdss_device_is_enabled(dssdev)) + return false; + + return in->ops.hdmi->audio_supported(in); +} + +static int hdmic_audio_config(struct omap_dss_device *dssdev, + struct omap_dss_audio *audio) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + /* config audio only if the display is active */ + if (!omapdss_device_is_enabled(dssdev)) + return -EPERM; + + r = in->ops.hdmi->audio_config(in, audio); + if (r) + return r; + + dssdev->audio_state = OMAP_DSS_AUDIO_CONFIGURED; + + return 0; +} + +static struct omap_dss_driver hdmic_driver = { + .connect = hdmic_connect, + .disconnect = hdmic_disconnect, + + .enable = hdmic_enable, + .disable = hdmic_disable, + + .set_timings = hdmic_set_timings, + .get_timings = hdmic_get_timings, + .check_timings = hdmic_check_timings, + + .get_resolution = omapdss_default_get_resolution, + + .read_edid = hdmic_read_edid, + .detect = hdmic_detect, + + .audio_enable = hdmic_audio_enable, + .audio_disable = hdmic_audio_disable, + .audio_start = hdmic_audio_start, + .audio_stop = hdmic_audio_stop, + .audio_supported = hdmic_audio_supported, + .audio_config = hdmic_audio_config, +}; + +static int hdmic_probe_pdata(struct platform_device *pdev) +{ + struct panel_drv_data *ddata = platform_get_drvdata(pdev); + struct connector_hdmi_platform_data *pdata; + struct omap_dss_device *in, *dssdev; + + pdata = dev_get_platdata(&pdev->dev); + + in = omap_dss_find_output(pdata->source); + if (in == NULL) { + dev_err(&pdev->dev, "Failed to find video source\n"); + return -ENODEV; + } + + ddata->in = in; + + dssdev = &ddata->dssdev; + dssdev->name = pdata->name; + + return 0; +} + +static int hdmic_probe(struct platform_device *pdev) +{ + struct panel_drv_data *ddata; + struct omap_dss_device *dssdev; + int r; + + ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + + platform_set_drvdata(pdev, ddata); + ddata->dev = &pdev->dev; + + if (dev_get_platdata(&pdev->dev)) { + r = hdmic_probe_pdata(pdev); + if (r) + return r; + } else { + return -ENODEV; + } + + ddata->timings = hdmic_default_timings; + + dssdev = &ddata->dssdev; + dssdev->driver = &hdmic_driver; + dssdev->dev = &pdev->dev; + dssdev->type = OMAP_DISPLAY_TYPE_HDMI; + dssdev->owner = THIS_MODULE; + dssdev->panel.timings = hdmic_default_timings; + + r = omapdss_register_display(dssdev); + if (r) { + dev_err(&pdev->dev, "Failed to register panel\n"); + goto err_reg; + } + + return 0; +err_reg: + omap_dss_put_device(ddata->in); + return r; +} + +static int __exit hdmic_remove(struct platform_device *pdev) +{ + struct panel_drv_data *ddata = platform_get_drvdata(pdev); + struct omap_dss_device *dssdev = &ddata->dssdev; + struct omap_dss_device *in = ddata->in; + + omapdss_unregister_display(&ddata->dssdev); + + hdmic_disable(dssdev); + hdmic_disconnect(dssdev); + + omap_dss_put_device(in); + + return 0; +} + +static struct platform_driver hdmi_connector_driver = { + .probe = hdmic_probe, + .remove = __exit_p(hdmic_remove), + .driver = { + .name = "connector-hdmi", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(hdmi_connector_driver); + +MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); +MODULE_DESCRIPTION("HDMI Connector driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays-new/encoder-tfp410.c b/drivers/video/omap2/displays-new/encoder-tfp410.c new file mode 100644 index 000000000000..a04f65856d6b --- /dev/null +++ b/drivers/video/omap2/displays-new/encoder-tfp410.c @@ -0,0 +1,267 @@ +/* + * TFP410 DPI-to-DVI encoder driver + * + * Copyright (C) 2013 Texas Instruments + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <linux/gpio.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include <video/omapdss.h> +#include <video/omap-panel-data.h> + +struct panel_drv_data { + struct omap_dss_device dssdev; + struct omap_dss_device *in; + + int pd_gpio; + int data_lines; + + struct omap_video_timings timings; +}; + +#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev) + +static int tfp410_connect(struct omap_dss_device *dssdev, + struct omap_dss_device *dst) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + if (omapdss_device_is_connected(dssdev)) + return -EBUSY; + + r = in->ops.dpi->connect(in, dssdev); + if (r) + return r; + + dst->output = dssdev; + dssdev->device = dst; + + return 0; +} + +static void tfp410_disconnect(struct omap_dss_device *dssdev, + struct omap_dss_device *dst) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + WARN_ON(!omapdss_device_is_connected(dssdev)); + if (!omapdss_device_is_connected(dssdev)) + return; + + WARN_ON(dst != dssdev->device); + if (dst != dssdev->device) + return; + + dst->output = NULL; + dssdev->device = NULL; + + in->ops.dpi->disconnect(in, &ddata->dssdev); +} + +static int tfp410_enable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + if (!omapdss_device_is_connected(dssdev)) + return -ENODEV; + + if (omapdss_device_is_enabled(dssdev)) + return 0; + + in->ops.dpi->set_timings(in, &ddata->timings); + in->ops.dpi->set_data_lines(in, ddata->data_lines); + + r = in->ops.dpi->enable(in); + if (r) + return r; + + if (gpio_is_valid(ddata->pd_gpio)) + gpio_set_value_cansleep(ddata->pd_gpio, 1); + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + return 0; +} + +static void tfp410_disable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + if (!omapdss_device_is_enabled(dssdev)) + return; + + if (gpio_is_valid(ddata->pd_gpio)) + gpio_set_value_cansleep(ddata->pd_gpio, 0); + + in->ops.dpi->disable(in); + + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; +} + +static void tfp410_set_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + ddata->timings = *timings; + dssdev->panel.timings = *timings; + + in->ops.dpi->set_timings(in, timings); +} + +static void tfp410_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + + *timings = ddata->timings; +} + +static int tfp410_check_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + return in->ops.dpi->check_timings(in, timings); +} + +static const struct omapdss_dvi_ops tfp410_dvi_ops = { + .connect = tfp410_connect, + .disconnect = tfp410_disconnect, + + .enable = tfp410_enable, + .disable = tfp410_disable, + + .check_timings = tfp410_check_timings, + .set_timings = tfp410_set_timings, + .get_timings = tfp410_get_timings, +}; + +static int tfp410_probe_pdata(struct platform_device *pdev) +{ + struct panel_drv_data *ddata = platform_get_drvdata(pdev); + struct encoder_tfp410_platform_data *pdata; + struct omap_dss_device *dssdev, *in; + + pdata = dev_get_platdata(&pdev->dev); + + ddata->pd_gpio = pdata->power_down_gpio; + + ddata->data_lines = pdata->data_lines; + + in = omap_dss_find_output(pdata->source); + if (in == NULL) { + dev_err(&pdev->dev, "Failed to find video source\n"); + return -ENODEV; + } + + ddata->in = in; + + dssdev = &ddata->dssdev; + dssdev->name = pdata->name; + + return 0; +} + +static int tfp410_probe(struct platform_device *pdev) +{ + struct panel_drv_data *ddata; + struct omap_dss_device *dssdev; + int r; + + ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + + platform_set_drvdata(pdev, ddata); + + if (dev_get_platdata(&pdev->dev)) { + r = tfp410_probe_pdata(pdev); + if (r) + return r; + } else { + return -ENODEV; + } + + if (gpio_is_valid(ddata->pd_gpio)) { + r = devm_gpio_request_one(&pdev->dev, ddata->pd_gpio, + GPIOF_OUT_INIT_LOW, "tfp410 PD"); + if (r) { + dev_err(&pdev->dev, "Failed to request PD GPIO %d\n", + ddata->pd_gpio); + goto err_gpio; + } + } + + dssdev = &ddata->dssdev; + dssdev->ops.dvi = &tfp410_dvi_ops; + dssdev->dev = &pdev->dev; + dssdev->type = OMAP_DISPLAY_TYPE_DPI; + dssdev->output_type = OMAP_DISPLAY_TYPE_DVI; + dssdev->owner = THIS_MODULE; + dssdev->phy.dpi.data_lines = ddata->data_lines; + + r = omapdss_register_output(dssdev); + if (r) { + dev_err(&pdev->dev, "Failed to register output\n"); + goto err_reg; + } + + return 0; +err_reg: +err_gpio: + omap_dss_put_device(ddata->in); + return r; +} + +static int __exit tfp410_remove(struct platform_device *pdev) +{ + struct panel_drv_data *ddata = platform_get_drvdata(pdev); + struct omap_dss_device *dssdev = &ddata->dssdev; + struct omap_dss_device *in = ddata->in; + + omapdss_unregister_output(&ddata->dssdev); + + WARN_ON(omapdss_device_is_enabled(dssdev)); + if (omapdss_device_is_enabled(dssdev)) + tfp410_disable(dssdev); + + WARN_ON(omapdss_device_is_connected(dssdev)); + if (omapdss_device_is_connected(dssdev)) + tfp410_disconnect(dssdev, dssdev->device); + + omap_dss_put_device(in); + + return 0; +} + +static struct platform_driver tfp410_driver = { + .probe = tfp410_probe, + .remove = __exit_p(tfp410_remove), + .driver = { + .name = "tfp410", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(tfp410_driver); + +MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); +MODULE_DESCRIPTION("TFP410 DPI to DVI encoder driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays-new/encoder-tpd12s015.c b/drivers/video/omap2/displays-new/encoder-tpd12s015.c new file mode 100644 index 000000000000..ce0e010026cb --- /dev/null +++ b/drivers/video/omap2/displays-new/encoder-tpd12s015.c @@ -0,0 +1,395 @@ +/* + * TPD12S015 HDMI ESD protection & level shifter chip driver + * + * Copyright (C) 2013 Texas Instruments + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/gpio.h> +#include <linux/platform_device.h> + +#include <video/omapdss.h> +#include <video/omap-panel-data.h> + +struct panel_drv_data { + struct omap_dss_device dssdev; + struct omap_dss_device *in; + + int ct_cp_hpd_gpio; + int ls_oe_gpio; + int hpd_gpio; + + struct omap_video_timings timings; + + struct completion hpd_completion; +}; + +#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev) + +static irqreturn_t tpd_hpd_irq_handler(int irq, void *data) +{ + struct panel_drv_data *ddata = data; + bool hpd; + + hpd = gpio_get_value_cansleep(ddata->hpd_gpio); + + dev_dbg(ddata->dssdev.dev, "hpd %d\n", hpd); + + if (gpio_is_valid(ddata->ls_oe_gpio)) { + if (hpd) + gpio_set_value_cansleep(ddata->ls_oe_gpio, 1); + else + gpio_set_value_cansleep(ddata->ls_oe_gpio, 0); + } + + complete_all(&ddata->hpd_completion); + + return IRQ_HANDLED; +} + +static int tpd_connect(struct omap_dss_device *dssdev, + struct omap_dss_device *dst) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + r = in->ops.hdmi->connect(in, dssdev); + if (r) + return r; + + dst->output = dssdev; + dssdev->device = dst; + + INIT_COMPLETION(ddata->hpd_completion); + + gpio_set_value_cansleep(ddata->ct_cp_hpd_gpio, 1); + /* DC-DC converter needs at max 300us to get to 90% of 5V */ + udelay(300); + + /* + * If there's a cable connected, wait for the hpd irq to trigger, + * which turns on the level shifters. + */ + if (gpio_get_value_cansleep(ddata->hpd_gpio)) { + unsigned long to; + to = wait_for_completion_timeout(&ddata->hpd_completion, + msecs_to_jiffies(250)); + WARN_ON_ONCE(to == 0); + } + + return 0; +} + +static void tpd_disconnect(struct omap_dss_device *dssdev, + struct omap_dss_device *dst) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + WARN_ON(dst != dssdev->device); + + if (dst != dssdev->device) + return; + + gpio_set_value_cansleep(ddata->ct_cp_hpd_gpio, 0); + + dst->output = NULL; + dssdev->device = NULL; + + in->ops.hdmi->disconnect(in, &ddata->dssdev); +} + +static int tpd_enable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) + return 0; + + in->ops.hdmi->set_timings(in, &ddata->timings); + + r = in->ops.hdmi->enable(in); + if (r) + return r; + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + return r; +} + +static void tpd_disable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + return; + + in->ops.hdmi->disable(in); + + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; +} + +static void tpd_set_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + ddata->timings = *timings; + dssdev->panel.timings = *timings; + + in->ops.hdmi->set_timings(in, timings); +} + +static void tpd_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + + *timings = ddata->timings; +} + +static int tpd_check_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + r = in->ops.hdmi->check_timings(in, timings); + + return r; +} + +static int tpd_read_edid(struct omap_dss_device *dssdev, + u8 *edid, int len) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + if (!gpio_get_value_cansleep(ddata->hpd_gpio)) + return -ENODEV; + + return in->ops.hdmi->read_edid(in, edid, len); +} + +static bool tpd_detect(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + + return gpio_get_value_cansleep(ddata->hpd_gpio); +} + +static int tpd_audio_enable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + return in->ops.hdmi->audio_enable(in); +} + +static void tpd_audio_disable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + in->ops.hdmi->audio_disable(in); +} + +static int tpd_audio_start(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + return in->ops.hdmi->audio_start(in); +} + +static void tpd_audio_stop(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + in->ops.hdmi->audio_stop(in); +} + +static bool tpd_audio_supported(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + return in->ops.hdmi->audio_supported(in); +} + +static int tpd_audio_config(struct omap_dss_device *dssdev, + struct omap_dss_audio *audio) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + return in->ops.hdmi->audio_config(in, audio); +} + +static const struct omapdss_hdmi_ops tpd_hdmi_ops = { + .connect = tpd_connect, + .disconnect = tpd_disconnect, + + .enable = tpd_enable, + .disable = tpd_disable, + + .check_timings = tpd_check_timings, + .set_timings = tpd_set_timings, + .get_timings = tpd_get_timings, + + .read_edid = tpd_read_edid, + .detect = tpd_detect, + + .audio_enable = tpd_audio_enable, + .audio_disable = tpd_audio_disable, + .audio_start = tpd_audio_start, + .audio_stop = tpd_audio_stop, + .audio_supported = tpd_audio_supported, + .audio_config = tpd_audio_config, +}; + +static int tpd_probe_pdata(struct platform_device *pdev) +{ + struct panel_drv_data *ddata = platform_get_drvdata(pdev); + struct encoder_tpd12s015_platform_data *pdata; + struct omap_dss_device *dssdev, *in; + + pdata = dev_get_platdata(&pdev->dev); + + ddata->ct_cp_hpd_gpio = pdata->ct_cp_hpd_gpio; + ddata->ls_oe_gpio = pdata->ls_oe_gpio; + ddata->hpd_gpio = pdata->hpd_gpio; + + in = omap_dss_find_output(pdata->source); + if (in == NULL) { + dev_err(&pdev->dev, "Failed to find video source\n"); + return -ENODEV; + } + + ddata->in = in; + + dssdev = &ddata->dssdev; + dssdev->name = pdata->name; + + return 0; +} + +static int tpd_probe(struct platform_device *pdev) +{ + struct omap_dss_device *in, *dssdev; + struct panel_drv_data *ddata; + int r; + + ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + + platform_set_drvdata(pdev, ddata); + + init_completion(&ddata->hpd_completion); + + if (dev_get_platdata(&pdev->dev)) { + r = tpd_probe_pdata(pdev); + if (r) + return r; + } else { + return -ENODEV; + } + + r = devm_gpio_request_one(&pdev->dev, ddata->ct_cp_hpd_gpio, + GPIOF_OUT_INIT_LOW, "hdmi_ct_cp_hpd"); + if (r) + goto err_gpio; + + if (gpio_is_valid(ddata->ls_oe_gpio)) { + r = devm_gpio_request_one(&pdev->dev, ddata->ls_oe_gpio, + GPIOF_OUT_INIT_LOW, "hdmi_ls_oe"); + if (r) + goto err_gpio; + } + + r = devm_gpio_request_one(&pdev->dev, ddata->hpd_gpio, + GPIOF_DIR_IN, "hdmi_hpd"); + if (r) + goto err_gpio; + + r = devm_request_threaded_irq(&pdev->dev, gpio_to_irq(ddata->hpd_gpio), + NULL, tpd_hpd_irq_handler, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, "hpd", ddata); + if (r) + goto err_irq; + + dssdev = &ddata->dssdev; + dssdev->ops.hdmi = &tpd_hdmi_ops; + dssdev->dev = &pdev->dev; + dssdev->type = OMAP_DISPLAY_TYPE_HDMI; + dssdev->output_type = OMAP_DISPLAY_TYPE_HDMI; + dssdev->owner = THIS_MODULE; + + in = ddata->in; + + r = omapdss_register_output(dssdev); + if (r) { + dev_err(&pdev->dev, "Failed to register output\n"); + goto err_reg; + } + + return 0; +err_reg: +err_irq: +err_gpio: + omap_dss_put_device(ddata->in); + return r; +} + +static int __exit tpd_remove(struct platform_device *pdev) +{ + struct panel_drv_data *ddata = platform_get_drvdata(pdev); + struct omap_dss_device *dssdev = &ddata->dssdev; + struct omap_dss_device *in = ddata->in; + + omapdss_unregister_output(&ddata->dssdev); + + WARN_ON(omapdss_device_is_enabled(dssdev)); + if (omapdss_device_is_enabled(dssdev)) + tpd_disable(dssdev); + + WARN_ON(omapdss_device_is_connected(dssdev)); + if (omapdss_device_is_connected(dssdev)) + tpd_disconnect(dssdev, dssdev->device); + + omap_dss_put_device(in); + + return 0; +} + +static struct platform_driver tpd_driver = { + .probe = tpd_probe, + .remove = __exit_p(tpd_remove), + .driver = { + .name = "tpd12s015", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(tpd_driver); + +MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); +MODULE_DESCRIPTION("TPD12S015 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays-new/panel-dpi.c b/drivers/video/omap2/displays-new/panel-dpi.c new file mode 100644 index 000000000000..5f8f7e7c81ef --- /dev/null +++ b/drivers/video/omap2/displays-new/panel-dpi.c @@ -0,0 +1,270 @@ +/* + * Generic MIPI DPI Panel Driver + * + * Copyright (C) 2013 Texas Instruments + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <linux/gpio.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include <video/omapdss.h> +#include <video/omap-panel-data.h> + +struct panel_drv_data { + struct omap_dss_device dssdev; + struct omap_dss_device *in; + + int data_lines; + + struct omap_video_timings videomode; + + int backlight_gpio; + int enable_gpio; +}; + +#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) + +static int panel_dpi_connect(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + if (omapdss_device_is_connected(dssdev)) + return 0; + + r = in->ops.dpi->connect(in, dssdev); + if (r) + return r; + + return 0; +} + +static void panel_dpi_disconnect(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + if (!omapdss_device_is_connected(dssdev)) + return; + + in->ops.dpi->disconnect(in, dssdev); +} + +static int panel_dpi_enable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + if (!omapdss_device_is_connected(dssdev)) + return -ENODEV; + + if (omapdss_device_is_enabled(dssdev)) + return 0; + + in->ops.dpi->set_data_lines(in, ddata->data_lines); + in->ops.dpi->set_timings(in, &ddata->videomode); + + r = in->ops.dpi->enable(in); + if (r) + return r; + + if (gpio_is_valid(ddata->enable_gpio)) + gpio_set_value_cansleep(ddata->enable_gpio, 1); + + if (gpio_is_valid(ddata->backlight_gpio)) + gpio_set_value_cansleep(ddata->backlight_gpio, 1); + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + return 0; +} + +static void panel_dpi_disable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + if (!omapdss_device_is_enabled(dssdev)) + return; + + if (gpio_is_valid(ddata->enable_gpio)) + gpio_set_value_cansleep(ddata->enable_gpio, 0); + + if (gpio_is_valid(ddata->backlight_gpio)) + gpio_set_value_cansleep(ddata->backlight_gpio, 0); + + in->ops.dpi->disable(in); + + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; +} + +static void panel_dpi_set_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + ddata->videomode = *timings; + dssdev->panel.timings = *timings; + + in->ops.dpi->set_timings(in, timings); +} + +static void panel_dpi_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + + *timings = ddata->videomode; +} + +static int panel_dpi_check_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + return in->ops.dpi->check_timings(in, timings); +} + +static struct omap_dss_driver panel_dpi_ops = { + .connect = panel_dpi_connect, + .disconnect = panel_dpi_disconnect, + + .enable = panel_dpi_enable, + .disable = panel_dpi_disable, + + .set_timings = panel_dpi_set_timings, + .get_timings = panel_dpi_get_timings, + .check_timings = panel_dpi_check_timings, + + .get_resolution = omapdss_default_get_resolution, +}; + +static int panel_dpi_probe_pdata(struct platform_device *pdev) +{ + const struct panel_dpi_platform_data *pdata; + struct panel_drv_data *ddata = platform_get_drvdata(pdev); + struct omap_dss_device *dssdev, *in; + struct videomode vm; + + pdata = dev_get_platdata(&pdev->dev); + + in = omap_dss_find_output(pdata->source); + if (in == NULL) { + dev_err(&pdev->dev, "failed to find video source '%s'\n", + pdata->source); + return -EPROBE_DEFER; + } + + ddata->in = in; + + ddata->data_lines = pdata->data_lines; + + videomode_from_timing(pdata->display_timing, &vm); + videomode_to_omap_video_timings(&vm, &ddata->videomode); + + dssdev = &ddata->dssdev; + dssdev->name = pdata->name; + + ddata->enable_gpio = pdata->enable_gpio; + ddata->backlight_gpio = pdata->backlight_gpio; + + return 0; +} + +static int panel_dpi_probe(struct platform_device *pdev) +{ + struct panel_drv_data *ddata; + struct omap_dss_device *dssdev; + int r; + + ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); + if (ddata == NULL) + return -ENOMEM; + + platform_set_drvdata(pdev, ddata); + + if (dev_get_platdata(&pdev->dev)) { + r = panel_dpi_probe_pdata(pdev); + if (r) + return r; + } else { + return -ENODEV; + } + + if (gpio_is_valid(ddata->enable_gpio)) { + r = devm_gpio_request_one(&pdev->dev, ddata->enable_gpio, + GPIOF_OUT_INIT_LOW, "panel enable"); + if (r) + goto err_gpio; + } + + if (gpio_is_valid(ddata->backlight_gpio)) { + r = devm_gpio_request_one(&pdev->dev, ddata->backlight_gpio, + GPIOF_OUT_INIT_LOW, "panel backlight"); + if (r) + goto err_gpio; + } + + dssdev = &ddata->dssdev; + dssdev->dev = &pdev->dev; + dssdev->driver = &panel_dpi_ops; + dssdev->type = OMAP_DISPLAY_TYPE_DPI; + dssdev->owner = THIS_MODULE; + dssdev->panel.timings = ddata->videomode; + dssdev->phy.dpi.data_lines = ddata->data_lines; + + r = omapdss_register_display(dssdev); + if (r) { + dev_err(&pdev->dev, "Failed to register panel\n"); + goto err_reg; + } + + return 0; + +err_reg: +err_gpio: + omap_dss_put_device(ddata->in); + return r; +} + +static int __exit panel_dpi_remove(struct platform_device *pdev) +{ + struct panel_drv_data *ddata = platform_get_drvdata(pdev); + struct omap_dss_device *dssdev = &ddata->dssdev; + struct omap_dss_device *in = ddata->in; + + omapdss_unregister_display(dssdev); + + panel_dpi_disable(dssdev); + panel_dpi_disconnect(dssdev); + + omap_dss_put_device(in); + + return 0; +} + +static struct platform_driver panel_dpi_driver = { + .probe = panel_dpi_probe, + .remove = __exit_p(panel_dpi_remove), + .driver = { + .name = "panel-dpi", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(panel_dpi_driver); + +MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); +MODULE_DESCRIPTION("Generic MIPI DPI Panel Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays-new/panel-dsi-cm.c b/drivers/video/omap2/displays-new/panel-dsi-cm.c new file mode 100644 index 000000000000..aaaea6469cd9 --- /dev/null +++ b/drivers/video/omap2/displays-new/panel-dsi-cm.c @@ -0,0 +1,1336 @@ +/* + * Generic DSI Command Mode panel driver + * + * Copyright (C) 2013 Texas Instruments + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +/* #define DEBUG */ + +#include <linux/backlight.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/jiffies.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/workqueue.h> + +#include <video/omapdss.h> +#include <video/omap-panel-data.h> +#include <video/mipi_display.h> + +/* DSI Virtual channel. Hardcoded for now. */ +#define TCH 0 + +#define DCS_READ_NUM_ERRORS 0x05 +#define DCS_BRIGHTNESS 0x51 +#define DCS_CTRL_DISPLAY 0x53 +#define DCS_GET_ID1 0xda +#define DCS_GET_ID2 0xdb +#define DCS_GET_ID3 0xdc + +struct panel_drv_data { + struct omap_dss_device dssdev; + struct omap_dss_device *in; + + struct omap_video_timings timings; + + struct platform_device *pdev; + + struct mutex lock; + + struct backlight_device *bldev; + + unsigned long hw_guard_end; /* next value of jiffies when we can + * issue the next sleep in/out command + */ + unsigned long hw_guard_wait; /* max guard time in jiffies */ + + /* panel HW configuration from DT or platform data */ + int reset_gpio; + int ext_te_gpio; + + bool use_dsi_backlight; + + struct omap_dsi_pin_config pin_config; + + /* runtime variables */ + bool enabled; + + bool te_enabled; + + atomic_t do_update; + int channel; + + struct delayed_work te_timeout_work; + + bool intro_printed; + + struct workqueue_struct *workqueue; + + bool ulps_enabled; + unsigned ulps_timeout; + struct delayed_work ulps_work; +}; + +#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) + +static irqreturn_t dsicm_te_isr(int irq, void *data); +static void dsicm_te_timeout_work_callback(struct work_struct *work); +static int _dsicm_enable_te(struct panel_drv_data *ddata, bool enable); + +static int dsicm_panel_reset(struct panel_drv_data *ddata); + +static void dsicm_ulps_work(struct work_struct *work); + +static void hw_guard_start(struct panel_drv_data *ddata, int guard_msec) +{ + ddata->hw_guard_wait = msecs_to_jiffies(guard_msec); + ddata->hw_guard_end = jiffies + ddata->hw_guard_wait; +} + +static void hw_guard_wait(struct panel_drv_data *ddata) +{ + unsigned long wait = ddata->hw_guard_end - jiffies; + + if ((long)wait > 0 && wait <= ddata->hw_guard_wait) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(wait); + } +} + +static int dsicm_dcs_read_1(struct panel_drv_data *ddata, u8 dcs_cmd, u8 *data) +{ + struct omap_dss_device *in = ddata->in; + int r; + u8 buf[1]; + + r = in->ops.dsi->dcs_read(in, ddata->channel, dcs_cmd, buf, 1); + + if (r < 0) + return r; + + *data = buf[0]; + + return 0; +} + +static int dsicm_dcs_write_0(struct panel_drv_data *ddata, u8 dcs_cmd) +{ + struct omap_dss_device *in = ddata->in; + return in->ops.dsi->dcs_write(in, ddata->channel, &dcs_cmd, 1); +} + +static int dsicm_dcs_write_1(struct panel_drv_data *ddata, u8 dcs_cmd, u8 param) +{ + struct omap_dss_device *in = ddata->in; + u8 buf[2] = { dcs_cmd, param }; + + return in->ops.dsi->dcs_write(in, ddata->channel, buf, 2); +} + +static int dsicm_sleep_in(struct panel_drv_data *ddata) + +{ + struct omap_dss_device *in = ddata->in; + u8 cmd; + int r; + + hw_guard_wait(ddata); + + cmd = MIPI_DCS_ENTER_SLEEP_MODE; + r = in->ops.dsi->dcs_write_nosync(in, ddata->channel, &cmd, 1); + if (r) + return r; + + hw_guard_start(ddata, 120); + + usleep_range(5000, 10000); + + return 0; +} + +static int dsicm_sleep_out(struct panel_drv_data *ddata) +{ + int r; + + hw_guard_wait(ddata); + + r = dsicm_dcs_write_0(ddata, MIPI_DCS_EXIT_SLEEP_MODE); + if (r) + return r; + + hw_guard_start(ddata, 120); + + usleep_range(5000, 10000); + + return 0; +} + +static int dsicm_get_id(struct panel_drv_data *ddata, u8 *id1, u8 *id2, u8 *id3) +{ + int r; + + r = dsicm_dcs_read_1(ddata, DCS_GET_ID1, id1); + if (r) + return r; + r = dsicm_dcs_read_1(ddata, DCS_GET_ID2, id2); + if (r) + return r; + r = dsicm_dcs_read_1(ddata, DCS_GET_ID3, id3); + if (r) + return r; + + return 0; +} + +static int dsicm_set_update_window(struct panel_drv_data *ddata, + u16 x, u16 y, u16 w, u16 h) +{ + struct omap_dss_device *in = ddata->in; + int r; + u16 x1 = x; + u16 x2 = x + w - 1; + u16 y1 = y; + u16 y2 = y + h - 1; + + u8 buf[5]; + buf[0] = MIPI_DCS_SET_COLUMN_ADDRESS; + buf[1] = (x1 >> 8) & 0xff; + buf[2] = (x1 >> 0) & 0xff; + buf[3] = (x2 >> 8) & 0xff; + buf[4] = (x2 >> 0) & 0xff; + + r = in->ops.dsi->dcs_write_nosync(in, ddata->channel, buf, sizeof(buf)); + if (r) + return r; + + buf[0] = MIPI_DCS_SET_PAGE_ADDRESS; + buf[1] = (y1 >> 8) & 0xff; + buf[2] = (y1 >> 0) & 0xff; + buf[3] = (y2 >> 8) & 0xff; + buf[4] = (y2 >> 0) & 0xff; + + r = in->ops.dsi->dcs_write_nosync(in, ddata->channel, buf, sizeof(buf)); + if (r) + return r; + + in->ops.dsi->bta_sync(in, ddata->channel); + + return r; +} + +static void dsicm_queue_ulps_work(struct panel_drv_data *ddata) +{ + if (ddata->ulps_timeout > 0) + queue_delayed_work(ddata->workqueue, &ddata->ulps_work, + msecs_to_jiffies(ddata->ulps_timeout)); +} + +static void dsicm_cancel_ulps_work(struct panel_drv_data *ddata) +{ + cancel_delayed_work(&ddata->ulps_work); +} + +static int dsicm_enter_ulps(struct panel_drv_data *ddata) +{ + struct omap_dss_device *in = ddata->in; + int r; + + if (ddata->ulps_enabled) + return 0; + + dsicm_cancel_ulps_work(ddata); + + r = _dsicm_enable_te(ddata, false); + if (r) + goto err; + + if (gpio_is_valid(ddata->ext_te_gpio)) + disable_irq(gpio_to_irq(ddata->ext_te_gpio)); + + in->ops.dsi->disable(in, false, true); + + ddata->ulps_enabled = true; + + return 0; + +err: + dev_err(&ddata->pdev->dev, "enter ULPS failed"); + dsicm_panel_reset(ddata); + + ddata->ulps_enabled = false; + + dsicm_queue_ulps_work(ddata); + + return r; +} + +static int dsicm_exit_ulps(struct panel_drv_data *ddata) +{ + struct omap_dss_device *in = ddata->in; + int r; + + if (!ddata->ulps_enabled) + return 0; + + r = in->ops.dsi->enable(in); + if (r) { + dev_err(&ddata->pdev->dev, "failed to enable DSI\n"); + goto err1; + } + + in->ops.dsi->enable_hs(in, ddata->channel, true); + + r = _dsicm_enable_te(ddata, true); + if (r) { + dev_err(&ddata->pdev->dev, "failed to re-enable TE"); + goto err2; + } + + if (gpio_is_valid(ddata->ext_te_gpio)) + enable_irq(gpio_to_irq(ddata->ext_te_gpio)); + + dsicm_queue_ulps_work(ddata); + + ddata->ulps_enabled = false; + + return 0; + +err2: + dev_err(&ddata->pdev->dev, "failed to exit ULPS"); + + r = dsicm_panel_reset(ddata); + if (!r) { + if (gpio_is_valid(ddata->ext_te_gpio)) + enable_irq(gpio_to_irq(ddata->ext_te_gpio)); + ddata->ulps_enabled = false; + } +err1: + dsicm_queue_ulps_work(ddata); + + return r; +} + +static int dsicm_wake_up(struct panel_drv_data *ddata) +{ + if (ddata->ulps_enabled) + return dsicm_exit_ulps(ddata); + + dsicm_cancel_ulps_work(ddata); + dsicm_queue_ulps_work(ddata); + return 0; +} + +static int dsicm_bl_update_status(struct backlight_device *dev) +{ + struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev); + struct omap_dss_device *in = ddata->in; + int r; + int level; + + if (dev->props.fb_blank == FB_BLANK_UNBLANK && + dev->props.power == FB_BLANK_UNBLANK) + level = dev->props.brightness; + else + level = 0; + + dev_dbg(&ddata->pdev->dev, "update brightness to %d\n", level); + + mutex_lock(&ddata->lock); + + if (ddata->enabled) { + in->ops.dsi->bus_lock(in); + + r = dsicm_wake_up(ddata); + if (!r) + r = dsicm_dcs_write_1(ddata, DCS_BRIGHTNESS, level); + + in->ops.dsi->bus_unlock(in); + } else { + r = 0; + } + + mutex_unlock(&ddata->lock); + + return r; +} + +static int dsicm_bl_get_intensity(struct backlight_device *dev) +{ + if (dev->props.fb_blank == FB_BLANK_UNBLANK && + dev->props.power == FB_BLANK_UNBLANK) + return dev->props.brightness; + + return 0; +} + +static const struct backlight_ops dsicm_bl_ops = { + .get_brightness = dsicm_bl_get_intensity, + .update_status = dsicm_bl_update_status, +}; + +static void dsicm_get_resolution(struct omap_dss_device *dssdev, + u16 *xres, u16 *yres) +{ + *xres = dssdev->panel.timings.x_res; + *yres = dssdev->panel.timings.y_res; +} + +static ssize_t dsicm_num_errors_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct panel_drv_data *ddata = platform_get_drvdata(pdev); + struct omap_dss_device *in = ddata->in; + u8 errors = 0; + int r; + + mutex_lock(&ddata->lock); + + if (ddata->enabled) { + in->ops.dsi->bus_lock(in); + + r = dsicm_wake_up(ddata); + if (!r) + r = dsicm_dcs_read_1(ddata, DCS_READ_NUM_ERRORS, + &errors); + + in->ops.dsi->bus_unlock(in); + } else { + r = -ENODEV; + } + + mutex_unlock(&ddata->lock); + + if (r) + return r; + + return snprintf(buf, PAGE_SIZE, "%d\n", errors); +} + +static ssize_t dsicm_hw_revision_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct panel_drv_data *ddata = platform_get_drvdata(pdev); + struct omap_dss_device *in = ddata->in; + u8 id1, id2, id3; + int r; + + mutex_lock(&ddata->lock); + + if (ddata->enabled) { + in->ops.dsi->bus_lock(in); + + r = dsicm_wake_up(ddata); + if (!r) + r = dsicm_get_id(ddata, &id1, &id2, &id3); + + in->ops.dsi->bus_unlock(in); + } else { + r = -ENODEV; + } + + mutex_unlock(&ddata->lock); + + if (r) + return r; + + return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x\n", id1, id2, id3); +} + +static ssize_t dsicm_store_ulps(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct panel_drv_data *ddata = platform_get_drvdata(pdev); + struct omap_dss_device *in = ddata->in; + unsigned long t; + int r; + + r = kstrtoul(buf, 0, &t); + if (r) + return r; + + mutex_lock(&ddata->lock); + + if (ddata->enabled) { + in->ops.dsi->bus_lock(in); + + if (t) + r = dsicm_enter_ulps(ddata); + else + r = dsicm_wake_up(ddata); + + in->ops.dsi->bus_unlock(in); + } + + mutex_unlock(&ddata->lock); + + if (r) + return r; + + return count; +} + +static ssize_t dsicm_show_ulps(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct panel_drv_data *ddata = platform_get_drvdata(pdev); + unsigned t; + + mutex_lock(&ddata->lock); + t = ddata->ulps_enabled; + mutex_unlock(&ddata->lock); + + return snprintf(buf, PAGE_SIZE, "%u\n", t); +} + +static ssize_t dsicm_store_ulps_timeout(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct panel_drv_data *ddata = platform_get_drvdata(pdev); + struct omap_dss_device *in = ddata->in; + unsigned long t; + int r; + + r = kstrtoul(buf, 0, &t); + if (r) + return r; + + mutex_lock(&ddata->lock); + ddata->ulps_timeout = t; + + if (ddata->enabled) { + /* dsicm_wake_up will restart the timer */ + in->ops.dsi->bus_lock(in); + r = dsicm_wake_up(ddata); + in->ops.dsi->bus_unlock(in); + } + + mutex_unlock(&ddata->lock); + + if (r) + return r; + + return count; +} + +static ssize_t dsicm_show_ulps_timeout(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct panel_drv_data *ddata = platform_get_drvdata(pdev); + unsigned t; + + mutex_lock(&ddata->lock); + t = ddata->ulps_timeout; + mutex_unlock(&ddata->lock); + + return snprintf(buf, PAGE_SIZE, "%u\n", t); +} + +static DEVICE_ATTR(num_dsi_errors, S_IRUGO, dsicm_num_errors_show, NULL); +static DEVICE_ATTR(hw_revision, S_IRUGO, dsicm_hw_revision_show, NULL); +static DEVICE_ATTR(ulps, S_IRUGO | S_IWUSR, + dsicm_show_ulps, dsicm_store_ulps); +static DEVICE_ATTR(ulps_timeout, S_IRUGO | S_IWUSR, + dsicm_show_ulps_timeout, dsicm_store_ulps_timeout); + +static struct attribute *dsicm_attrs[] = { + &dev_attr_num_dsi_errors.attr, + &dev_attr_hw_revision.attr, + &dev_attr_ulps.attr, + &dev_attr_ulps_timeout.attr, + NULL, +}; + +static struct attribute_group dsicm_attr_group = { + .attrs = dsicm_attrs, +}; + +static void dsicm_hw_reset(struct panel_drv_data *ddata) +{ + if (!gpio_is_valid(ddata->reset_gpio)) + return; + + gpio_set_value(ddata->reset_gpio, 1); + udelay(10); + /* reset the panel */ + gpio_set_value(ddata->reset_gpio, 0); + /* assert reset */ + udelay(10); + gpio_set_value(ddata->reset_gpio, 1); + /* wait after releasing reset */ + usleep_range(5000, 10000); +} + +static int dsicm_power_on(struct panel_drv_data *ddata) +{ + struct omap_dss_device *in = ddata->in; + u8 id1, id2, id3; + int r; + struct omap_dss_dsi_config dsi_config = { + .mode = OMAP_DSS_DSI_CMD_MODE, + .pixel_format = OMAP_DSS_DSI_FMT_RGB888, + .timings = &ddata->timings, + .hs_clk_min = 150000000, + .hs_clk_max = 300000000, + .lp_clk_min = 7000000, + .lp_clk_max = 10000000, + }; + + r = in->ops.dsi->configure_pins(in, &ddata->pin_config); + if (r) { + dev_err(&ddata->pdev->dev, "failed to configure DSI pins\n"); + goto err0; + }; + + r = in->ops.dsi->set_config(in, &dsi_config); + if (r) { + dev_err(&ddata->pdev->dev, "failed to configure DSI\n"); + goto err0; + } + + r = in->ops.dsi->enable(in); + if (r) { + dev_err(&ddata->pdev->dev, "failed to enable DSI\n"); + goto err0; + } + + dsicm_hw_reset(ddata); + + in->ops.dsi->enable_hs(in, ddata->channel, false); + + r = dsicm_sleep_out(ddata); + if (r) + goto err; + + r = dsicm_get_id(ddata, &id1, &id2, &id3); + if (r) + goto err; + + r = dsicm_dcs_write_1(ddata, DCS_BRIGHTNESS, 0xff); + if (r) + goto err; + + r = dsicm_dcs_write_1(ddata, DCS_CTRL_DISPLAY, + (1<<2) | (1<<5)); /* BL | BCTRL */ + if (r) + goto err; + + r = dsicm_dcs_write_1(ddata, MIPI_DCS_SET_PIXEL_FORMAT, + MIPI_DCS_PIXEL_FMT_24BIT); + if (r) + goto err; + + r = dsicm_dcs_write_0(ddata, MIPI_DCS_SET_DISPLAY_ON); + if (r) + goto err; + + r = _dsicm_enable_te(ddata, ddata->te_enabled); + if (r) + goto err; + + r = in->ops.dsi->enable_video_output(in, ddata->channel); + if (r) + goto err; + + ddata->enabled = 1; + + if (!ddata->intro_printed) { + dev_info(&ddata->pdev->dev, "panel revision %02x.%02x.%02x\n", + id1, id2, id3); + ddata->intro_printed = true; + } + + in->ops.dsi->enable_hs(in, ddata->channel, true); + + return 0; +err: + dev_err(&ddata->pdev->dev, "error while enabling panel, issuing HW reset\n"); + + dsicm_hw_reset(ddata); + + in->ops.dsi->disable(in, true, false); +err0: + return r; +} + +static void dsicm_power_off(struct panel_drv_data *ddata) +{ + struct omap_dss_device *in = ddata->in; + int r; + + in->ops.dsi->disable_video_output(in, ddata->channel); + + r = dsicm_dcs_write_0(ddata, MIPI_DCS_SET_DISPLAY_OFF); + if (!r) + r = dsicm_sleep_in(ddata); + + if (r) { + dev_err(&ddata->pdev->dev, + "error disabling panel, issuing HW reset\n"); + dsicm_hw_reset(ddata); + } + + in->ops.dsi->disable(in, true, false); + + ddata->enabled = 0; +} + +static int dsicm_panel_reset(struct panel_drv_data *ddata) +{ + dev_err(&ddata->pdev->dev, "performing LCD reset\n"); + + dsicm_power_off(ddata); + dsicm_hw_reset(ddata); + return dsicm_power_on(ddata); +} + +static int dsicm_connect(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + struct device *dev = &ddata->pdev->dev; + int r; + + if (omapdss_device_is_connected(dssdev)) + return 0; + + r = in->ops.dsi->connect(in, dssdev); + if (r) { + dev_err(dev, "Failed to connect to video source\n"); + return r; + } + + r = in->ops.dsi->request_vc(ddata->in, &ddata->channel); + if (r) { + dev_err(dev, "failed to get virtual channel\n"); + goto err_req_vc; + } + + r = in->ops.dsi->set_vc_id(ddata->in, ddata->channel, TCH); + if (r) { + dev_err(dev, "failed to set VC_ID\n"); + goto err_vc_id; + } + + return 0; + +err_vc_id: + in->ops.dsi->release_vc(ddata->in, ddata->channel); +err_req_vc: + in->ops.dsi->disconnect(in, dssdev); + return r; +} + +static void dsicm_disconnect(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + if (!omapdss_device_is_connected(dssdev)) + return; + + in->ops.dsi->release_vc(in, ddata->channel); + in->ops.dsi->disconnect(in, dssdev); +} + +static int dsicm_enable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + dev_dbg(&ddata->pdev->dev, "enable\n"); + + mutex_lock(&ddata->lock); + + if (!omapdss_device_is_connected(dssdev)) { + r = -ENODEV; + goto err; + } + + if (omapdss_device_is_enabled(dssdev)) { + r = 0; + goto err; + } + + in->ops.dsi->bus_lock(in); + + r = dsicm_power_on(ddata); + + in->ops.dsi->bus_unlock(in); + + if (r) + goto err; + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + mutex_unlock(&ddata->lock); + + return 0; +err: + dev_dbg(&ddata->pdev->dev, "enable failed\n"); + mutex_unlock(&ddata->lock); + return r; +} + +static void dsicm_disable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + dev_dbg(&ddata->pdev->dev, "disable\n"); + + mutex_lock(&ddata->lock); + + dsicm_cancel_ulps_work(ddata); + + in->ops.dsi->bus_lock(in); + + if (omapdss_device_is_enabled(dssdev)) { + r = dsicm_wake_up(ddata); + if (!r) + dsicm_power_off(ddata); + } + + in->ops.dsi->bus_unlock(in); + + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; + + mutex_unlock(&ddata->lock); +} + +static void dsicm_framedone_cb(int err, void *data) +{ + struct panel_drv_data *ddata = data; + struct omap_dss_device *in = ddata->in; + + dev_dbg(&ddata->pdev->dev, "framedone, err %d\n", err); + in->ops.dsi->bus_unlock(ddata->in); +} + +static irqreturn_t dsicm_te_isr(int irq, void *data) +{ + struct panel_drv_data *ddata = data; + struct omap_dss_device *in = ddata->in; + int old; + int r; + + old = atomic_cmpxchg(&ddata->do_update, 1, 0); + + if (old) { + cancel_delayed_work(&ddata->te_timeout_work); + + r = in->ops.dsi->update(in, ddata->channel, dsicm_framedone_cb, + ddata); + if (r) + goto err; + } + + return IRQ_HANDLED; +err: + dev_err(&ddata->pdev->dev, "start update failed\n"); + in->ops.dsi->bus_unlock(in); + return IRQ_HANDLED; +} + +static void dsicm_te_timeout_work_callback(struct work_struct *work) +{ + struct panel_drv_data *ddata = container_of(work, struct panel_drv_data, + te_timeout_work.work); + struct omap_dss_device *in = ddata->in; + + dev_err(&ddata->pdev->dev, "TE not received for 250ms!\n"); + + atomic_set(&ddata->do_update, 0); + in->ops.dsi->bus_unlock(in); +} + +static int dsicm_update(struct omap_dss_device *dssdev, + u16 x, u16 y, u16 w, u16 h) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + dev_dbg(&ddata->pdev->dev, "update %d, %d, %d x %d\n", x, y, w, h); + + mutex_lock(&ddata->lock); + in->ops.dsi->bus_lock(in); + + r = dsicm_wake_up(ddata); + if (r) + goto err; + + if (!ddata->enabled) { + r = 0; + goto err; + } + + /* XXX no need to send this every frame, but dsi break if not done */ + r = dsicm_set_update_window(ddata, 0, 0, + dssdev->panel.timings.x_res, + dssdev->panel.timings.y_res); + if (r) + goto err; + + if (ddata->te_enabled && gpio_is_valid(ddata->ext_te_gpio)) { + schedule_delayed_work(&ddata->te_timeout_work, + msecs_to_jiffies(250)); + atomic_set(&ddata->do_update, 1); + } else { + r = in->ops.dsi->update(in, ddata->channel, dsicm_framedone_cb, + ddata); + if (r) + goto err; + } + + /* note: no bus_unlock here. unlock is in framedone_cb */ + mutex_unlock(&ddata->lock); + return 0; +err: + in->ops.dsi->bus_unlock(in); + mutex_unlock(&ddata->lock); + return r; +} + +static int dsicm_sync(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + dev_dbg(&ddata->pdev->dev, "sync\n"); + + mutex_lock(&ddata->lock); + in->ops.dsi->bus_lock(in); + in->ops.dsi->bus_unlock(in); + mutex_unlock(&ddata->lock); + + dev_dbg(&ddata->pdev->dev, "sync done\n"); + + return 0; +} + +static int _dsicm_enable_te(struct panel_drv_data *ddata, bool enable) +{ + struct omap_dss_device *in = ddata->in; + int r; + + if (enable) + r = dsicm_dcs_write_1(ddata, MIPI_DCS_SET_TEAR_ON, 0); + else + r = dsicm_dcs_write_0(ddata, MIPI_DCS_SET_TEAR_OFF); + + if (!gpio_is_valid(ddata->ext_te_gpio)) + in->ops.dsi->enable_te(in, enable); + + /* possible panel bug */ + msleep(100); + + return r; +} + +static int dsicm_enable_te(struct omap_dss_device *dssdev, bool enable) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + mutex_lock(&ddata->lock); + + if (ddata->te_enabled == enable) + goto end; + + in->ops.dsi->bus_lock(in); + + if (ddata->enabled) { + r = dsicm_wake_up(ddata); + if (r) + goto err; + + r = _dsicm_enable_te(ddata, enable); + if (r) + goto err; + } + + ddata->te_enabled = enable; + + in->ops.dsi->bus_unlock(in); +end: + mutex_unlock(&ddata->lock); + + return 0; +err: + in->ops.dsi->bus_unlock(in); + mutex_unlock(&ddata->lock); + + return r; +} + +static int dsicm_get_te(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + int r; + + mutex_lock(&ddata->lock); + r = ddata->te_enabled; + mutex_unlock(&ddata->lock); + + return r; +} + +static int dsicm_memory_read(struct omap_dss_device *dssdev, + void *buf, size_t size, + u16 x, u16 y, u16 w, u16 h) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + int first = 1; + int plen; + unsigned buf_used = 0; + + if (size < w * h * 3) + return -ENOMEM; + + mutex_lock(&ddata->lock); + + if (!ddata->enabled) { + r = -ENODEV; + goto err1; + } + + size = min(w * h * 3, + dssdev->panel.timings.x_res * + dssdev->panel.timings.y_res * 3); + + in->ops.dsi->bus_lock(in); + + r = dsicm_wake_up(ddata); + if (r) + goto err2; + + /* plen 1 or 2 goes into short packet. until checksum error is fixed, + * use short packets. plen 32 works, but bigger packets seem to cause + * an error. */ + if (size % 2) + plen = 1; + else + plen = 2; + + dsicm_set_update_window(ddata, x, y, w, h); + + r = in->ops.dsi->set_max_rx_packet_size(in, ddata->channel, plen); + if (r) + goto err2; + + while (buf_used < size) { + u8 dcs_cmd = first ? 0x2e : 0x3e; + first = 0; + + r = in->ops.dsi->dcs_read(in, ddata->channel, dcs_cmd, + buf + buf_used, size - buf_used); + + if (r < 0) { + dev_err(dssdev->dev, "read error\n"); + goto err3; + } + + buf_used += r; + + if (r < plen) { + dev_err(&ddata->pdev->dev, "short read\n"); + break; + } + + if (signal_pending(current)) { + dev_err(&ddata->pdev->dev, "signal pending, " + "aborting memory read\n"); + r = -ERESTARTSYS; + goto err3; + } + } + + r = buf_used; + +err3: + in->ops.dsi->set_max_rx_packet_size(in, ddata->channel, 1); +err2: + in->ops.dsi->bus_unlock(in); +err1: + mutex_unlock(&ddata->lock); + return r; +} + +static void dsicm_ulps_work(struct work_struct *work) +{ + struct panel_drv_data *ddata = container_of(work, struct panel_drv_data, + ulps_work.work); + struct omap_dss_device *dssdev = &ddata->dssdev; + struct omap_dss_device *in = ddata->in; + + mutex_lock(&ddata->lock); + + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE || !ddata->enabled) { + mutex_unlock(&ddata->lock); + return; + } + + in->ops.dsi->bus_lock(in); + + dsicm_enter_ulps(ddata); + + in->ops.dsi->bus_unlock(in); + mutex_unlock(&ddata->lock); +} + +static struct omap_dss_driver dsicm_ops = { + .connect = dsicm_connect, + .disconnect = dsicm_disconnect, + + .enable = dsicm_enable, + .disable = dsicm_disable, + + .update = dsicm_update, + .sync = dsicm_sync, + + .get_resolution = dsicm_get_resolution, + .get_recommended_bpp = omapdss_default_get_recommended_bpp, + + .enable_te = dsicm_enable_te, + .get_te = dsicm_get_te, + + .memory_read = dsicm_memory_read, +}; + +static int dsicm_probe_pdata(struct platform_device *pdev) +{ + const struct panel_dsicm_platform_data *pdata; + struct panel_drv_data *ddata = platform_get_drvdata(pdev); + struct omap_dss_device *dssdev, *in; + + pdata = dev_get_platdata(&pdev->dev); + + in = omap_dss_find_output(pdata->source); + if (in == NULL) { + dev_err(&pdev->dev, "failed to find video source\n"); + return -EPROBE_DEFER; + } + ddata->in = in; + + ddata->reset_gpio = pdata->reset_gpio; + + if (pdata->use_ext_te) + ddata->ext_te_gpio = pdata->ext_te_gpio; + else + ddata->ext_te_gpio = -1; + + ddata->ulps_timeout = pdata->ulps_timeout; + + ddata->use_dsi_backlight = pdata->use_dsi_backlight; + + ddata->pin_config = pdata->pin_config; + + dssdev = &ddata->dssdev; + dssdev->name = pdata->name; + + return 0; +} + +static int dsicm_probe(struct platform_device *pdev) +{ + struct backlight_properties props; + struct panel_drv_data *ddata; + struct backlight_device *bldev = NULL; + struct device *dev = &pdev->dev; + struct omap_dss_device *dssdev; + int r; + + dev_dbg(dev, "probe\n"); + + ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + + platform_set_drvdata(pdev, ddata); + ddata->pdev = pdev; + + if (dev_get_platdata(dev)) { + r = dsicm_probe_pdata(pdev); + if (r) + return r; + } else { + return -ENODEV; + } + + ddata->timings.x_res = 864; + ddata->timings.y_res = 480; + ddata->timings.pixel_clock = DIV_ROUND_UP(864 * 480 * 60, 1000); + + dssdev = &ddata->dssdev; + dssdev->dev = dev; + dssdev->driver = &dsicm_ops; + dssdev->panel.timings = ddata->timings; + dssdev->type = OMAP_DISPLAY_TYPE_DSI; + dssdev->owner = THIS_MODULE; + + dssdev->panel.dsi_pix_fmt = OMAP_DSS_DSI_FMT_RGB888; + dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE | + OMAP_DSS_DISPLAY_CAP_TEAR_ELIM; + + r = omapdss_register_display(dssdev); + if (r) { + dev_err(dev, "Failed to register panel\n"); + goto err_reg; + } + + mutex_init(&ddata->lock); + + atomic_set(&ddata->do_update, 0); + + if (gpio_is_valid(ddata->reset_gpio)) { + r = devm_gpio_request_one(dev, ddata->reset_gpio, + GPIOF_OUT_INIT_LOW, "taal rst"); + if (r) { + dev_err(dev, "failed to request reset gpio\n"); + return r; + } + } + + if (gpio_is_valid(ddata->ext_te_gpio)) { + r = devm_gpio_request_one(dev, ddata->ext_te_gpio, + GPIOF_IN, "taal irq"); + if (r) { + dev_err(dev, "GPIO request failed\n"); + return r; + } + + r = devm_request_irq(dev, gpio_to_irq(ddata->ext_te_gpio), + dsicm_te_isr, + IRQF_TRIGGER_RISING, + "taal vsync", ddata); + + if (r) { + dev_err(dev, "IRQ request failed\n"); + return r; + } + + INIT_DEFERRABLE_WORK(&ddata->te_timeout_work, + dsicm_te_timeout_work_callback); + + dev_dbg(dev, "Using GPIO TE\n"); + } + + ddata->workqueue = create_singlethread_workqueue("dsicm_wq"); + if (ddata->workqueue == NULL) { + dev_err(dev, "can't create workqueue\n"); + return -ENOMEM; + } + INIT_DELAYED_WORK(&ddata->ulps_work, dsicm_ulps_work); + + dsicm_hw_reset(ddata); + + if (ddata->use_dsi_backlight) { + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = 255; + + props.type = BACKLIGHT_RAW; + bldev = backlight_device_register(dev_name(dev), + dev, ddata, &dsicm_bl_ops, &props); + if (IS_ERR(bldev)) { + r = PTR_ERR(bldev); + goto err_bl; + } + + ddata->bldev = bldev; + + bldev->props.fb_blank = FB_BLANK_UNBLANK; + bldev->props.power = FB_BLANK_UNBLANK; + bldev->props.brightness = 255; + + dsicm_bl_update_status(bldev); + } + + r = sysfs_create_group(&dev->kobj, &dsicm_attr_group); + if (r) { + dev_err(dev, "failed to create sysfs files\n"); + goto err_sysfs_create; + } + + return 0; + +err_sysfs_create: + if (bldev != NULL) + backlight_device_unregister(bldev); +err_bl: + destroy_workqueue(ddata->workqueue); +err_reg: + return r; +} + +static int __exit dsicm_remove(struct platform_device *pdev) +{ + struct panel_drv_data *ddata = platform_get_drvdata(pdev); + struct omap_dss_device *dssdev = &ddata->dssdev; + struct backlight_device *bldev; + + dev_dbg(&pdev->dev, "remove\n"); + + omapdss_unregister_display(dssdev); + + dsicm_disable(dssdev); + dsicm_disconnect(dssdev); + + sysfs_remove_group(&pdev->dev.kobj, &dsicm_attr_group); + + bldev = ddata->bldev; + if (bldev != NULL) { + bldev->props.power = FB_BLANK_POWERDOWN; + dsicm_bl_update_status(bldev); + backlight_device_unregister(bldev); + } + + omap_dss_put_device(ddata->in); + + dsicm_cancel_ulps_work(ddata); + destroy_workqueue(ddata->workqueue); + + /* reset, to be sure that the panel is in a valid state */ + dsicm_hw_reset(ddata); + + return 0; +} + +static struct platform_driver dsicm_driver = { + .probe = dsicm_probe, + .remove = __exit_p(dsicm_remove), + .driver = { + .name = "panel-dsi-cm", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(dsicm_driver); + +MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); +MODULE_DESCRIPTION("Generic DSI Command Mode Panel Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays-new/panel-lgphilips-lb035q02.c b/drivers/video/omap2/displays-new/panel-lgphilips-lb035q02.c new file mode 100644 index 000000000000..6e8977b18950 --- /dev/null +++ b/drivers/video/omap2/displays-new/panel-lgphilips-lb035q02.c @@ -0,0 +1,358 @@ +/* + * LG.Philips LB035Q02 LCD Panel driver + * + * Copyright (C) 2013 Texas Instruments + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> + * Based on a driver by: Steve Sakoman <steve@sakoman.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/spi/spi.h> +#include <linux/mutex.h> +#include <linux/gpio.h> + +#include <video/omapdss.h> +#include <video/omap-panel-data.h> + +static struct omap_video_timings lb035q02_timings = { + .x_res = 320, + .y_res = 240, + + .pixel_clock = 6500, + + .hsw = 2, + .hfp = 20, + .hbp = 68, + + .vsw = 2, + .vfp = 4, + .vbp = 18, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, +}; + +struct panel_drv_data { + struct omap_dss_device dssdev; + struct omap_dss_device *in; + + struct spi_device *spi; + + int data_lines; + + struct omap_video_timings videomode; + + int reset_gpio; + int backlight_gpio; + int enable_gpio; +}; + +#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) + +static int lb035q02_write_reg(struct spi_device *spi, u8 reg, u16 val) +{ + struct spi_message msg; + struct spi_transfer index_xfer = { + .len = 3, + .cs_change = 1, + }; + struct spi_transfer value_xfer = { + .len = 3, + }; + u8 buffer[16]; + + spi_message_init(&msg); + + /* register index */ + buffer[0] = 0x70; + buffer[1] = 0x00; + buffer[2] = reg & 0x7f; + index_xfer.tx_buf = buffer; + spi_message_add_tail(&index_xfer, &msg); + + /* register value */ + buffer[4] = 0x72; + buffer[5] = val >> 8; + buffer[6] = val; + value_xfer.tx_buf = buffer + 4; + spi_message_add_tail(&value_xfer, &msg); + + return spi_sync(spi, &msg); +} + +static void init_lb035q02_panel(struct spi_device *spi) +{ + /* Init sequence from page 28 of the lb035q02 spec */ + lb035q02_write_reg(spi, 0x01, 0x6300); + lb035q02_write_reg(spi, 0x02, 0x0200); + lb035q02_write_reg(spi, 0x03, 0x0177); + lb035q02_write_reg(spi, 0x04, 0x04c7); + lb035q02_write_reg(spi, 0x05, 0xffc0); + lb035q02_write_reg(spi, 0x06, 0xe806); + lb035q02_write_reg(spi, 0x0a, 0x4008); + lb035q02_write_reg(spi, 0x0b, 0x0000); + lb035q02_write_reg(spi, 0x0d, 0x0030); + lb035q02_write_reg(spi, 0x0e, 0x2800); + lb035q02_write_reg(spi, 0x0f, 0x0000); + lb035q02_write_reg(spi, 0x16, 0x9f80); + lb035q02_write_reg(spi, 0x17, 0x0a0f); + lb035q02_write_reg(spi, 0x1e, 0x00c1); + lb035q02_write_reg(spi, 0x30, 0x0300); + lb035q02_write_reg(spi, 0x31, 0x0007); + lb035q02_write_reg(spi, 0x32, 0x0000); + lb035q02_write_reg(spi, 0x33, 0x0000); + lb035q02_write_reg(spi, 0x34, 0x0707); + lb035q02_write_reg(spi, 0x35, 0x0004); + lb035q02_write_reg(spi, 0x36, 0x0302); + lb035q02_write_reg(spi, 0x37, 0x0202); + lb035q02_write_reg(spi, 0x3a, 0x0a0d); + lb035q02_write_reg(spi, 0x3b, 0x0806); +} + +static int lb035q02_connect(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + if (omapdss_device_is_connected(dssdev)) + return 0; + + r = in->ops.dpi->connect(in, dssdev); + if (r) + return r; + + init_lb035q02_panel(ddata->spi); + + return 0; +} + +static void lb035q02_disconnect(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + if (!omapdss_device_is_connected(dssdev)) + return; + + in->ops.dpi->disconnect(in, dssdev); +} + +static int lb035q02_enable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + if (!omapdss_device_is_connected(dssdev)) + return -ENODEV; + + if (omapdss_device_is_enabled(dssdev)) + return 0; + + in->ops.dpi->set_data_lines(in, ddata->data_lines); + in->ops.dpi->set_timings(in, &ddata->videomode); + + r = in->ops.dpi->enable(in); + if (r) + return r; + + if (gpio_is_valid(ddata->enable_gpio)) + gpio_set_value_cansleep(ddata->enable_gpio, 1); + + if (gpio_is_valid(ddata->backlight_gpio)) + gpio_set_value_cansleep(ddata->backlight_gpio, 1); + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + return 0; +} + +static void lb035q02_disable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + if (!omapdss_device_is_enabled(dssdev)) + return; + + if (gpio_is_valid(ddata->enable_gpio)) + gpio_set_value_cansleep(ddata->enable_gpio, 0); + + if (gpio_is_valid(ddata->backlight_gpio)) + gpio_set_value_cansleep(ddata->backlight_gpio, 0); + + in->ops.dpi->disable(in); + + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; +} + +static void lb035q02_set_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + ddata->videomode = *timings; + dssdev->panel.timings = *timings; + + in->ops.dpi->set_timings(in, timings); +} + +static void lb035q02_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + + *timings = ddata->videomode; +} + +static int lb035q02_check_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + return in->ops.dpi->check_timings(in, timings); +} + +static struct omap_dss_driver lb035q02_ops = { + .connect = lb035q02_connect, + .disconnect = lb035q02_disconnect, + + .enable = lb035q02_enable, + .disable = lb035q02_disable, + + .set_timings = lb035q02_set_timings, + .get_timings = lb035q02_get_timings, + .check_timings = lb035q02_check_timings, + + .get_resolution = omapdss_default_get_resolution, +}; + +static int lb035q02_probe_pdata(struct spi_device *spi) +{ + const struct panel_lb035q02_platform_data *pdata; + struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); + struct omap_dss_device *dssdev, *in; + + pdata = dev_get_platdata(&spi->dev); + + in = omap_dss_find_output(pdata->source); + if (in == NULL) { + dev_err(&spi->dev, "failed to find video source '%s'\n", + pdata->source); + return -EPROBE_DEFER; + } + + ddata->in = in; + + ddata->data_lines = pdata->data_lines; + + dssdev = &ddata->dssdev; + dssdev->name = pdata->name; + + ddata->enable_gpio = pdata->enable_gpio; + ddata->backlight_gpio = pdata->backlight_gpio; + + return 0; +} + +static int lb035q02_panel_spi_probe(struct spi_device *spi) +{ + struct panel_drv_data *ddata; + struct omap_dss_device *dssdev; + int r; + + ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL); + if (ddata == NULL) + return -ENOMEM; + + dev_set_drvdata(&spi->dev, ddata); + + ddata->spi = spi; + + if (dev_get_platdata(&spi->dev)) { + r = lb035q02_probe_pdata(spi); + if (r) + return r; + } else { + return -ENODEV; + } + + if (gpio_is_valid(ddata->enable_gpio)) { + r = devm_gpio_request_one(&spi->dev, ddata->enable_gpio, + GPIOF_OUT_INIT_LOW, "panel enable"); + if (r) + goto err_gpio; + } + + if (gpio_is_valid(ddata->backlight_gpio)) { + r = devm_gpio_request_one(&spi->dev, ddata->backlight_gpio, + GPIOF_OUT_INIT_LOW, "panel backlight"); + if (r) + goto err_gpio; + } + + ddata->videomode = lb035q02_timings; + + dssdev = &ddata->dssdev; + dssdev->dev = &spi->dev; + dssdev->driver = &lb035q02_ops; + dssdev->type = OMAP_DISPLAY_TYPE_DPI; + dssdev->owner = THIS_MODULE; + dssdev->panel.timings = ddata->videomode; + dssdev->phy.dpi.data_lines = ddata->data_lines; + + r = omapdss_register_display(dssdev); + if (r) { + dev_err(&spi->dev, "Failed to register panel\n"); + goto err_reg; + } + + return 0; + +err_reg: +err_gpio: + omap_dss_put_device(ddata->in); + return r; +} + +static int lb035q02_panel_spi_remove(struct spi_device *spi) +{ + struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); + struct omap_dss_device *dssdev = &ddata->dssdev; + struct omap_dss_device *in = ddata->in; + + omapdss_unregister_display(dssdev); + + lb035q02_disable(dssdev); + lb035q02_disconnect(dssdev); + + omap_dss_put_device(in); + + return 0; +} + +static struct spi_driver lb035q02_spi_driver = { + .probe = lb035q02_panel_spi_probe, + .remove = lb035q02_panel_spi_remove, + .driver = { + .name = "panel_lgphilips_lb035q02", + .owner = THIS_MODULE, + }, +}; + +module_spi_driver(lb035q02_spi_driver); + +MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); +MODULE_DESCRIPTION("LG.Philips LB035Q02 LCD Panel driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays-new/panel-nec-nl8048hl11.c b/drivers/video/omap2/displays-new/panel-nec-nl8048hl11.c new file mode 100644 index 000000000000..bb217da65c5f --- /dev/null +++ b/drivers/video/omap2/displays-new/panel-nec-nl8048hl11.c @@ -0,0 +1,394 @@ +/* + * NEC NL8048HL11 Panel driver + * + * Copyright (C) 2010 Texas Instruments Inc. + * Author: Erik Gilling <konkers@android.com> + * Converted to new DSS device model: Tomi Valkeinen <tomi.valkeinen@ti.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/spi/spi.h> +#include <linux/fb.h> +#include <linux/gpio.h> + +#include <video/omapdss.h> +#include <video/omap-panel-data.h> + +struct panel_drv_data { + struct omap_dss_device dssdev; + struct omap_dss_device *in; + + struct omap_video_timings videomode; + + int data_lines; + + int res_gpio; + int qvga_gpio; + + struct spi_device *spi; +}; + +#define LCD_XRES 800 +#define LCD_YRES 480 +/* + * NEC PIX Clock Ratings + * MIN:21.8MHz TYP:23.8MHz MAX:25.7MHz + */ +#define LCD_PIXEL_CLOCK 23800 + +static const struct { + unsigned char addr; + unsigned char dat; +} nec_8048_init_seq[] = { + { 3, 0x01 }, { 0, 0x00 }, { 1, 0x01 }, { 4, 0x00 }, { 5, 0x14 }, + { 6, 0x24 }, { 16, 0xD7 }, { 17, 0x00 }, { 18, 0x00 }, { 19, 0x55 }, + { 20, 0x01 }, { 21, 0x70 }, { 22, 0x1E }, { 23, 0x25 }, { 24, 0x25 }, + { 25, 0x02 }, { 26, 0x02 }, { 27, 0xA0 }, { 32, 0x2F }, { 33, 0x0F }, + { 34, 0x0F }, { 35, 0x0F }, { 36, 0x0F }, { 37, 0x0F }, { 38, 0x0F }, + { 39, 0x00 }, { 40, 0x02 }, { 41, 0x02 }, { 42, 0x02 }, { 43, 0x0F }, + { 44, 0x0F }, { 45, 0x0F }, { 46, 0x0F }, { 47, 0x0F }, { 48, 0x0F }, + { 49, 0x0F }, { 50, 0x00 }, { 51, 0x02 }, { 52, 0x02 }, { 53, 0x02 }, + { 80, 0x0C }, { 83, 0x42 }, { 84, 0x42 }, { 85, 0x41 }, { 86, 0x14 }, + { 89, 0x88 }, { 90, 0x01 }, { 91, 0x00 }, { 92, 0x02 }, { 93, 0x0C }, + { 94, 0x1C }, { 95, 0x27 }, { 98, 0x49 }, { 99, 0x27 }, { 102, 0x76 }, + { 103, 0x27 }, { 112, 0x01 }, { 113, 0x0E }, { 114, 0x02 }, + { 115, 0x0C }, { 118, 0x0C }, { 121, 0x30 }, { 130, 0x00 }, + { 131, 0x00 }, { 132, 0xFC }, { 134, 0x00 }, { 136, 0x00 }, + { 138, 0x00 }, { 139, 0x00 }, { 140, 0x00 }, { 141, 0xFC }, + { 143, 0x00 }, { 145, 0x00 }, { 147, 0x00 }, { 148, 0x00 }, + { 149, 0x00 }, { 150, 0xFC }, { 152, 0x00 }, { 154, 0x00 }, + { 156, 0x00 }, { 157, 0x00 }, { 2, 0x00 }, +}; + +static const struct omap_video_timings nec_8048_panel_timings = { + .x_res = LCD_XRES, + .y_res = LCD_YRES, + .pixel_clock = LCD_PIXEL_CLOCK, + .hfp = 6, + .hsw = 1, + .hbp = 4, + .vfp = 3, + .vsw = 1, + .vbp = 4, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, +}; + +#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) + +static int nec_8048_spi_send(struct spi_device *spi, unsigned char reg_addr, + unsigned char reg_data) +{ + int ret = 0; + unsigned int cmd = 0, data = 0; + + cmd = 0x0000 | reg_addr; /* register address write */ + data = 0x0100 | reg_data; /* register data write */ + data = (cmd << 16) | data; + + ret = spi_write(spi, (unsigned char *)&data, 4); + if (ret) + pr_err("error in spi_write %x\n", data); + + return ret; +} + +static int init_nec_8048_wvga_lcd(struct spi_device *spi) +{ + unsigned int i; + /* Initialization Sequence */ + /* nec_8048_spi_send(spi, REG, VAL) */ + for (i = 0; i < (ARRAY_SIZE(nec_8048_init_seq) - 1); i++) + nec_8048_spi_send(spi, nec_8048_init_seq[i].addr, + nec_8048_init_seq[i].dat); + udelay(20); + nec_8048_spi_send(spi, nec_8048_init_seq[i].addr, + nec_8048_init_seq[i].dat); + return 0; +} + +static int nec_8048_connect(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + if (omapdss_device_is_connected(dssdev)) + return 0; + + r = in->ops.dpi->connect(in, dssdev); + if (r) + return r; + + return 0; +} + +static void nec_8048_disconnect(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + if (!omapdss_device_is_connected(dssdev)) + return; + + in->ops.dpi->disconnect(in, dssdev); +} + +static int nec_8048_enable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + if (!omapdss_device_is_connected(dssdev)) + return -ENODEV; + + if (omapdss_device_is_enabled(dssdev)) + return 0; + + in->ops.dpi->set_data_lines(in, ddata->data_lines); + in->ops.dpi->set_timings(in, &ddata->videomode); + + r = in->ops.dpi->enable(in); + if (r) + return r; + + if (gpio_is_valid(ddata->res_gpio)) + gpio_set_value_cansleep(ddata->res_gpio, 1); + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + return 0; +} + +static void nec_8048_disable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + if (!omapdss_device_is_enabled(dssdev)) + return; + + if (gpio_is_valid(ddata->res_gpio)) + gpio_set_value_cansleep(ddata->res_gpio, 0); + + in->ops.dpi->disable(in); + + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; +} + +static void nec_8048_set_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + ddata->videomode = *timings; + dssdev->panel.timings = *timings; + + in->ops.dpi->set_timings(in, timings); +} + +static void nec_8048_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + + *timings = ddata->videomode; +} + +static int nec_8048_check_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + return in->ops.dpi->check_timings(in, timings); +} + +static struct omap_dss_driver nec_8048_ops = { + .connect = nec_8048_connect, + .disconnect = nec_8048_disconnect, + + .enable = nec_8048_enable, + .disable = nec_8048_disable, + + .set_timings = nec_8048_set_timings, + .get_timings = nec_8048_get_timings, + .check_timings = nec_8048_check_timings, + + .get_resolution = omapdss_default_get_resolution, +}; + + +static int nec_8048_probe_pdata(struct spi_device *spi) +{ + const struct panel_nec_nl8048hl11_platform_data *pdata; + struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); + struct omap_dss_device *dssdev, *in; + + pdata = dev_get_platdata(&spi->dev); + + ddata->qvga_gpio = pdata->qvga_gpio; + ddata->res_gpio = pdata->res_gpio; + + in = omap_dss_find_output(pdata->source); + if (in == NULL) { + dev_err(&spi->dev, "failed to find video source '%s'\n", + pdata->source); + return -EPROBE_DEFER; + } + ddata->in = in; + + ddata->data_lines = pdata->data_lines; + + dssdev = &ddata->dssdev; + dssdev->name = pdata->name; + + return 0; +} + +static int nec_8048_probe(struct spi_device *spi) +{ + struct panel_drv_data *ddata; + struct omap_dss_device *dssdev; + int r; + + dev_dbg(&spi->dev, "%s\n", __func__); + + spi->mode = SPI_MODE_0; + spi->bits_per_word = 32; + + r = spi_setup(spi); + if (r < 0) { + dev_err(&spi->dev, "spi_setup failed: %d\n", r); + return r; + } + + init_nec_8048_wvga_lcd(spi); + + ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL); + if (ddata == NULL) + return -ENOMEM; + + dev_set_drvdata(&spi->dev, ddata); + + ddata->spi = spi; + + if (dev_get_platdata(&spi->dev)) { + r = nec_8048_probe_pdata(spi); + if (r) + return r; + } else { + return -ENODEV; + } + + if (gpio_is_valid(ddata->qvga_gpio)) { + r = devm_gpio_request_one(&spi->dev, ddata->qvga_gpio, + GPIOF_OUT_INIT_HIGH, "lcd QVGA"); + if (r) + goto err_gpio; + } + + if (gpio_is_valid(ddata->res_gpio)) { + r = devm_gpio_request_one(&spi->dev, ddata->res_gpio, + GPIOF_OUT_INIT_LOW, "lcd RES"); + if (r) + goto err_gpio; + } + + ddata->videomode = nec_8048_panel_timings; + + dssdev = &ddata->dssdev; + dssdev->dev = &spi->dev; + dssdev->driver = &nec_8048_ops; + dssdev->type = OMAP_DISPLAY_TYPE_DPI; + dssdev->owner = THIS_MODULE; + dssdev->panel.timings = ddata->videomode; + + r = omapdss_register_display(dssdev); + if (r) { + dev_err(&spi->dev, "Failed to register panel\n"); + goto err_reg; + } + + return 0; + +err_reg: +err_gpio: + omap_dss_put_device(ddata->in); + return r; +} + +static int nec_8048_remove(struct spi_device *spi) +{ + struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); + struct omap_dss_device *dssdev = &ddata->dssdev; + struct omap_dss_device *in = ddata->in; + + dev_dbg(&ddata->spi->dev, "%s\n", __func__); + + omapdss_unregister_display(dssdev); + + nec_8048_disable(dssdev); + nec_8048_disconnect(dssdev); + + omap_dss_put_device(in); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int nec_8048_suspend(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + + nec_8048_spi_send(spi, 2, 0x01); + mdelay(40); + + return 0; +} + +static int nec_8048_resume(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + + /* reinitialize the panel */ + spi_setup(spi); + nec_8048_spi_send(spi, 2, 0x00); + init_nec_8048_wvga_lcd(spi); + + return 0; +} +static SIMPLE_DEV_PM_OPS(nec_8048_pm_ops, nec_8048_suspend, + nec_8048_resume); +#define NEC_8048_PM_OPS (&nec_8048_pm_ops) +#else +#define NEC_8048_PM_OPS NULL +#endif + +static struct spi_driver nec_8048_driver = { + .driver = { + .name = "panel-nec-nl8048hl11", + .owner = THIS_MODULE, + .pm = NEC_8048_PM_OPS, + }, + .probe = nec_8048_probe, + .remove = nec_8048_remove, +}; + +module_spi_driver(nec_8048_driver); + +MODULE_AUTHOR("Erik Gilling <konkers@android.com>"); +MODULE_DESCRIPTION("NEC-NL8048HL11 Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays-new/panel-sharp-ls037v7dw01.c b/drivers/video/omap2/displays-new/panel-sharp-ls037v7dw01.c new file mode 100644 index 000000000000..72a4fb5aa6b1 --- /dev/null +++ b/drivers/video/omap2/displays-new/panel-sharp-ls037v7dw01.c @@ -0,0 +1,324 @@ +/* + * LCD panel driver for Sharp LS037V7DW01 + * + * Copyright (C) 2013 Texas Instruments + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include <video/omapdss.h> +#include <video/omap-panel-data.h> + +struct panel_drv_data { + struct omap_dss_device dssdev; + struct omap_dss_device *in; + + int data_lines; + + struct omap_video_timings videomode; + + int resb_gpio; + int ini_gpio; + int mo_gpio; + int lr_gpio; + int ud_gpio; +}; + +static const struct omap_video_timings sharp_ls_timings = { + .x_res = 480, + .y_res = 640, + + .pixel_clock = 19200, + + .hsw = 2, + .hfp = 1, + .hbp = 28, + + .vsw = 1, + .vfp = 1, + .vbp = 1, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, +}; + +#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) + +static int sharp_ls_connect(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + if (omapdss_device_is_connected(dssdev)) + return 0; + + r = in->ops.dpi->connect(in, dssdev); + if (r) + return r; + + return 0; +} + +static void sharp_ls_disconnect(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + if (!omapdss_device_is_connected(dssdev)) + return; + + in->ops.dpi->disconnect(in, dssdev); +} + +static int sharp_ls_enable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + if (!omapdss_device_is_connected(dssdev)) + return -ENODEV; + + if (omapdss_device_is_enabled(dssdev)) + return 0; + + in->ops.dpi->set_data_lines(in, ddata->data_lines); + in->ops.dpi->set_timings(in, &ddata->videomode); + + r = in->ops.dpi->enable(in); + if (r) + return r; + + /* wait couple of vsyncs until enabling the LCD */ + msleep(50); + + if (gpio_is_valid(ddata->resb_gpio)) + gpio_set_value_cansleep(ddata->resb_gpio, 1); + + if (gpio_is_valid(ddata->ini_gpio)) + gpio_set_value_cansleep(ddata->ini_gpio, 1); + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + return 0; +} + +static void sharp_ls_disable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + if (!omapdss_device_is_enabled(dssdev)) + return; + + if (gpio_is_valid(ddata->ini_gpio)) + gpio_set_value_cansleep(ddata->ini_gpio, 0); + + if (gpio_is_valid(ddata->resb_gpio)) + gpio_set_value_cansleep(ddata->resb_gpio, 0); + + /* wait at least 5 vsyncs after disabling the LCD */ + + msleep(100); + + in->ops.dpi->disable(in); + + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; +} + +static void sharp_ls_set_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + ddata->videomode = *timings; + dssdev->panel.timings = *timings; + + in->ops.dpi->set_timings(in, timings); +} + +static void sharp_ls_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + + *timings = ddata->videomode; +} + +static int sharp_ls_check_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + return in->ops.dpi->check_timings(in, timings); +} + +static struct omap_dss_driver sharp_ls_ops = { + .connect = sharp_ls_connect, + .disconnect = sharp_ls_disconnect, + + .enable = sharp_ls_enable, + .disable = sharp_ls_disable, + + .set_timings = sharp_ls_set_timings, + .get_timings = sharp_ls_get_timings, + .check_timings = sharp_ls_check_timings, + + .get_resolution = omapdss_default_get_resolution, +}; + +static int sharp_ls_probe_pdata(struct platform_device *pdev) +{ + const struct panel_sharp_ls037v7dw01_platform_data *pdata; + struct panel_drv_data *ddata = platform_get_drvdata(pdev); + struct omap_dss_device *dssdev, *in; + + pdata = dev_get_platdata(&pdev->dev); + + in = omap_dss_find_output(pdata->source); + if (in == NULL) { + dev_err(&pdev->dev, "failed to find video source '%s'\n", + pdata->source); + return -EPROBE_DEFER; + } + + ddata->in = in; + + ddata->data_lines = pdata->data_lines; + + dssdev = &ddata->dssdev; + dssdev->name = pdata->name; + + ddata->resb_gpio = pdata->resb_gpio; + ddata->ini_gpio = pdata->ini_gpio; + ddata->mo_gpio = pdata->mo_gpio; + ddata->lr_gpio = pdata->lr_gpio; + ddata->ud_gpio = pdata->ud_gpio; + + return 0; +} + +static int sharp_ls_probe(struct platform_device *pdev) +{ + struct panel_drv_data *ddata; + struct omap_dss_device *dssdev; + int r; + + ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); + if (ddata == NULL) + return -ENOMEM; + + platform_set_drvdata(pdev, ddata); + + if (dev_get_platdata(&pdev->dev)) { + r = sharp_ls_probe_pdata(pdev); + if (r) + return r; + } else { + return -ENODEV; + } + + if (gpio_is_valid(ddata->mo_gpio)) { + r = devm_gpio_request_one(&pdev->dev, ddata->mo_gpio, + GPIOF_OUT_INIT_LOW, "lcd MO"); + if (r) + goto err_gpio; + } + + if (gpio_is_valid(ddata->lr_gpio)) { + r = devm_gpio_request_one(&pdev->dev, ddata->lr_gpio, + GPIOF_OUT_INIT_HIGH, "lcd LR"); + if (r) + goto err_gpio; + } + + if (gpio_is_valid(ddata->ud_gpio)) { + r = devm_gpio_request_one(&pdev->dev, ddata->ud_gpio, + GPIOF_OUT_INIT_HIGH, "lcd UD"); + if (r) + goto err_gpio; + } + + if (gpio_is_valid(ddata->resb_gpio)) { + r = devm_gpio_request_one(&pdev->dev, ddata->resb_gpio, + GPIOF_OUT_INIT_LOW, "lcd RESB"); + if (r) + goto err_gpio; + } + + if (gpio_is_valid(ddata->ini_gpio)) { + r = devm_gpio_request_one(&pdev->dev, ddata->ini_gpio, + GPIOF_OUT_INIT_LOW, "lcd INI"); + if (r) + goto err_gpio; + } + + ddata->videomode = sharp_ls_timings; + + dssdev = &ddata->dssdev; + dssdev->dev = &pdev->dev; + dssdev->driver = &sharp_ls_ops; + dssdev->type = OMAP_DISPLAY_TYPE_DPI; + dssdev->owner = THIS_MODULE; + dssdev->panel.timings = ddata->videomode; + dssdev->phy.dpi.data_lines = ddata->data_lines; + + r = omapdss_register_display(dssdev); + if (r) { + dev_err(&pdev->dev, "Failed to register panel\n"); + goto err_reg; + } + + return 0; + +err_reg: +err_gpio: + omap_dss_put_device(ddata->in); + return r; +} + +static int __exit sharp_ls_remove(struct platform_device *pdev) +{ + struct panel_drv_data *ddata = platform_get_drvdata(pdev); + struct omap_dss_device *dssdev = &ddata->dssdev; + struct omap_dss_device *in = ddata->in; + + omapdss_unregister_display(dssdev); + + sharp_ls_disable(dssdev); + sharp_ls_disconnect(dssdev); + + omap_dss_put_device(in); + + return 0; +} + +static struct platform_driver sharp_ls_driver = { + .probe = sharp_ls_probe, + .remove = __exit_p(sharp_ls_remove), + .driver = { + .name = "panel-sharp-ls037v7dw01", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(sharp_ls_driver); + +MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); +MODULE_DESCRIPTION("Sharp LS037V7DW01 Panel Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays-new/panel-sony-acx565akm.c b/drivers/video/omap2/displays-new/panel-sony-acx565akm.c new file mode 100644 index 000000000000..e6d56f714ae4 --- /dev/null +++ b/drivers/video/omap2/displays-new/panel-sony-acx565akm.c @@ -0,0 +1,865 @@ +/* + * Sony ACX565AKM LCD Panel driver + * + * Copyright (C) 2010 Nokia Corporation + * + * Original Driver Author: Imre Deak <imre.deak@nokia.com> + * Based on panel-generic.c by Tomi Valkeinen <tomi.valkeinen@nokia.com> + * Adapted to new DSS2 framework: Roger Quadros <roger.quadros@nokia.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/spi/spi.h> +#include <linux/jiffies.h> +#include <linux/sched.h> +#include <linux/backlight.h> +#include <linux/fb.h> +#include <linux/gpio.h> + +#include <video/omapdss.h> +#include <video/omap-panel-data.h> + +#define MIPID_CMD_READ_DISP_ID 0x04 +#define MIPID_CMD_READ_RED 0x06 +#define MIPID_CMD_READ_GREEN 0x07 +#define MIPID_CMD_READ_BLUE 0x08 +#define MIPID_CMD_READ_DISP_STATUS 0x09 +#define MIPID_CMD_RDDSDR 0x0F +#define MIPID_CMD_SLEEP_IN 0x10 +#define MIPID_CMD_SLEEP_OUT 0x11 +#define MIPID_CMD_DISP_OFF 0x28 +#define MIPID_CMD_DISP_ON 0x29 +#define MIPID_CMD_WRITE_DISP_BRIGHTNESS 0x51 +#define MIPID_CMD_READ_DISP_BRIGHTNESS 0x52 +#define MIPID_CMD_WRITE_CTRL_DISP 0x53 + +#define CTRL_DISP_BRIGHTNESS_CTRL_ON (1 << 5) +#define CTRL_DISP_AMBIENT_LIGHT_CTRL_ON (1 << 4) +#define CTRL_DISP_BACKLIGHT_ON (1 << 2) +#define CTRL_DISP_AUTO_BRIGHTNESS_ON (1 << 1) + +#define MIPID_CMD_READ_CTRL_DISP 0x54 +#define MIPID_CMD_WRITE_CABC 0x55 +#define MIPID_CMD_READ_CABC 0x56 + +#define MIPID_VER_LPH8923 3 +#define MIPID_VER_LS041Y3 4 +#define MIPID_VER_L4F00311 8 +#define MIPID_VER_ACX565AKM 9 + +struct panel_drv_data { + struct omap_dss_device dssdev; + struct omap_dss_device *in; + + int reset_gpio; + int datapairs; + + struct omap_video_timings videomode; + + char *name; + int enabled; + int model; + int revision; + u8 display_id[3]; + unsigned has_bc:1; + unsigned has_cabc:1; + unsigned cabc_mode; + unsigned long hw_guard_end; /* next value of jiffies + when we can issue the + next sleep in/out command */ + unsigned long hw_guard_wait; /* max guard time in jiffies */ + + struct spi_device *spi; + struct mutex mutex; + + struct backlight_device *bl_dev; +}; + +static const struct omap_video_timings acx565akm_panel_timings = { + .x_res = 800, + .y_res = 480, + .pixel_clock = 24000, + .hfp = 28, + .hsw = 4, + .hbp = 24, + .vfp = 3, + .vsw = 3, + .vbp = 4, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, +}; + +#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) + +static void acx565akm_transfer(struct panel_drv_data *ddata, int cmd, + const u8 *wbuf, int wlen, u8 *rbuf, int rlen) +{ + struct spi_message m; + struct spi_transfer *x, xfer[5]; + int r; + + BUG_ON(ddata->spi == NULL); + + spi_message_init(&m); + + memset(xfer, 0, sizeof(xfer)); + x = &xfer[0]; + + cmd &= 0xff; + x->tx_buf = &cmd; + x->bits_per_word = 9; + x->len = 2; + + if (rlen > 1 && wlen == 0) { + /* + * Between the command and the response data there is a + * dummy clock cycle. Add an extra bit after the command + * word to account for this. + */ + x->bits_per_word = 10; + cmd <<= 1; + } + spi_message_add_tail(x, &m); + + if (wlen) { + x++; + x->tx_buf = wbuf; + x->len = wlen; + x->bits_per_word = 9; + spi_message_add_tail(x, &m); + } + + if (rlen) { + x++; + x->rx_buf = rbuf; + x->len = rlen; + spi_message_add_tail(x, &m); + } + + r = spi_sync(ddata->spi, &m); + if (r < 0) + dev_dbg(&ddata->spi->dev, "spi_sync %d\n", r); +} + +static inline void acx565akm_cmd(struct panel_drv_data *ddata, int cmd) +{ + acx565akm_transfer(ddata, cmd, NULL, 0, NULL, 0); +} + +static inline void acx565akm_write(struct panel_drv_data *ddata, + int reg, const u8 *buf, int len) +{ + acx565akm_transfer(ddata, reg, buf, len, NULL, 0); +} + +static inline void acx565akm_read(struct panel_drv_data *ddata, + int reg, u8 *buf, int len) +{ + acx565akm_transfer(ddata, reg, NULL, 0, buf, len); +} + +static void hw_guard_start(struct panel_drv_data *ddata, int guard_msec) +{ + ddata->hw_guard_wait = msecs_to_jiffies(guard_msec); + ddata->hw_guard_end = jiffies + ddata->hw_guard_wait; +} + +static void hw_guard_wait(struct panel_drv_data *ddata) +{ + unsigned long wait = ddata->hw_guard_end - jiffies; + + if ((long)wait > 0 && wait <= ddata->hw_guard_wait) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(wait); + } +} + +static void set_sleep_mode(struct panel_drv_data *ddata, int on) +{ + int cmd; + + if (on) + cmd = MIPID_CMD_SLEEP_IN; + else + cmd = MIPID_CMD_SLEEP_OUT; + /* + * We have to keep 120msec between sleep in/out commands. + * (8.2.15, 8.2.16). + */ + hw_guard_wait(ddata); + acx565akm_cmd(ddata, cmd); + hw_guard_start(ddata, 120); +} + +static void set_display_state(struct panel_drv_data *ddata, int enabled) +{ + int cmd = enabled ? MIPID_CMD_DISP_ON : MIPID_CMD_DISP_OFF; + + acx565akm_cmd(ddata, cmd); +} + +static int panel_enabled(struct panel_drv_data *ddata) +{ + u32 disp_status; + int enabled; + + acx565akm_read(ddata, MIPID_CMD_READ_DISP_STATUS, + (u8 *)&disp_status, 4); + disp_status = __be32_to_cpu(disp_status); + enabled = (disp_status & (1 << 17)) && (disp_status & (1 << 10)); + dev_dbg(&ddata->spi->dev, + "LCD panel %senabled by bootloader (status 0x%04x)\n", + enabled ? "" : "not ", disp_status); + return enabled; +} + +static int panel_detect(struct panel_drv_data *ddata) +{ + acx565akm_read(ddata, MIPID_CMD_READ_DISP_ID, ddata->display_id, 3); + dev_dbg(&ddata->spi->dev, "MIPI display ID: %02x%02x%02x\n", + ddata->display_id[0], + ddata->display_id[1], + ddata->display_id[2]); + + switch (ddata->display_id[0]) { + case 0x10: + ddata->model = MIPID_VER_ACX565AKM; + ddata->name = "acx565akm"; + ddata->has_bc = 1; + ddata->has_cabc = 1; + break; + case 0x29: + ddata->model = MIPID_VER_L4F00311; + ddata->name = "l4f00311"; + break; + case 0x45: + ddata->model = MIPID_VER_LPH8923; + ddata->name = "lph8923"; + break; + case 0x83: + ddata->model = MIPID_VER_LS041Y3; + ddata->name = "ls041y3"; + break; + default: + ddata->name = "unknown"; + dev_err(&ddata->spi->dev, "invalid display ID\n"); + return -ENODEV; + } + + ddata->revision = ddata->display_id[1]; + + dev_info(&ddata->spi->dev, "omapfb: %s rev %02x LCD detected\n", + ddata->name, ddata->revision); + + return 0; +} + +/*----------------------Backlight Control-------------------------*/ + +static void enable_backlight_ctrl(struct panel_drv_data *ddata, int enable) +{ + u16 ctrl; + + acx565akm_read(ddata, MIPID_CMD_READ_CTRL_DISP, (u8 *)&ctrl, 1); + if (enable) { + ctrl |= CTRL_DISP_BRIGHTNESS_CTRL_ON | + CTRL_DISP_BACKLIGHT_ON; + } else { + ctrl &= ~(CTRL_DISP_BRIGHTNESS_CTRL_ON | + CTRL_DISP_BACKLIGHT_ON); + } + + ctrl |= 1 << 8; + acx565akm_write(ddata, MIPID_CMD_WRITE_CTRL_DISP, (u8 *)&ctrl, 2); +} + +static void set_cabc_mode(struct panel_drv_data *ddata, unsigned mode) +{ + u16 cabc_ctrl; + + ddata->cabc_mode = mode; + if (!ddata->enabled) + return; + cabc_ctrl = 0; + acx565akm_read(ddata, MIPID_CMD_READ_CABC, (u8 *)&cabc_ctrl, 1); + cabc_ctrl &= ~3; + cabc_ctrl |= (1 << 8) | (mode & 3); + acx565akm_write(ddata, MIPID_CMD_WRITE_CABC, (u8 *)&cabc_ctrl, 2); +} + +static unsigned get_cabc_mode(struct panel_drv_data *ddata) +{ + return ddata->cabc_mode; +} + +static unsigned get_hw_cabc_mode(struct panel_drv_data *ddata) +{ + u8 cabc_ctrl; + + acx565akm_read(ddata, MIPID_CMD_READ_CABC, &cabc_ctrl, 1); + return cabc_ctrl & 3; +} + +static void acx565akm_set_brightness(struct panel_drv_data *ddata, int level) +{ + int bv; + + bv = level | (1 << 8); + acx565akm_write(ddata, MIPID_CMD_WRITE_DISP_BRIGHTNESS, (u8 *)&bv, 2); + + if (level) + enable_backlight_ctrl(ddata, 1); + else + enable_backlight_ctrl(ddata, 0); +} + +static int acx565akm_get_actual_brightness(struct panel_drv_data *ddata) +{ + u8 bv; + + acx565akm_read(ddata, MIPID_CMD_READ_DISP_BRIGHTNESS, &bv, 1); + + return bv; +} + + +static int acx565akm_bl_update_status(struct backlight_device *dev) +{ + struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev); + int r; + int level; + + dev_dbg(&ddata->spi->dev, "%s\n", __func__); + + mutex_lock(&ddata->mutex); + + if (dev->props.fb_blank == FB_BLANK_UNBLANK && + dev->props.power == FB_BLANK_UNBLANK) + level = dev->props.brightness; + else + level = 0; + + r = 0; + if (ddata->has_bc) + acx565akm_set_brightness(ddata, level); + else + r = -ENODEV; + + mutex_unlock(&ddata->mutex); + + return r; +} + +static int acx565akm_bl_get_intensity(struct backlight_device *dev) +{ + struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev); + + dev_dbg(&dev->dev, "%s\n", __func__); + + if (!ddata->has_bc) + return -ENODEV; + + if (dev->props.fb_blank == FB_BLANK_UNBLANK && + dev->props.power == FB_BLANK_UNBLANK) { + if (ddata->has_bc) + return acx565akm_get_actual_brightness(ddata); + else + return dev->props.brightness; + } + + return 0; +} + +static const struct backlight_ops acx565akm_bl_ops = { + .get_brightness = acx565akm_bl_get_intensity, + .update_status = acx565akm_bl_update_status, +}; + +/*--------------------Auto Brightness control via Sysfs---------------------*/ + +static const char * const cabc_modes[] = { + "off", /* always used when CABC is not supported */ + "ui", + "still-image", + "moving-image", +}; + +static ssize_t show_cabc_mode(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct panel_drv_data *ddata = dev_get_drvdata(dev); + const char *mode_str; + int mode; + int len; + + if (!ddata->has_cabc) + mode = 0; + else + mode = get_cabc_mode(ddata); + mode_str = "unknown"; + if (mode >= 0 && mode < ARRAY_SIZE(cabc_modes)) + mode_str = cabc_modes[mode]; + len = snprintf(buf, PAGE_SIZE, "%s\n", mode_str); + + return len < PAGE_SIZE - 1 ? len : PAGE_SIZE - 1; +} + +static ssize_t store_cabc_mode(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct panel_drv_data *ddata = dev_get_drvdata(dev); + int i; + + for (i = 0; i < ARRAY_SIZE(cabc_modes); i++) { + const char *mode_str = cabc_modes[i]; + int cmp_len = strlen(mode_str); + + if (count > 0 && buf[count - 1] == '\n') + count--; + if (count != cmp_len) + continue; + + if (strncmp(buf, mode_str, cmp_len) == 0) + break; + } + + if (i == ARRAY_SIZE(cabc_modes)) + return -EINVAL; + + if (!ddata->has_cabc && i != 0) + return -EINVAL; + + mutex_lock(&ddata->mutex); + set_cabc_mode(ddata, i); + mutex_unlock(&ddata->mutex); + + return count; +} + +static ssize_t show_cabc_available_modes(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct panel_drv_data *ddata = dev_get_drvdata(dev); + int len; + int i; + + if (!ddata->has_cabc) + return snprintf(buf, PAGE_SIZE, "%s\n", cabc_modes[0]); + + for (i = 0, len = 0; + len < PAGE_SIZE && i < ARRAY_SIZE(cabc_modes); i++) + len += snprintf(&buf[len], PAGE_SIZE - len, "%s%s%s", + i ? " " : "", cabc_modes[i], + i == ARRAY_SIZE(cabc_modes) - 1 ? "\n" : ""); + + return len < PAGE_SIZE ? len : PAGE_SIZE - 1; +} + +static DEVICE_ATTR(cabc_mode, S_IRUGO | S_IWUSR, + show_cabc_mode, store_cabc_mode); +static DEVICE_ATTR(cabc_available_modes, S_IRUGO, + show_cabc_available_modes, NULL); + +static struct attribute *bldev_attrs[] = { + &dev_attr_cabc_mode.attr, + &dev_attr_cabc_available_modes.attr, + NULL, +}; + +static struct attribute_group bldev_attr_group = { + .attrs = bldev_attrs, +}; + +static int acx565akm_connect(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + if (omapdss_device_is_connected(dssdev)) + return 0; + + r = in->ops.sdi->connect(in, dssdev); + if (r) + return r; + + return 0; +} + +static void acx565akm_disconnect(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + if (!omapdss_device_is_connected(dssdev)) + return; + + in->ops.sdi->disconnect(in, dssdev); +} + +static int acx565akm_panel_power_on(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + dev_dbg(&ddata->spi->dev, "%s\n", __func__); + + in->ops.sdi->set_timings(in, &ddata->videomode); + in->ops.sdi->set_datapairs(in, ddata->datapairs); + + r = in->ops.sdi->enable(in); + if (r) { + pr_err("%s sdi enable failed\n", __func__); + return r; + } + + /*FIXME tweak me */ + msleep(50); + + if (gpio_is_valid(ddata->reset_gpio)) + gpio_set_value(ddata->reset_gpio, 1); + + if (ddata->enabled) { + dev_dbg(&ddata->spi->dev, "panel already enabled\n"); + return 0; + } + + /* + * We have to meet all the following delay requirements: + * 1. tRW: reset pulse width 10usec (7.12.1) + * 2. tRT: reset cancel time 5msec (7.12.1) + * 3. Providing PCLK,HS,VS signals for 2 frames = ~50msec worst + * case (7.6.2) + * 4. 120msec before the sleep out command (7.12.1) + */ + msleep(120); + + set_sleep_mode(ddata, 0); + ddata->enabled = 1; + + /* 5msec between sleep out and the next command. (8.2.16) */ + usleep_range(5000, 10000); + set_display_state(ddata, 1); + set_cabc_mode(ddata, ddata->cabc_mode); + + mutex_unlock(&ddata->mutex); + + return acx565akm_bl_update_status(ddata->bl_dev); +} + +static void acx565akm_panel_power_off(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + dev_dbg(dssdev->dev, "%s\n", __func__); + + if (!ddata->enabled) + return; + + set_display_state(ddata, 0); + set_sleep_mode(ddata, 1); + ddata->enabled = 0; + /* + * We have to provide PCLK,HS,VS signals for 2 frames (worst case + * ~50msec) after sending the sleep in command and asserting the + * reset signal. We probably could assert the reset w/o the delay + * but we still delay to avoid possible artifacts. (7.6.1) + */ + msleep(50); + + if (gpio_is_valid(ddata->reset_gpio)) + gpio_set_value(ddata->reset_gpio, 0); + + /* FIXME need to tweak this delay */ + msleep(100); + + in->ops.sdi->disable(in); +} + +static int acx565akm_enable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + int r; + + dev_dbg(dssdev->dev, "%s\n", __func__); + + if (!omapdss_device_is_connected(dssdev)) + return -ENODEV; + + if (omapdss_device_is_enabled(dssdev)) + return 0; + + mutex_lock(&ddata->mutex); + r = acx565akm_panel_power_on(dssdev); + mutex_unlock(&ddata->mutex); + + if (r) + return r; + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + return 0; +} + +static void acx565akm_disable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + + dev_dbg(dssdev->dev, "%s\n", __func__); + + if (!omapdss_device_is_enabled(dssdev)) + return; + + mutex_lock(&ddata->mutex); + acx565akm_panel_power_off(dssdev); + mutex_unlock(&ddata->mutex); + + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; +} + +static void acx565akm_set_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + ddata->videomode = *timings; + dssdev->panel.timings = *timings; + + in->ops.sdi->set_timings(in, timings); +} + +static void acx565akm_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + + *timings = ddata->videomode; +} + +static int acx565akm_check_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + return in->ops.sdi->check_timings(in, timings); +} + +static struct omap_dss_driver acx565akm_ops = { + .connect = acx565akm_connect, + .disconnect = acx565akm_disconnect, + + .enable = acx565akm_enable, + .disable = acx565akm_disable, + + .set_timings = acx565akm_set_timings, + .get_timings = acx565akm_get_timings, + .check_timings = acx565akm_check_timings, + + .get_resolution = omapdss_default_get_resolution, +}; + +static int acx565akm_probe_pdata(struct spi_device *spi) +{ + const struct panel_acx565akm_platform_data *pdata; + struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); + struct omap_dss_device *dssdev, *in; + + pdata = dev_get_platdata(&spi->dev); + + ddata->reset_gpio = pdata->reset_gpio; + + in = omap_dss_find_output(pdata->source); + if (in == NULL) { + dev_err(&spi->dev, "failed to find video source '%s'\n", + pdata->source); + return -EPROBE_DEFER; + } + ddata->in = in; + + ddata->datapairs = pdata->datapairs; + + dssdev = &ddata->dssdev; + dssdev->name = pdata->name; + + return 0; +} + +static int acx565akm_probe(struct spi_device *spi) +{ + struct panel_drv_data *ddata; + struct omap_dss_device *dssdev; + struct backlight_device *bldev; + int max_brightness, brightness; + struct backlight_properties props; + int r; + + dev_dbg(&spi->dev, "%s\n", __func__); + + spi->mode = SPI_MODE_3; + + ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL); + if (ddata == NULL) + return -ENOMEM; + + dev_set_drvdata(&spi->dev, ddata); + + ddata->spi = spi; + + mutex_init(&ddata->mutex); + + if (dev_get_platdata(&spi->dev)) { + r = acx565akm_probe_pdata(spi); + if (r) + return r; + } else { + return -ENODEV; + } + + if (gpio_is_valid(ddata->reset_gpio)) { + r = devm_gpio_request_one(&spi->dev, ddata->reset_gpio, + GPIOF_OUT_INIT_LOW, "lcd reset"); + if (r) + goto err_gpio; + } + + if (gpio_is_valid(ddata->reset_gpio)) + gpio_set_value(ddata->reset_gpio, 1); + + /* + * After reset we have to wait 5 msec before the first + * command can be sent. + */ + usleep_range(5000, 10000); + + ddata->enabled = panel_enabled(ddata); + + r = panel_detect(ddata); + + if (!ddata->enabled && gpio_is_valid(ddata->reset_gpio)) + gpio_set_value(ddata->reset_gpio, 0); + + if (r) { + dev_err(&spi->dev, "%s panel detect error\n", __func__); + goto err_detect; + } + + memset(&props, 0, sizeof(props)); + props.fb_blank = FB_BLANK_UNBLANK; + props.power = FB_BLANK_UNBLANK; + props.type = BACKLIGHT_RAW; + + bldev = backlight_device_register("acx565akm", &ddata->spi->dev, + ddata, &acx565akm_bl_ops, &props); + ddata->bl_dev = bldev; + if (ddata->has_cabc) { + r = sysfs_create_group(&bldev->dev.kobj, &bldev_attr_group); + if (r) { + dev_err(&bldev->dev, + "%s failed to create sysfs files\n", __func__); + goto err_sysfs; + } + ddata->cabc_mode = get_hw_cabc_mode(ddata); + } + + max_brightness = 255; + + if (ddata->has_bc) + brightness = acx565akm_get_actual_brightness(ddata); + else + brightness = 0; + + bldev->props.max_brightness = max_brightness; + bldev->props.brightness = brightness; + + acx565akm_bl_update_status(bldev); + + + ddata->videomode = acx565akm_panel_timings; + + dssdev = &ddata->dssdev; + dssdev->dev = &spi->dev; + dssdev->driver = &acx565akm_ops; + dssdev->type = OMAP_DISPLAY_TYPE_SDI; + dssdev->owner = THIS_MODULE; + dssdev->panel.timings = ddata->videomode; + + r = omapdss_register_display(dssdev); + if (r) { + dev_err(&spi->dev, "Failed to register panel\n"); + goto err_reg; + } + + return 0; + +err_reg: + sysfs_remove_group(&bldev->dev.kobj, &bldev_attr_group); +err_sysfs: + backlight_device_unregister(bldev); +err_detect: +err_gpio: + omap_dss_put_device(ddata->in); + return r; +} + +static int acx565akm_remove(struct spi_device *spi) +{ + struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); + struct omap_dss_device *dssdev = &ddata->dssdev; + struct omap_dss_device *in = ddata->in; + + dev_dbg(&ddata->spi->dev, "%s\n", __func__); + + sysfs_remove_group(&ddata->bl_dev->dev.kobj, &bldev_attr_group); + backlight_device_unregister(ddata->bl_dev); + + omapdss_unregister_display(dssdev); + + acx565akm_disable(dssdev); + acx565akm_disconnect(dssdev); + + omap_dss_put_device(in); + + return 0; +} + +static struct spi_driver acx565akm_driver = { + .driver = { + .name = "acx565akm", + .owner = THIS_MODULE, + }, + .probe = acx565akm_probe, + .remove = acx565akm_remove, +}; + +module_spi_driver(acx565akm_driver); + +MODULE_AUTHOR("Nokia Corporation"); +MODULE_DESCRIPTION("acx565akm LCD Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays-new/panel-tpo-td043mtea1.c b/drivers/video/omap2/displays-new/panel-tpo-td043mtea1.c new file mode 100644 index 000000000000..eadc6529fa3d --- /dev/null +++ b/drivers/video/omap2/displays-new/panel-tpo-td043mtea1.c @@ -0,0 +1,646 @@ +/* + * TPO TD043MTEA1 Panel driver + * + * Author: GraĹľvydas Ignotas <notasas@gmail.com> + * Converted to new DSS device model: Tomi Valkeinen <tomi.valkeinen@ti.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/spi/spi.h> +#include <linux/regulator/consumer.h> +#include <linux/gpio.h> +#include <linux/err.h> +#include <linux/slab.h> + +#include <video/omapdss.h> +#include <video/omap-panel-data.h> + +#define TPO_R02_MODE(x) ((x) & 7) +#define TPO_R02_MODE_800x480 7 +#define TPO_R02_NCLK_RISING BIT(3) +#define TPO_R02_HSYNC_HIGH BIT(4) +#define TPO_R02_VSYNC_HIGH BIT(5) + +#define TPO_R03_NSTANDBY BIT(0) +#define TPO_R03_EN_CP_CLK BIT(1) +#define TPO_R03_EN_VGL_PUMP BIT(2) +#define TPO_R03_EN_PWM BIT(3) +#define TPO_R03_DRIVING_CAP_100 BIT(4) +#define TPO_R03_EN_PRE_CHARGE BIT(6) +#define TPO_R03_SOFTWARE_CTL BIT(7) + +#define TPO_R04_NFLIP_H BIT(0) +#define TPO_R04_NFLIP_V BIT(1) +#define TPO_R04_CP_CLK_FREQ_1H BIT(2) +#define TPO_R04_VGL_FREQ_1H BIT(4) + +#define TPO_R03_VAL_NORMAL (TPO_R03_NSTANDBY | TPO_R03_EN_CP_CLK | \ + TPO_R03_EN_VGL_PUMP | TPO_R03_EN_PWM | \ + TPO_R03_DRIVING_CAP_100 | TPO_R03_EN_PRE_CHARGE | \ + TPO_R03_SOFTWARE_CTL) + +#define TPO_R03_VAL_STANDBY (TPO_R03_DRIVING_CAP_100 | \ + TPO_R03_EN_PRE_CHARGE | TPO_R03_SOFTWARE_CTL) + +static const u16 tpo_td043_def_gamma[12] = { + 105, 315, 381, 431, 490, 537, 579, 686, 780, 837, 880, 1023 +}; + +struct panel_drv_data { + struct omap_dss_device dssdev; + struct omap_dss_device *in; + + struct omap_video_timings videomode; + + int data_lines; + + struct spi_device *spi; + struct regulator *vcc_reg; + int nreset_gpio; + u16 gamma[12]; + u32 mode; + u32 hmirror:1; + u32 vmirror:1; + u32 powered_on:1; + u32 spi_suspended:1; + u32 power_on_resume:1; +}; + +static const struct omap_video_timings tpo_td043_timings = { + .x_res = 800, + .y_res = 480, + + .pixel_clock = 36000, + + .hsw = 1, + .hfp = 68, + .hbp = 214, + + .vsw = 1, + .vfp = 39, + .vbp = 34, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, +}; + +#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) + +static int tpo_td043_write(struct spi_device *spi, u8 addr, u8 data) +{ + struct spi_message m; + struct spi_transfer xfer; + u16 w; + int r; + + spi_message_init(&m); + + memset(&xfer, 0, sizeof(xfer)); + + w = ((u16)addr << 10) | (1 << 8) | data; + xfer.tx_buf = &w; + xfer.bits_per_word = 16; + xfer.len = 2; + spi_message_add_tail(&xfer, &m); + + r = spi_sync(spi, &m); + if (r < 0) + dev_warn(&spi->dev, "failed to write to LCD reg (%d)\n", r); + return r; +} + +static void tpo_td043_write_gamma(struct spi_device *spi, u16 gamma[12]) +{ + u8 i, val; + + /* gamma bits [9:8] */ + for (val = i = 0; i < 4; i++) + val |= (gamma[i] & 0x300) >> ((i + 1) * 2); + tpo_td043_write(spi, 0x11, val); + + for (val = i = 0; i < 4; i++) + val |= (gamma[i+4] & 0x300) >> ((i + 1) * 2); + tpo_td043_write(spi, 0x12, val); + + for (val = i = 0; i < 4; i++) + val |= (gamma[i+8] & 0x300) >> ((i + 1) * 2); + tpo_td043_write(spi, 0x13, val); + + /* gamma bits [7:0] */ + for (val = i = 0; i < 12; i++) + tpo_td043_write(spi, 0x14 + i, gamma[i] & 0xff); +} + +static int tpo_td043_write_mirror(struct spi_device *spi, bool h, bool v) +{ + u8 reg4 = TPO_R04_NFLIP_H | TPO_R04_NFLIP_V | + TPO_R04_CP_CLK_FREQ_1H | TPO_R04_VGL_FREQ_1H; + if (h) + reg4 &= ~TPO_R04_NFLIP_H; + if (v) + reg4 &= ~TPO_R04_NFLIP_V; + + return tpo_td043_write(spi, 4, reg4); +} + +static int tpo_td043_set_hmirror(struct omap_dss_device *dssdev, bool enable) +{ + struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev); + + ddata->hmirror = enable; + return tpo_td043_write_mirror(ddata->spi, ddata->hmirror, + ddata->vmirror); +} + +static bool tpo_td043_get_hmirror(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev); + + return ddata->hmirror; +} + +static ssize_t tpo_td043_vmirror_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct panel_drv_data *ddata = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", ddata->vmirror); +} + +static ssize_t tpo_td043_vmirror_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct panel_drv_data *ddata = dev_get_drvdata(dev); + int val; + int ret; + + ret = kstrtoint(buf, 0, &val); + if (ret < 0) + return ret; + + val = !!val; + + ret = tpo_td043_write_mirror(ddata->spi, ddata->hmirror, val); + if (ret < 0) + return ret; + + ddata->vmirror = val; + + return count; +} + +static ssize_t tpo_td043_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct panel_drv_data *ddata = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", ddata->mode); +} + +static ssize_t tpo_td043_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct panel_drv_data *ddata = dev_get_drvdata(dev); + long val; + int ret; + + ret = kstrtol(buf, 0, &val); + if (ret != 0 || val & ~7) + return -EINVAL; + + ddata->mode = val; + + val |= TPO_R02_NCLK_RISING; + tpo_td043_write(ddata->spi, 2, val); + + return count; +} + +static ssize_t tpo_td043_gamma_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct panel_drv_data *ddata = dev_get_drvdata(dev); + ssize_t len = 0; + int ret; + int i; + + for (i = 0; i < ARRAY_SIZE(ddata->gamma); i++) { + ret = snprintf(buf + len, PAGE_SIZE - len, "%u ", + ddata->gamma[i]); + if (ret < 0) + return ret; + len += ret; + } + buf[len - 1] = '\n'; + + return len; +} + +static ssize_t tpo_td043_gamma_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct panel_drv_data *ddata = dev_get_drvdata(dev); + unsigned int g[12]; + int ret; + int i; + + ret = sscanf(buf, "%u %u %u %u %u %u %u %u %u %u %u %u", + &g[0], &g[1], &g[2], &g[3], &g[4], &g[5], + &g[6], &g[7], &g[8], &g[9], &g[10], &g[11]); + + if (ret != 12) + return -EINVAL; + + for (i = 0; i < 12; i++) + ddata->gamma[i] = g[i]; + + tpo_td043_write_gamma(ddata->spi, ddata->gamma); + + return count; +} + +static DEVICE_ATTR(vmirror, S_IRUGO | S_IWUSR, + tpo_td043_vmirror_show, tpo_td043_vmirror_store); +static DEVICE_ATTR(mode, S_IRUGO | S_IWUSR, + tpo_td043_mode_show, tpo_td043_mode_store); +static DEVICE_ATTR(gamma, S_IRUGO | S_IWUSR, + tpo_td043_gamma_show, tpo_td043_gamma_store); + +static struct attribute *tpo_td043_attrs[] = { + &dev_attr_vmirror.attr, + &dev_attr_mode.attr, + &dev_attr_gamma.attr, + NULL, +}; + +static struct attribute_group tpo_td043_attr_group = { + .attrs = tpo_td043_attrs, +}; + +static int tpo_td043_power_on(struct panel_drv_data *ddata) +{ + int r; + + if (ddata->powered_on) + return 0; + + r = regulator_enable(ddata->vcc_reg); + if (r != 0) + return r; + + /* wait for panel to stabilize */ + msleep(160); + + if (gpio_is_valid(ddata->nreset_gpio)) + gpio_set_value(ddata->nreset_gpio, 1); + + tpo_td043_write(ddata->spi, 2, + TPO_R02_MODE(ddata->mode) | TPO_R02_NCLK_RISING); + tpo_td043_write(ddata->spi, 3, TPO_R03_VAL_NORMAL); + tpo_td043_write(ddata->spi, 0x20, 0xf0); + tpo_td043_write(ddata->spi, 0x21, 0xf0); + tpo_td043_write_mirror(ddata->spi, ddata->hmirror, + ddata->vmirror); + tpo_td043_write_gamma(ddata->spi, ddata->gamma); + + ddata->powered_on = 1; + return 0; +} + +static void tpo_td043_power_off(struct panel_drv_data *ddata) +{ + if (!ddata->powered_on) + return; + + tpo_td043_write(ddata->spi, 3, + TPO_R03_VAL_STANDBY | TPO_R03_EN_PWM); + + if (gpio_is_valid(ddata->nreset_gpio)) + gpio_set_value(ddata->nreset_gpio, 0); + + /* wait for at least 2 vsyncs before cutting off power */ + msleep(50); + + tpo_td043_write(ddata->spi, 3, TPO_R03_VAL_STANDBY); + + regulator_disable(ddata->vcc_reg); + + ddata->powered_on = 0; +} + +static int tpo_td043_connect(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + if (omapdss_device_is_connected(dssdev)) + return 0; + + r = in->ops.dpi->connect(in, dssdev); + if (r) + return r; + + return 0; +} + +static void tpo_td043_disconnect(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + if (!omapdss_device_is_connected(dssdev)) + return; + + in->ops.dpi->disconnect(in, dssdev); +} + +static int tpo_td043_enable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + if (!omapdss_device_is_connected(dssdev)) + return -ENODEV; + + if (omapdss_device_is_enabled(dssdev)) + return 0; + + in->ops.dpi->set_data_lines(in, ddata->data_lines); + in->ops.dpi->set_timings(in, &ddata->videomode); + + r = in->ops.dpi->enable(in); + if (r) + return r; + + /* + * If we are resuming from system suspend, SPI clocks might not be + * enabled yet, so we'll program the LCD from SPI PM resume callback. + */ + if (!ddata->spi_suspended) { + r = tpo_td043_power_on(ddata); + if (r) { + in->ops.dpi->disable(in); + return r; + } + } + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + return 0; +} + +static void tpo_td043_disable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + if (!omapdss_device_is_enabled(dssdev)) + return; + + in->ops.dpi->disable(in); + + if (!ddata->spi_suspended) + tpo_td043_power_off(ddata); + + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; +} + +static void tpo_td043_set_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + ddata->videomode = *timings; + dssdev->panel.timings = *timings; + + in->ops.dpi->set_timings(in, timings); +} + +static void tpo_td043_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + + *timings = ddata->videomode; +} + +static int tpo_td043_check_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + return in->ops.dpi->check_timings(in, timings); +} + +static struct omap_dss_driver tpo_td043_ops = { + .connect = tpo_td043_connect, + .disconnect = tpo_td043_disconnect, + + .enable = tpo_td043_enable, + .disable = tpo_td043_disable, + + .set_timings = tpo_td043_set_timings, + .get_timings = tpo_td043_get_timings, + .check_timings = tpo_td043_check_timings, + + .set_mirror = tpo_td043_set_hmirror, + .get_mirror = tpo_td043_get_hmirror, + + .get_resolution = omapdss_default_get_resolution, +}; + + +static int tpo_td043_probe_pdata(struct spi_device *spi) +{ + const struct panel_tpo_td043mtea1_platform_data *pdata; + struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); + struct omap_dss_device *dssdev, *in; + + pdata = dev_get_platdata(&spi->dev); + + ddata->nreset_gpio = pdata->nreset_gpio; + + in = omap_dss_find_output(pdata->source); + if (in == NULL) { + dev_err(&spi->dev, "failed to find video source '%s'\n", + pdata->source); + return -EPROBE_DEFER; + } + ddata->in = in; + + ddata->data_lines = pdata->data_lines; + + dssdev = &ddata->dssdev; + dssdev->name = pdata->name; + + return 0; +} + +static int tpo_td043_probe(struct spi_device *spi) +{ + struct panel_drv_data *ddata; + struct omap_dss_device *dssdev; + int r; + + dev_dbg(&spi->dev, "%s\n", __func__); + + spi->bits_per_word = 16; + spi->mode = SPI_MODE_0; + + r = spi_setup(spi); + if (r < 0) { + dev_err(&spi->dev, "spi_setup failed: %d\n", r); + return r; + } + + ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL); + if (ddata == NULL) + return -ENOMEM; + + dev_set_drvdata(&spi->dev, ddata); + + ddata->spi = spi; + + if (dev_get_platdata(&spi->dev)) { + r = tpo_td043_probe_pdata(spi); + if (r) + return r; + } else { + return -ENODEV; + } + + ddata->mode = TPO_R02_MODE_800x480; + memcpy(ddata->gamma, tpo_td043_def_gamma, sizeof(ddata->gamma)); + + ddata->vcc_reg = devm_regulator_get(&spi->dev, "vcc"); + if (IS_ERR(ddata->vcc_reg)) { + dev_err(&spi->dev, "failed to get LCD VCC regulator\n"); + r = PTR_ERR(ddata->vcc_reg); + goto err_regulator; + } + + if (gpio_is_valid(ddata->nreset_gpio)) { + r = devm_gpio_request_one(&spi->dev, + ddata->nreset_gpio, GPIOF_OUT_INIT_LOW, + "lcd reset"); + if (r < 0) { + dev_err(&spi->dev, "couldn't request reset GPIO\n"); + goto err_gpio_req; + } + } + + r = sysfs_create_group(&spi->dev.kobj, &tpo_td043_attr_group); + if (r) { + dev_err(&spi->dev, "failed to create sysfs files\n"); + goto err_sysfs; + } + + ddata->videomode = tpo_td043_timings; + + dssdev = &ddata->dssdev; + dssdev->dev = &spi->dev; + dssdev->driver = &tpo_td043_ops; + dssdev->type = OMAP_DISPLAY_TYPE_DPI; + dssdev->owner = THIS_MODULE; + dssdev->panel.timings = ddata->videomode; + + r = omapdss_register_display(dssdev); + if (r) { + dev_err(&spi->dev, "Failed to register panel\n"); + goto err_reg; + } + + return 0; + +err_reg: + sysfs_remove_group(&spi->dev.kobj, &tpo_td043_attr_group); +err_sysfs: +err_gpio_req: +err_regulator: + omap_dss_put_device(ddata->in); + return r; +} + +static int tpo_td043_remove(struct spi_device *spi) +{ + struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); + struct omap_dss_device *dssdev = &ddata->dssdev; + struct omap_dss_device *in = ddata->in; + + dev_dbg(&ddata->spi->dev, "%s\n", __func__); + + omapdss_unregister_display(dssdev); + + tpo_td043_disable(dssdev); + tpo_td043_disconnect(dssdev); + + omap_dss_put_device(in); + + sysfs_remove_group(&spi->dev.kobj, &tpo_td043_attr_group); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int tpo_td043_spi_suspend(struct device *dev) +{ + struct panel_drv_data *ddata = dev_get_drvdata(dev); + + dev_dbg(dev, "tpo_td043_spi_suspend, tpo %p\n", ddata); + + ddata->power_on_resume = ddata->powered_on; + tpo_td043_power_off(ddata); + ddata->spi_suspended = 1; + + return 0; +} + +static int tpo_td043_spi_resume(struct device *dev) +{ + struct panel_drv_data *ddata = dev_get_drvdata(dev); + int ret; + + dev_dbg(dev, "tpo_td043_spi_resume\n"); + + if (ddata->power_on_resume) { + ret = tpo_td043_power_on(ddata); + if (ret) + return ret; + } + ddata->spi_suspended = 0; + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(tpo_td043_spi_pm, + tpo_td043_spi_suspend, tpo_td043_spi_resume); + +static struct spi_driver tpo_td043_spi_driver = { + .driver = { + .name = "panel-tpo-td043mtea1", + .owner = THIS_MODULE, + .pm = &tpo_td043_spi_pm, + }, + .probe = tpo_td043_probe, + .remove = tpo_td043_remove, +}; + +module_spi_driver(tpo_td043_spi_driver); + +MODULE_AUTHOR("GraĹľvydas Ignotas <notasas@gmail.com>"); +MODULE_DESCRIPTION("TPO TD043MTEA1 LCD Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays/Kconfig b/drivers/video/omap2/displays/Kconfig index c3853c92279b..e80ac1c79561 100644 --- a/drivers/video/omap2/displays/Kconfig +++ b/drivers/video/omap2/displays/Kconfig @@ -1,4 +1,4 @@ -menu "OMAP2/3 Display Device Drivers" +menu "OMAP2/3 Display Device Drivers (old device model)" depends on OMAP2_DSS config PANEL_GENERIC_DPI diff --git a/drivers/video/omap2/displays/panel-acx565akm.c b/drivers/video/omap2/displays/panel-acx565akm.c index d7f69c09ecf1..3fd100fc853e 100644 --- a/drivers/video/omap2/displays/panel-acx565akm.c +++ b/drivers/video/omap2/displays/panel-acx565akm.c @@ -510,7 +510,7 @@ static int acx_panel_probe(struct omap_dss_device *dssdev) int max_brightness, brightness; struct backlight_properties props; - dev_dbg(&dssdev->dev, "%s\n", __func__); + dev_dbg(dssdev->dev, "%s\n", __func__); if (!panel_data) return -EINVAL; @@ -519,7 +519,7 @@ static int acx_panel_probe(struct omap_dss_device *dssdev) dssdev->panel.timings = acx_panel_timings; if (gpio_is_valid(panel_data->reset_gpio)) { - r = devm_gpio_request_one(&dssdev->dev, panel_data->reset_gpio, + r = devm_gpio_request_one(dssdev->dev, panel_data->reset_gpio, GPIOF_OUT_INIT_LOW, "lcd reset"); if (r) return r; @@ -538,7 +538,7 @@ static int acx_panel_probe(struct omap_dss_device *dssdev) r = panel_detect(md); if (r) { - dev_err(&dssdev->dev, "%s panel detect error\n", __func__); + dev_err(dssdev->dev, "%s panel detect error\n", __func__); if (!md->enabled && gpio_is_valid(panel_data->reset_gpio)) gpio_set_value(panel_data->reset_gpio, 0); @@ -593,7 +593,7 @@ static void acx_panel_remove(struct omap_dss_device *dssdev) { struct acx565akm_device *md = &acx_dev; - dev_dbg(&dssdev->dev, "%s\n", __func__); + dev_dbg(dssdev->dev, "%s\n", __func__); sysfs_remove_group(&md->bl_dev->dev.kobj, &bldev_attr_group); backlight_device_unregister(md->bl_dev); mutex_lock(&acx_dev.mutex); @@ -607,7 +607,7 @@ static int acx_panel_power_on(struct omap_dss_device *dssdev) struct panel_acx565akm_data *panel_data = get_panel_data(dssdev); int r; - dev_dbg(&dssdev->dev, "%s\n", __func__); + dev_dbg(dssdev->dev, "%s\n", __func__); if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) return 0; @@ -667,7 +667,7 @@ static void acx_panel_power_off(struct omap_dss_device *dssdev) struct acx565akm_device *md = &acx_dev; struct panel_acx565akm_data *panel_data = get_panel_data(dssdev); - dev_dbg(&dssdev->dev, "%s\n", __func__); + dev_dbg(dssdev->dev, "%s\n", __func__); if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) return; @@ -704,7 +704,7 @@ static int acx_panel_enable(struct omap_dss_device *dssdev) { int r; - dev_dbg(&dssdev->dev, "%s\n", __func__); + dev_dbg(dssdev->dev, "%s\n", __func__); r = acx_panel_power_on(dssdev); if (r) @@ -716,7 +716,7 @@ static int acx_panel_enable(struct omap_dss_device *dssdev) static void acx_panel_disable(struct omap_dss_device *dssdev) { - dev_dbg(&dssdev->dev, "%s\n", __func__); + dev_dbg(dssdev->dev, "%s\n", __func__); acx_panel_power_off(dssdev); dssdev->state = OMAP_DSS_DISPLAY_DISABLED; } diff --git a/drivers/video/omap2/displays/panel-generic-dpi.c b/drivers/video/omap2/displays/panel-generic-dpi.c index 97363f733683..bebebd45847f 100644 --- a/drivers/video/omap2/displays/panel-generic-dpi.c +++ b/drivers/video/omap2/displays/panel-generic-dpi.c @@ -536,7 +536,7 @@ static int generic_dpi_panel_power_on(struct omap_dss_device *dssdev) { int r, i; struct panel_generic_dpi_data *panel_data = get_panel_data(dssdev); - struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev); + struct panel_drv_data *drv_data = dev_get_drvdata(dssdev->dev); struct panel_config *panel_config = drv_data->panel_config; if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) @@ -567,7 +567,7 @@ err0: static void generic_dpi_panel_power_off(struct omap_dss_device *dssdev) { struct panel_generic_dpi_data *panel_data = get_panel_data(dssdev); - struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev); + struct panel_drv_data *drv_data = dev_get_drvdata(dssdev->dev); struct panel_config *panel_config = drv_data->panel_config; int i; @@ -593,7 +593,7 @@ static int generic_dpi_panel_probe(struct omap_dss_device *dssdev) struct panel_drv_data *drv_data = NULL; int i, r; - dev_dbg(&dssdev->dev, "probe\n"); + dev_dbg(dssdev->dev, "probe\n"); if (!panel_data || !panel_data->name) return -EINVAL; @@ -609,7 +609,7 @@ static int generic_dpi_panel_probe(struct omap_dss_device *dssdev) return -EINVAL; for (i = 0; i < panel_data->num_gpios; ++i) { - r = devm_gpio_request_one(&dssdev->dev, panel_data->gpios[i], + r = devm_gpio_request_one(dssdev->dev, panel_data->gpios[i], panel_data->gpio_invert[i] ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, "panel gpio"); @@ -619,7 +619,7 @@ static int generic_dpi_panel_probe(struct omap_dss_device *dssdev) dssdev->panel.timings = panel_config->timings; - drv_data = devm_kzalloc(&dssdev->dev, sizeof(*drv_data), GFP_KERNEL); + drv_data = devm_kzalloc(dssdev->dev, sizeof(*drv_data), GFP_KERNEL); if (!drv_data) return -ENOMEM; @@ -628,21 +628,21 @@ static int generic_dpi_panel_probe(struct omap_dss_device *dssdev) mutex_init(&drv_data->lock); - dev_set_drvdata(&dssdev->dev, drv_data); + dev_set_drvdata(dssdev->dev, drv_data); return 0; } static void __exit generic_dpi_panel_remove(struct omap_dss_device *dssdev) { - dev_dbg(&dssdev->dev, "remove\n"); + dev_dbg(dssdev->dev, "remove\n"); - dev_set_drvdata(&dssdev->dev, NULL); + dev_set_drvdata(dssdev->dev, NULL); } static int generic_dpi_panel_enable(struct omap_dss_device *dssdev) { - struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev); + struct panel_drv_data *drv_data = dev_get_drvdata(dssdev->dev); int r; mutex_lock(&drv_data->lock); @@ -660,7 +660,7 @@ err: static void generic_dpi_panel_disable(struct omap_dss_device *dssdev) { - struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev); + struct panel_drv_data *drv_data = dev_get_drvdata(dssdev->dev); mutex_lock(&drv_data->lock); @@ -674,7 +674,7 @@ static void generic_dpi_panel_disable(struct omap_dss_device *dssdev) static void generic_dpi_panel_set_timings(struct omap_dss_device *dssdev, struct omap_video_timings *timings) { - struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev); + struct panel_drv_data *drv_data = dev_get_drvdata(dssdev->dev); mutex_lock(&drv_data->lock); @@ -688,7 +688,7 @@ static void generic_dpi_panel_set_timings(struct omap_dss_device *dssdev, static void generic_dpi_panel_get_timings(struct omap_dss_device *dssdev, struct omap_video_timings *timings) { - struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev); + struct panel_drv_data *drv_data = dev_get_drvdata(dssdev->dev); mutex_lock(&drv_data->lock); @@ -700,7 +700,7 @@ static void generic_dpi_panel_get_timings(struct omap_dss_device *dssdev, static int generic_dpi_panel_check_timings(struct omap_dss_device *dssdev, struct omap_video_timings *timings) { - struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev); + struct panel_drv_data *drv_data = dev_get_drvdata(dssdev->dev); int r; mutex_lock(&drv_data->lock); diff --git a/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c b/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c index 4ea6548c0ae9..6c51430ddb37 100644 --- a/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c +++ b/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c @@ -109,12 +109,12 @@ static int lb035q02_panel_probe(struct omap_dss_device *dssdev) dssdev->panel.timings = lb035q02_timings; - ld = devm_kzalloc(&dssdev->dev, sizeof(*ld), GFP_KERNEL); + ld = devm_kzalloc(dssdev->dev, sizeof(*ld), GFP_KERNEL); if (!ld) return -ENOMEM; for (i = 0; i < panel_data->num_gpios; ++i) { - r = devm_gpio_request_one(&dssdev->dev, panel_data->gpios[i], + r = devm_gpio_request_one(dssdev->dev, panel_data->gpios[i], panel_data->gpio_invert[i] ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, "panel gpio"); @@ -123,7 +123,7 @@ static int lb035q02_panel_probe(struct omap_dss_device *dssdev) } mutex_init(&ld->lock); - dev_set_drvdata(&dssdev->dev, ld); + dev_set_drvdata(dssdev->dev, ld); return 0; } @@ -134,7 +134,7 @@ static void lb035q02_panel_remove(struct omap_dss_device *dssdev) static int lb035q02_panel_enable(struct omap_dss_device *dssdev) { - struct lb035q02_data *ld = dev_get_drvdata(&dssdev->dev); + struct lb035q02_data *ld = dev_get_drvdata(dssdev->dev); int r; mutex_lock(&ld->lock); @@ -153,7 +153,7 @@ err: static void lb035q02_panel_disable(struct omap_dss_device *dssdev) { - struct lb035q02_data *ld = dev_get_drvdata(&dssdev->dev); + struct lb035q02_data *ld = dev_get_drvdata(dssdev->dev); mutex_lock(&ld->lock); diff --git a/drivers/video/omap2/displays/panel-n8x0.c b/drivers/video/omap2/displays/panel-n8x0.c index f94ead6a3183..d3860c50d3f9 100644 --- a/drivers/video/omap2/displays/panel-n8x0.c +++ b/drivers/video/omap2/displays/panel-n8x0.c @@ -311,16 +311,16 @@ static int n8x0_panel_power_on(struct omap_dss_device *dssdev) switch (rev & 0xfc) { case 0x9c: ddata->blizzard_ver = BLIZZARD_VERSION_S1D13744; - dev_info(&dssdev->dev, "s1d13744 LCD controller rev %d " + dev_info(dssdev->dev, "s1d13744 LCD controller rev %d " "initialized (CNF pins %x)\n", rev & 0x03, conf & 0x07); break; case 0xa4: ddata->blizzard_ver = BLIZZARD_VERSION_S1D13745; - dev_info(&dssdev->dev, "s1d13745 LCD controller rev %d " + dev_info(dssdev->dev, "s1d13745 LCD controller rev %d " "initialized (CNF pins %x)\n", rev & 0x03, conf & 0x07); break; default: - dev_err(&dssdev->dev, "invalid s1d1374x revision %02x\n", rev); + dev_err(dssdev->dev, "invalid s1d1374x revision %02x\n", rev); r = -ENODEV; goto err_inv_chip; } @@ -341,13 +341,13 @@ static int n8x0_panel_power_on(struct omap_dss_device *dssdev) panel_name = "ls041y3"; break; default: - dev_err(&dssdev->dev, "invalid display ID 0x%x\n", + dev_err(dssdev->dev, "invalid display ID 0x%x\n", display_id[0]); r = -ENODEV; goto err_inv_panel; } - dev_info(&dssdev->dev, "%s rev %02x LCD detected\n", + dev_info(dssdev->dev, "%s rev %02x LCD detected\n", panel_name, display_id[1]); send_sleep_out(spi); @@ -416,7 +416,7 @@ static int n8x0_panel_probe(struct omap_dss_device *dssdev) struct panel_drv_data *ddata; int r; - dev_dbg(&dssdev->dev, "probe\n"); + dev_dbg(dssdev->dev, "probe\n"); if (!bdata) return -EINVAL; @@ -434,14 +434,14 @@ static int n8x0_panel_probe(struct omap_dss_device *dssdev) dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE; if (gpio_is_valid(bdata->panel_reset)) { - r = devm_gpio_request_one(&dssdev->dev, bdata->panel_reset, + r = devm_gpio_request_one(dssdev->dev, bdata->panel_reset, GPIOF_OUT_INIT_LOW, "PANEL RESET"); if (r) return r; } if (gpio_is_valid(bdata->ctrl_pwrdown)) { - r = devm_gpio_request_one(&dssdev->dev, bdata->ctrl_pwrdown, + r = devm_gpio_request_one(dssdev->dev, bdata->ctrl_pwrdown, GPIOF_OUT_INIT_LOW, "PANEL PWRDOWN"); if (r) return r; @@ -452,9 +452,9 @@ static int n8x0_panel_probe(struct omap_dss_device *dssdev) static void n8x0_panel_remove(struct omap_dss_device *dssdev) { - dev_dbg(&dssdev->dev, "remove\n"); + dev_dbg(dssdev->dev, "remove\n"); - dev_set_drvdata(&dssdev->dev, NULL); + dev_set_drvdata(dssdev->dev, NULL); } static int n8x0_panel_enable(struct omap_dss_device *dssdev) @@ -462,7 +462,7 @@ static int n8x0_panel_enable(struct omap_dss_device *dssdev) struct panel_drv_data *ddata = get_drv_data(dssdev); int r; - dev_dbg(&dssdev->dev, "enable\n"); + dev_dbg(dssdev->dev, "enable\n"); mutex_lock(&ddata->lock); @@ -488,7 +488,7 @@ static void n8x0_panel_disable(struct omap_dss_device *dssdev) { struct panel_drv_data *ddata = get_drv_data(dssdev); - dev_dbg(&dssdev->dev, "disable\n"); + dev_dbg(dssdev->dev, "disable\n"); mutex_lock(&ddata->lock); @@ -521,13 +521,13 @@ static int n8x0_panel_update(struct omap_dss_device *dssdev, struct panel_drv_data *ddata = get_drv_data(dssdev); u16 dw, dh; - dev_dbg(&dssdev->dev, "update\n"); + dev_dbg(dssdev->dev, "update\n"); dw = dssdev->panel.timings.x_res; dh = dssdev->panel.timings.y_res; if (x != 0 || y != 0 || w != dw || h != dh) { - dev_err(&dssdev->dev, "invaid update region %d, %d, %d, %d\n", + dev_err(dssdev->dev, "invaid update region %d, %d, %d, %d\n", x, y, w, h); return -EINVAL; } @@ -548,7 +548,7 @@ static int n8x0_panel_sync(struct omap_dss_device *dssdev) { struct panel_drv_data *ddata = get_drv_data(dssdev); - dev_dbg(&dssdev->dev, "sync\n"); + dev_dbg(dssdev->dev, "sync\n"); mutex_lock(&ddata->lock); rfbi_bus_lock(); diff --git a/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c b/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c index 20c3cd91ff9b..6b9f7925e918 100644 --- a/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c +++ b/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c @@ -98,14 +98,14 @@ static int nec_8048_panel_probe(struct omap_dss_device *dssdev) dssdev->panel.timings = nec_8048_panel_timings; if (gpio_is_valid(pd->qvga_gpio)) { - r = devm_gpio_request_one(&dssdev->dev, pd->qvga_gpio, + r = devm_gpio_request_one(dssdev->dev, pd->qvga_gpio, GPIOF_OUT_INIT_HIGH, "lcd QVGA"); if (r) return r; } if (gpio_is_valid(pd->res_gpio)) { - r = devm_gpio_request_one(&dssdev->dev, pd->res_gpio, + r = devm_gpio_request_one(dssdev->dev, pd->res_gpio, GPIOF_OUT_INIT_LOW, "lcd RES"); if (r) return r; diff --git a/drivers/video/omap2/displays/panel-picodlp.c b/drivers/video/omap2/displays/panel-picodlp.c index 62f2db04fbc8..153e9bea0f6e 100644 --- a/drivers/video/omap2/displays/panel-picodlp.c +++ b/drivers/video/omap2/displays/panel-picodlp.c @@ -351,7 +351,7 @@ static struct i2c_driver picodlp_i2c_driver = { static int picodlp_panel_power_on(struct omap_dss_device *dssdev) { int r, trial = 100; - struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev); + struct picodlp_data *picod = dev_get_drvdata(dssdev->dev); struct picodlp_panel_data *picodlp_pdata = get_panel_data(dssdev); gpio_set_value(picodlp_pdata->pwrgood_gpio, 0); @@ -360,7 +360,7 @@ static int picodlp_panel_power_on(struct omap_dss_device *dssdev) while (!gpio_get_value(picodlp_pdata->emu_done_gpio)) { if (!trial--) { - dev_err(&dssdev->dev, "emu_done signal not" + dev_err(dssdev->dev, "emu_done signal not" " going high\n"); return -ETIMEDOUT; } @@ -378,7 +378,7 @@ static int picodlp_panel_power_on(struct omap_dss_device *dssdev) r = omapdss_dpi_display_enable(dssdev); if (r) { - dev_err(&dssdev->dev, "failed to enable DPI\n"); + dev_err(dssdev->dev, "failed to enable DPI\n"); goto err1; } @@ -418,7 +418,7 @@ static int picodlp_panel_probe(struct omap_dss_device *dssdev) if (!picodlp_pdata) return -EINVAL; - picod = devm_kzalloc(&dssdev->dev, sizeof(*picod), GFP_KERNEL); + picod = devm_kzalloc(dssdev->dev, sizeof(*picod), GFP_KERNEL); if (!picod) return -ENOMEM; @@ -428,23 +428,23 @@ static int picodlp_panel_probe(struct omap_dss_device *dssdev) adapter = i2c_get_adapter(picodlp_adapter_id); if (!adapter) { - dev_err(&dssdev->dev, "can't get i2c adapter\n"); + dev_err(dssdev->dev, "can't get i2c adapter\n"); return -ENODEV; } picodlp_i2c_client = i2c_new_device(adapter, &picodlp_i2c_board_info); if (!picodlp_i2c_client) { - dev_err(&dssdev->dev, "can't add i2c device::" + dev_err(dssdev->dev, "can't add i2c device::" " picodlp_i2c_client is NULL\n"); return -ENODEV; } picod->picodlp_i2c_client = picodlp_i2c_client; - dev_set_drvdata(&dssdev->dev, picod); + dev_set_drvdata(dssdev->dev, picod); if (gpio_is_valid(picodlp_pdata->emu_done_gpio)) { - r = devm_gpio_request_one(&dssdev->dev, + r = devm_gpio_request_one(dssdev->dev, picodlp_pdata->emu_done_gpio, GPIOF_IN, "DLP EMU DONE"); if (r) @@ -452,7 +452,7 @@ static int picodlp_panel_probe(struct omap_dss_device *dssdev) } if (gpio_is_valid(picodlp_pdata->pwrgood_gpio)) { - r = devm_gpio_request_one(&dssdev->dev, + r = devm_gpio_request_one(dssdev->dev, picodlp_pdata->pwrgood_gpio, GPIOF_OUT_INIT_LOW, "DLP PWRGOOD"); if (r) @@ -464,21 +464,19 @@ static int picodlp_panel_probe(struct omap_dss_device *dssdev) static void picodlp_panel_remove(struct omap_dss_device *dssdev) { - struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev); + struct picodlp_data *picod = dev_get_drvdata(dssdev->dev); i2c_unregister_device(picod->picodlp_i2c_client); - dev_set_drvdata(&dssdev->dev, NULL); - dev_dbg(&dssdev->dev, "removing picodlp panel\n"); - - kfree(picod); + dev_set_drvdata(dssdev->dev, NULL); + dev_dbg(dssdev->dev, "removing picodlp panel\n"); } static int picodlp_panel_enable(struct omap_dss_device *dssdev) { - struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev); + struct picodlp_data *picod = dev_get_drvdata(dssdev->dev); int r; - dev_dbg(&dssdev->dev, "enabling picodlp panel\n"); + dev_dbg(dssdev->dev, "enabling picodlp panel\n"); mutex_lock(&picod->lock); if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { @@ -494,7 +492,7 @@ static int picodlp_panel_enable(struct omap_dss_device *dssdev) static void picodlp_panel_disable(struct omap_dss_device *dssdev) { - struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev); + struct picodlp_data *picod = dev_get_drvdata(dssdev->dev); mutex_lock(&picod->lock); /* Turn off DLP Power */ @@ -504,7 +502,7 @@ static void picodlp_panel_disable(struct omap_dss_device *dssdev) dssdev->state = OMAP_DSS_DISPLAY_DISABLED; mutex_unlock(&picod->lock); - dev_dbg(&dssdev->dev, "disabling picodlp panel\n"); + dev_dbg(dssdev->dev, "disabling picodlp panel\n"); } static void picodlp_get_resolution(struct omap_dss_device *dssdev, diff --git a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c index 74cb0eb45311..78f0a6779756 100644 --- a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c +++ b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c @@ -66,35 +66,35 @@ static int sharp_ls_panel_probe(struct omap_dss_device *dssdev) dssdev->panel.timings = sharp_ls_timings; if (gpio_is_valid(pd->mo_gpio)) { - r = devm_gpio_request_one(&dssdev->dev, pd->mo_gpio, + r = devm_gpio_request_one(dssdev->dev, pd->mo_gpio, GPIOF_OUT_INIT_LOW, "lcd MO"); if (r) return r; } if (gpio_is_valid(pd->lr_gpio)) { - r = devm_gpio_request_one(&dssdev->dev, pd->lr_gpio, + r = devm_gpio_request_one(dssdev->dev, pd->lr_gpio, GPIOF_OUT_INIT_HIGH, "lcd LR"); if (r) return r; } if (gpio_is_valid(pd->ud_gpio)) { - r = devm_gpio_request_one(&dssdev->dev, pd->ud_gpio, + r = devm_gpio_request_one(dssdev->dev, pd->ud_gpio, GPIOF_OUT_INIT_HIGH, "lcd UD"); if (r) return r; } if (gpio_is_valid(pd->resb_gpio)) { - r = devm_gpio_request_one(&dssdev->dev, pd->resb_gpio, + r = devm_gpio_request_one(dssdev->dev, pd->resb_gpio, GPIOF_OUT_INIT_LOW, "lcd RESB"); if (r) return r; } if (gpio_is_valid(pd->ini_gpio)) { - r = devm_gpio_request_one(&dssdev->dev, pd->ini_gpio, + r = devm_gpio_request_one(dssdev->dev, pd->ini_gpio, GPIOF_OUT_INIT_LOW, "lcd INI"); if (r) return r; diff --git a/drivers/video/omap2/displays/panel-taal.c b/drivers/video/omap2/displays/panel-taal.c index 52541f264021..54a07da8587a 100644 --- a/drivers/video/omap2/displays/panel-taal.c +++ b/drivers/video/omap2/displays/panel-taal.c @@ -237,7 +237,7 @@ static int taal_set_update_window(struct taal_data *td, static void taal_queue_esd_work(struct omap_dss_device *dssdev) { - struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct taal_data *td = dev_get_drvdata(dssdev->dev); if (td->esd_interval > 0) queue_delayed_work(td->workqueue, &td->esd_work, @@ -246,14 +246,14 @@ static void taal_queue_esd_work(struct omap_dss_device *dssdev) static void taal_cancel_esd_work(struct omap_dss_device *dssdev) { - struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct taal_data *td = dev_get_drvdata(dssdev->dev); cancel_delayed_work(&td->esd_work); } static void taal_queue_ulps_work(struct omap_dss_device *dssdev) { - struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct taal_data *td = dev_get_drvdata(dssdev->dev); if (td->ulps_timeout > 0) queue_delayed_work(td->workqueue, &td->ulps_work, @@ -262,14 +262,14 @@ static void taal_queue_ulps_work(struct omap_dss_device *dssdev) static void taal_cancel_ulps_work(struct omap_dss_device *dssdev) { - struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct taal_data *td = dev_get_drvdata(dssdev->dev); cancel_delayed_work(&td->ulps_work); } static int taal_enter_ulps(struct omap_dss_device *dssdev) { - struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct taal_data *td = dev_get_drvdata(dssdev->dev); int r; if (td->ulps_enabled) @@ -291,7 +291,7 @@ static int taal_enter_ulps(struct omap_dss_device *dssdev) return 0; err: - dev_err(&dssdev->dev, "enter ULPS failed"); + dev_err(dssdev->dev, "enter ULPS failed"); taal_panel_reset(dssdev); td->ulps_enabled = false; @@ -303,7 +303,7 @@ err: static int taal_exit_ulps(struct omap_dss_device *dssdev) { - struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct taal_data *td = dev_get_drvdata(dssdev->dev); int r; if (!td->ulps_enabled) @@ -311,7 +311,7 @@ static int taal_exit_ulps(struct omap_dss_device *dssdev) r = omapdss_dsi_display_enable(dssdev); if (r) { - dev_err(&dssdev->dev, "failed to enable DSI\n"); + dev_err(dssdev->dev, "failed to enable DSI\n"); goto err1; } @@ -319,7 +319,7 @@ static int taal_exit_ulps(struct omap_dss_device *dssdev) r = _taal_enable_te(dssdev, true); if (r) { - dev_err(&dssdev->dev, "failed to re-enable TE"); + dev_err(dssdev->dev, "failed to re-enable TE"); goto err2; } @@ -333,7 +333,7 @@ static int taal_exit_ulps(struct omap_dss_device *dssdev) return 0; err2: - dev_err(&dssdev->dev, "failed to exit ULPS"); + dev_err(dssdev->dev, "failed to exit ULPS"); r = taal_panel_reset(dssdev); if (!r) { @@ -349,7 +349,7 @@ err1: static int taal_wake_up(struct omap_dss_device *dssdev) { - struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct taal_data *td = dev_get_drvdata(dssdev->dev); if (td->ulps_enabled) return taal_exit_ulps(dssdev); @@ -362,7 +362,7 @@ static int taal_wake_up(struct omap_dss_device *dssdev) static int taal_bl_update_status(struct backlight_device *dev) { struct omap_dss_device *dssdev = dev_get_drvdata(&dev->dev); - struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct taal_data *td = dev_get_drvdata(dssdev->dev); int r; int level; @@ -372,7 +372,7 @@ static int taal_bl_update_status(struct backlight_device *dev) else level = 0; - dev_dbg(&dssdev->dev, "update brightness to %d\n", level); + dev_dbg(dssdev->dev, "update brightness to %d\n", level); mutex_lock(&td->lock); @@ -418,7 +418,7 @@ static ssize_t taal_num_errors_show(struct device *dev, struct device_attribute *attr, char *buf) { struct omap_dss_device *dssdev = to_dss_device(dev); - struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct taal_data *td = dev_get_drvdata(dssdev->dev); u8 errors = 0; int r; @@ -448,7 +448,7 @@ static ssize_t taal_hw_revision_show(struct device *dev, struct device_attribute *attr, char *buf) { struct omap_dss_device *dssdev = to_dss_device(dev); - struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct taal_data *td = dev_get_drvdata(dssdev->dev); u8 id1, id2, id3; int r; @@ -486,7 +486,7 @@ static ssize_t show_cabc_mode(struct device *dev, char *buf) { struct omap_dss_device *dssdev = to_dss_device(dev); - struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct taal_data *td = dev_get_drvdata(dssdev->dev); const char *mode_str; int mode; int len; @@ -506,7 +506,7 @@ static ssize_t store_cabc_mode(struct device *dev, const char *buf, size_t count) { struct omap_dss_device *dssdev = to_dss_device(dev); - struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct taal_data *td = dev_get_drvdata(dssdev->dev); int i; int r; @@ -568,7 +568,7 @@ static ssize_t taal_store_esd_interval(struct device *dev, const char *buf, size_t count) { struct omap_dss_device *dssdev = to_dss_device(dev); - struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct taal_data *td = dev_get_drvdata(dssdev->dev); unsigned long t; int r; @@ -592,7 +592,7 @@ static ssize_t taal_show_esd_interval(struct device *dev, char *buf) { struct omap_dss_device *dssdev = to_dss_device(dev); - struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct taal_data *td = dev_get_drvdata(dssdev->dev); unsigned t; mutex_lock(&td->lock); @@ -607,7 +607,7 @@ static ssize_t taal_store_ulps(struct device *dev, const char *buf, size_t count) { struct omap_dss_device *dssdev = to_dss_device(dev); - struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct taal_data *td = dev_get_drvdata(dssdev->dev); unsigned long t; int r; @@ -641,7 +641,7 @@ static ssize_t taal_show_ulps(struct device *dev, char *buf) { struct omap_dss_device *dssdev = to_dss_device(dev); - struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct taal_data *td = dev_get_drvdata(dssdev->dev); unsigned t; mutex_lock(&td->lock); @@ -656,7 +656,7 @@ static ssize_t taal_store_ulps_timeout(struct device *dev, const char *buf, size_t count) { struct omap_dss_device *dssdev = to_dss_device(dev); - struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct taal_data *td = dev_get_drvdata(dssdev->dev); unsigned long t; int r; @@ -687,7 +687,7 @@ static ssize_t taal_show_ulps_timeout(struct device *dev, char *buf) { struct omap_dss_device *dssdev = to_dss_device(dev); - struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct taal_data *td = dev_get_drvdata(dssdev->dev); unsigned t; mutex_lock(&td->lock); @@ -727,7 +727,7 @@ static struct attribute_group taal_attr_group = { static void taal_hw_reset(struct omap_dss_device *dssdev) { - struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct taal_data *td = dev_get_drvdata(dssdev->dev); if (!gpio_is_valid(td->reset_gpio)) return; @@ -768,13 +768,13 @@ static int taal_probe(struct omap_dss_device *dssdev) struct backlight_device *bldev = NULL; int r; - dev_dbg(&dssdev->dev, "probe\n"); + dev_dbg(dssdev->dev, "probe\n"); - td = devm_kzalloc(&dssdev->dev, sizeof(*td), GFP_KERNEL); + td = devm_kzalloc(dssdev->dev, sizeof(*td), GFP_KERNEL); if (!td) return -ENOMEM; - dev_set_drvdata(&dssdev->dev, td); + dev_set_drvdata(dssdev->dev, td); td->dssdev = dssdev; if (dssdev->data) { @@ -797,41 +797,41 @@ static int taal_probe(struct omap_dss_device *dssdev) atomic_set(&td->do_update, 0); if (gpio_is_valid(td->reset_gpio)) { - r = devm_gpio_request_one(&dssdev->dev, td->reset_gpio, + r = devm_gpio_request_one(dssdev->dev, td->reset_gpio, GPIOF_OUT_INIT_LOW, "taal rst"); if (r) { - dev_err(&dssdev->dev, "failed to request reset gpio\n"); + dev_err(dssdev->dev, "failed to request reset gpio\n"); return r; } } if (gpio_is_valid(td->ext_te_gpio)) { - r = devm_gpio_request_one(&dssdev->dev, td->ext_te_gpio, + r = devm_gpio_request_one(dssdev->dev, td->ext_te_gpio, GPIOF_IN, "taal irq"); if (r) { - dev_err(&dssdev->dev, "GPIO request failed\n"); + dev_err(dssdev->dev, "GPIO request failed\n"); return r; } - r = devm_request_irq(&dssdev->dev, gpio_to_irq(td->ext_te_gpio), + r = devm_request_irq(dssdev->dev, gpio_to_irq(td->ext_te_gpio), taal_te_isr, IRQF_TRIGGER_RISING, "taal vsync", dssdev); if (r) { - dev_err(&dssdev->dev, "IRQ request failed\n"); + dev_err(dssdev->dev, "IRQ request failed\n"); return r; } INIT_DEFERRABLE_WORK(&td->te_timeout_work, taal_te_timeout_work_callback); - dev_dbg(&dssdev->dev, "Using GPIO TE\n"); + dev_dbg(dssdev->dev, "Using GPIO TE\n"); } td->workqueue = create_singlethread_workqueue("taal_esd"); if (td->workqueue == NULL) { - dev_err(&dssdev->dev, "can't create ESD workqueue\n"); + dev_err(dssdev->dev, "can't create ESD workqueue\n"); return -ENOMEM; } INIT_DEFERRABLE_WORK(&td->esd_work, taal_esd_work); @@ -844,8 +844,8 @@ static int taal_probe(struct omap_dss_device *dssdev) props.max_brightness = 255; props.type = BACKLIGHT_RAW; - bldev = backlight_device_register(dev_name(&dssdev->dev), - &dssdev->dev, dssdev, &taal_bl_ops, &props); + bldev = backlight_device_register(dev_name(dssdev->dev), + dssdev->dev, dssdev, &taal_bl_ops, &props); if (IS_ERR(bldev)) { r = PTR_ERR(bldev); goto err_bl; @@ -862,19 +862,19 @@ static int taal_probe(struct omap_dss_device *dssdev) r = omap_dsi_request_vc(dssdev, &td->channel); if (r) { - dev_err(&dssdev->dev, "failed to get virtual channel\n"); + dev_err(dssdev->dev, "failed to get virtual channel\n"); goto err_req_vc; } r = omap_dsi_set_vc_id(dssdev, td->channel, TCH); if (r) { - dev_err(&dssdev->dev, "failed to set VC_ID\n"); + dev_err(dssdev->dev, "failed to set VC_ID\n"); goto err_vc_id; } - r = sysfs_create_group(&dssdev->dev.kobj, &taal_attr_group); + r = sysfs_create_group(&dssdev->dev->kobj, &taal_attr_group); if (r) { - dev_err(&dssdev->dev, "failed to create sysfs files\n"); + dev_err(dssdev->dev, "failed to create sysfs files\n"); goto err_vc_id; } @@ -892,12 +892,12 @@ err_bl: static void __exit taal_remove(struct omap_dss_device *dssdev) { - struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct taal_data *td = dev_get_drvdata(dssdev->dev); struct backlight_device *bldev; - dev_dbg(&dssdev->dev, "remove\n"); + dev_dbg(dssdev->dev, "remove\n"); - sysfs_remove_group(&dssdev->dev.kobj, &taal_attr_group); + sysfs_remove_group(&dssdev->dev->kobj, &taal_attr_group); omap_dsi_release_vc(dssdev, td->channel); bldev = td->bldev; @@ -917,7 +917,7 @@ static void __exit taal_remove(struct omap_dss_device *dssdev) static int taal_power_on(struct omap_dss_device *dssdev) { - struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct taal_data *td = dev_get_drvdata(dssdev->dev); u8 id1, id2, id3; int r; struct omap_dss_dsi_config dsi_config = { @@ -932,19 +932,19 @@ static int taal_power_on(struct omap_dss_device *dssdev) r = omapdss_dsi_configure_pins(dssdev, &td->pin_config); if (r) { - dev_err(&dssdev->dev, "failed to configure DSI pins\n"); + dev_err(dssdev->dev, "failed to configure DSI pins\n"); goto err0; }; r = omapdss_dsi_set_config(dssdev, &dsi_config); if (r) { - dev_err(&dssdev->dev, "failed to configure DSI\n"); + dev_err(dssdev->dev, "failed to configure DSI\n"); goto err0; } r = omapdss_dsi_display_enable(dssdev); if (r) { - dev_err(&dssdev->dev, "failed to enable DSI\n"); + dev_err(dssdev->dev, "failed to enable DSI\n"); goto err0; } @@ -999,10 +999,10 @@ static int taal_power_on(struct omap_dss_device *dssdev) td->enabled = 1; if (!td->intro_printed) { - dev_info(&dssdev->dev, "panel revision %02x.%02x.%02x\n", + dev_info(dssdev->dev, "panel revision %02x.%02x.%02x\n", id1, id2, id3); if (td->cabc_broken) - dev_info(&dssdev->dev, + dev_info(dssdev->dev, "old Taal version, CABC disabled\n"); td->intro_printed = true; } @@ -1011,7 +1011,7 @@ static int taal_power_on(struct omap_dss_device *dssdev) return 0; err: - dev_err(&dssdev->dev, "error while enabling panel, issuing HW reset\n"); + dev_err(dssdev->dev, "error while enabling panel, issuing HW reset\n"); taal_hw_reset(dssdev); @@ -1022,7 +1022,7 @@ err0: static void taal_power_off(struct omap_dss_device *dssdev) { - struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct taal_data *td = dev_get_drvdata(dssdev->dev); int r; dsi_disable_video_output(dssdev, td->channel); @@ -1032,7 +1032,7 @@ static void taal_power_off(struct omap_dss_device *dssdev) r = taal_sleep_in(td); if (r) { - dev_err(&dssdev->dev, + dev_err(dssdev->dev, "error disabling panel, issuing HW reset\n"); taal_hw_reset(dssdev); } @@ -1044,7 +1044,7 @@ static void taal_power_off(struct omap_dss_device *dssdev) static int taal_panel_reset(struct omap_dss_device *dssdev) { - dev_err(&dssdev->dev, "performing LCD reset\n"); + dev_err(dssdev->dev, "performing LCD reset\n"); taal_power_off(dssdev); taal_hw_reset(dssdev); @@ -1053,10 +1053,10 @@ static int taal_panel_reset(struct omap_dss_device *dssdev) static int taal_enable(struct omap_dss_device *dssdev) { - struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct taal_data *td = dev_get_drvdata(dssdev->dev); int r; - dev_dbg(&dssdev->dev, "enable\n"); + dev_dbg(dssdev->dev, "enable\n"); mutex_lock(&td->lock); @@ -1082,16 +1082,16 @@ static int taal_enable(struct omap_dss_device *dssdev) return 0; err: - dev_dbg(&dssdev->dev, "enable failed\n"); + dev_dbg(dssdev->dev, "enable failed\n"); mutex_unlock(&td->lock); return r; } static void taal_disable(struct omap_dss_device *dssdev) { - struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct taal_data *td = dev_get_drvdata(dssdev->dev); - dev_dbg(&dssdev->dev, "disable\n"); + dev_dbg(dssdev->dev, "disable\n"); mutex_lock(&td->lock); @@ -1118,14 +1118,14 @@ static void taal_disable(struct omap_dss_device *dssdev) static void taal_framedone_cb(int err, void *data) { struct omap_dss_device *dssdev = data; - dev_dbg(&dssdev->dev, "framedone, err %d\n", err); + dev_dbg(dssdev->dev, "framedone, err %d\n", err); dsi_bus_unlock(dssdev); } static irqreturn_t taal_te_isr(int irq, void *data) { struct omap_dss_device *dssdev = data; - struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct taal_data *td = dev_get_drvdata(dssdev->dev); int old; int r; @@ -1142,7 +1142,7 @@ static irqreturn_t taal_te_isr(int irq, void *data) return IRQ_HANDLED; err: - dev_err(&dssdev->dev, "start update failed\n"); + dev_err(dssdev->dev, "start update failed\n"); dsi_bus_unlock(dssdev); return IRQ_HANDLED; } @@ -1153,7 +1153,7 @@ static void taal_te_timeout_work_callback(struct work_struct *work) te_timeout_work.work); struct omap_dss_device *dssdev = td->dssdev; - dev_err(&dssdev->dev, "TE not received for 250ms!\n"); + dev_err(dssdev->dev, "TE not received for 250ms!\n"); atomic_set(&td->do_update, 0); dsi_bus_unlock(dssdev); @@ -1162,10 +1162,10 @@ static void taal_te_timeout_work_callback(struct work_struct *work) static int taal_update(struct omap_dss_device *dssdev, u16 x, u16 y, u16 w, u16 h) { - struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct taal_data *td = dev_get_drvdata(dssdev->dev); int r; - dev_dbg(&dssdev->dev, "update %d, %d, %d x %d\n", x, y, w, h); + dev_dbg(dssdev->dev, "update %d, %d, %d x %d\n", x, y, w, h); mutex_lock(&td->lock); dsi_bus_lock(dssdev); @@ -1208,23 +1208,23 @@ err: static int taal_sync(struct omap_dss_device *dssdev) { - struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct taal_data *td = dev_get_drvdata(dssdev->dev); - dev_dbg(&dssdev->dev, "sync\n"); + dev_dbg(dssdev->dev, "sync\n"); mutex_lock(&td->lock); dsi_bus_lock(dssdev); dsi_bus_unlock(dssdev); mutex_unlock(&td->lock); - dev_dbg(&dssdev->dev, "sync done\n"); + dev_dbg(dssdev->dev, "sync done\n"); return 0; } static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable) { - struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct taal_data *td = dev_get_drvdata(dssdev->dev); int r; if (enable) @@ -1243,7 +1243,7 @@ static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable) static int taal_enable_te(struct omap_dss_device *dssdev, bool enable) { - struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct taal_data *td = dev_get_drvdata(dssdev->dev); int r; mutex_lock(&td->lock); @@ -1279,7 +1279,7 @@ err: static int taal_get_te(struct omap_dss_device *dssdev) { - struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct taal_data *td = dev_get_drvdata(dssdev->dev); int r; mutex_lock(&td->lock); @@ -1291,7 +1291,7 @@ static int taal_get_te(struct omap_dss_device *dssdev) static int taal_run_test(struct omap_dss_device *dssdev, int test_num) { - struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct taal_data *td = dev_get_drvdata(dssdev->dev); u8 id1, id2, id3; int r; @@ -1336,7 +1336,7 @@ static int taal_memory_read(struct omap_dss_device *dssdev, int first = 1; int plen; unsigned buf_used = 0; - struct taal_data *td = dev_get_drvdata(&dssdev->dev); + struct taal_data *td = dev_get_drvdata(dssdev->dev); if (size < w * h * 3) return -ENOMEM; @@ -1380,19 +1380,19 @@ static int taal_memory_read(struct omap_dss_device *dssdev, buf + buf_used, size - buf_used); if (r < 0) { - dev_err(&dssdev->dev, "read error\n"); + dev_err(dssdev->dev, "read error\n"); goto err3; } buf_used += r; if (r < plen) { - dev_err(&dssdev->dev, "short read\n"); + dev_err(dssdev->dev, "short read\n"); break; } if (signal_pending(current)) { - dev_err(&dssdev->dev, "signal pending, " + dev_err(dssdev->dev, "signal pending, " "aborting memory read\n"); r = -ERESTARTSYS; goto err3; @@ -1450,26 +1450,26 @@ static void taal_esd_work(struct work_struct *work) r = taal_wake_up(dssdev); if (r) { - dev_err(&dssdev->dev, "failed to exit ULPS\n"); + dev_err(dssdev->dev, "failed to exit ULPS\n"); goto err; } r = taal_dcs_read_1(td, MIPI_DCS_GET_DIAGNOSTIC_RESULT, &state1); if (r) { - dev_err(&dssdev->dev, "failed to read Taal status\n"); + dev_err(dssdev->dev, "failed to read Taal status\n"); goto err; } /* Run self diagnostics */ r = taal_sleep_out(td); if (r) { - dev_err(&dssdev->dev, "failed to run Taal self-diagnostics\n"); + dev_err(dssdev->dev, "failed to run Taal self-diagnostics\n"); goto err; } r = taal_dcs_read_1(td, MIPI_DCS_GET_DIAGNOSTIC_RESULT, &state2); if (r) { - dev_err(&dssdev->dev, "failed to read Taal status\n"); + dev_err(dssdev->dev, "failed to read Taal status\n"); goto err; } @@ -1477,7 +1477,7 @@ static void taal_esd_work(struct work_struct *work) * Bit6 if the test passes. */ if (!((state1 ^ state2) & (1 << 6))) { - dev_err(&dssdev->dev, "LCD self diagnostics failed\n"); + dev_err(dssdev->dev, "LCD self diagnostics failed\n"); goto err; } /* Self-diagnostics result is also shown on TE GPIO line. We need @@ -1495,7 +1495,7 @@ static void taal_esd_work(struct work_struct *work) mutex_unlock(&td->lock); return; err: - dev_err(&dssdev->dev, "performing LCD reset\n"); + dev_err(dssdev->dev, "performing LCD reset\n"); taal_panel_reset(dssdev); diff --git a/drivers/video/omap2/displays/panel-tfp410.c b/drivers/video/omap2/displays/panel-tfp410.c index 46039c4bf1ed..1fdfb158a2a9 100644 --- a/drivers/video/omap2/displays/panel-tfp410.c +++ b/drivers/video/omap2/displays/panel-tfp410.c @@ -59,7 +59,7 @@ struct panel_drv_data { static int tfp410_power_on(struct omap_dss_device *dssdev) { - struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); + struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev); int r; if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) @@ -82,7 +82,7 @@ err0: static void tfp410_power_off(struct omap_dss_device *dssdev) { - struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); + struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev); if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) return; @@ -99,7 +99,7 @@ static int tfp410_probe(struct omap_dss_device *dssdev) int r; int i2c_bus_num; - ddata = devm_kzalloc(&dssdev->dev, sizeof(*ddata), GFP_KERNEL); + ddata = devm_kzalloc(dssdev->dev, sizeof(*ddata), GFP_KERNEL); if (!ddata) return -ENOMEM; @@ -119,10 +119,10 @@ static int tfp410_probe(struct omap_dss_device *dssdev) } if (gpio_is_valid(ddata->pd_gpio)) { - r = devm_gpio_request_one(&dssdev->dev, ddata->pd_gpio, + r = devm_gpio_request_one(dssdev->dev, ddata->pd_gpio, GPIOF_OUT_INIT_LOW, "tfp410 pd"); if (r) { - dev_err(&dssdev->dev, "Failed to request PD GPIO %d\n", + dev_err(dssdev->dev, "Failed to request PD GPIO %d\n", ddata->pd_gpio); return r; } @@ -133,7 +133,7 @@ static int tfp410_probe(struct omap_dss_device *dssdev) adapter = i2c_get_adapter(i2c_bus_num); if (!adapter) { - dev_err(&dssdev->dev, "Failed to get I2C adapter, bus %d\n", + dev_err(dssdev->dev, "Failed to get I2C adapter, bus %d\n", i2c_bus_num); return -EPROBE_DEFER; } @@ -141,28 +141,28 @@ static int tfp410_probe(struct omap_dss_device *dssdev) ddata->i2c_adapter = adapter; } - dev_set_drvdata(&dssdev->dev, ddata); + dev_set_drvdata(dssdev->dev, ddata); return 0; } static void __exit tfp410_remove(struct omap_dss_device *dssdev) { - struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); + struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev); mutex_lock(&ddata->lock); if (ddata->i2c_adapter) i2c_put_adapter(ddata->i2c_adapter); - dev_set_drvdata(&dssdev->dev, NULL); + dev_set_drvdata(dssdev->dev, NULL); mutex_unlock(&ddata->lock); } static int tfp410_enable(struct omap_dss_device *dssdev) { - struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); + struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev); int r; mutex_lock(&ddata->lock); @@ -178,7 +178,7 @@ static int tfp410_enable(struct omap_dss_device *dssdev) static void tfp410_disable(struct omap_dss_device *dssdev) { - struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); + struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev); mutex_lock(&ddata->lock); @@ -192,7 +192,7 @@ static void tfp410_disable(struct omap_dss_device *dssdev) static void tfp410_set_timings(struct omap_dss_device *dssdev, struct omap_video_timings *timings) { - struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); + struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev); mutex_lock(&ddata->lock); omapdss_dpi_set_timings(dssdev, timings); @@ -203,7 +203,7 @@ static void tfp410_set_timings(struct omap_dss_device *dssdev, static void tfp410_get_timings(struct omap_dss_device *dssdev, struct omap_video_timings *timings) { - struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); + struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev); mutex_lock(&ddata->lock); *timings = dssdev->panel.timings; @@ -213,7 +213,7 @@ static void tfp410_get_timings(struct omap_dss_device *dssdev, static int tfp410_check_timings(struct omap_dss_device *dssdev, struct omap_video_timings *timings) { - struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); + struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev); int r; mutex_lock(&ddata->lock); @@ -258,7 +258,7 @@ static int tfp410_ddc_read(struct i2c_adapter *adapter, static int tfp410_read_edid(struct omap_dss_device *dssdev, u8 *edid, int len) { - struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); + struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev); int r, l, bytes_read; mutex_lock(&ddata->lock); @@ -298,7 +298,7 @@ err: static bool tfp410_detect(struct omap_dss_device *dssdev) { - struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); + struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev); unsigned char out; int r; diff --git a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c index abf2bc4a18ab..7729b6fa6f97 100644 --- a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c +++ b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c @@ -126,7 +126,7 @@ static int tpo_td043_write_mirror(struct spi_device *spi, bool h, bool v) static int tpo_td043_set_hmirror(struct omap_dss_device *dssdev, bool enable) { - struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev); + struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dssdev->dev); tpo_td043->hmirror = enable; return tpo_td043_write_mirror(tpo_td043->spi, tpo_td043->hmirror, @@ -135,7 +135,7 @@ static int tpo_td043_set_hmirror(struct omap_dss_device *dssdev, bool enable) static bool tpo_td043_get_hmirror(struct omap_dss_device *dssdev) { - struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev); + struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dssdev->dev); return tpo_td043->hmirror; } @@ -338,7 +338,7 @@ static void tpo_td043_power_off(struct tpo_td043_device *tpo_td043) static int tpo_td043_enable_dss(struct omap_dss_device *dssdev) { - struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev); + struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dssdev->dev); int r; if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) @@ -372,7 +372,7 @@ err0: static void tpo_td043_disable_dss(struct omap_dss_device *dssdev) { - struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev); + struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dssdev->dev); if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) return; @@ -385,14 +385,14 @@ static void tpo_td043_disable_dss(struct omap_dss_device *dssdev) static int tpo_td043_enable(struct omap_dss_device *dssdev) { - dev_dbg(&dssdev->dev, "enable\n"); + dev_dbg(dssdev->dev, "enable\n"); return tpo_td043_enable_dss(dssdev); } static void tpo_td043_disable(struct omap_dss_device *dssdev) { - dev_dbg(&dssdev->dev, "disable\n"); + dev_dbg(dssdev->dev, "disable\n"); tpo_td043_disable_dss(dssdev); @@ -405,10 +405,10 @@ static int tpo_td043_probe(struct omap_dss_device *dssdev) struct panel_tpo_td043_data *pdata = get_panel_data(dssdev); int ret = 0; - dev_dbg(&dssdev->dev, "probe\n"); + dev_dbg(dssdev->dev, "probe\n"); if (tpo_td043 == NULL) { - dev_err(&dssdev->dev, "missing tpo_td043_device\n"); + dev_err(dssdev->dev, "missing tpo_td043_device\n"); return -ENODEV; } @@ -423,28 +423,28 @@ static int tpo_td043_probe(struct omap_dss_device *dssdev) tpo_td043->mode = TPO_R02_MODE_800x480; memcpy(tpo_td043->gamma, tpo_td043_def_gamma, sizeof(tpo_td043->gamma)); - tpo_td043->vcc_reg = regulator_get(&dssdev->dev, "vcc"); + tpo_td043->vcc_reg = regulator_get(dssdev->dev, "vcc"); if (IS_ERR(tpo_td043->vcc_reg)) { - dev_err(&dssdev->dev, "failed to get LCD VCC regulator\n"); + dev_err(dssdev->dev, "failed to get LCD VCC regulator\n"); ret = PTR_ERR(tpo_td043->vcc_reg); goto fail_regulator; } if (gpio_is_valid(tpo_td043->nreset_gpio)) { - ret = devm_gpio_request_one(&dssdev->dev, + ret = devm_gpio_request_one(dssdev->dev, tpo_td043->nreset_gpio, GPIOF_OUT_INIT_LOW, "lcd reset"); if (ret < 0) { - dev_err(&dssdev->dev, "couldn't request reset GPIO\n"); + dev_err(dssdev->dev, "couldn't request reset GPIO\n"); goto fail_gpio_req; } } - ret = sysfs_create_group(&dssdev->dev.kobj, &tpo_td043_attr_group); + ret = sysfs_create_group(&dssdev->dev->kobj, &tpo_td043_attr_group); if (ret) - dev_warn(&dssdev->dev, "failed to create sysfs files\n"); + dev_warn(dssdev->dev, "failed to create sysfs files\n"); - dev_set_drvdata(&dssdev->dev, tpo_td043); + dev_set_drvdata(dssdev->dev, tpo_td043); return 0; @@ -457,11 +457,11 @@ fail_regulator: static void tpo_td043_remove(struct omap_dss_device *dssdev) { - struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev); + struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dssdev->dev); - dev_dbg(&dssdev->dev, "remove\n"); + dev_dbg(dssdev->dev, "remove\n"); - sysfs_remove_group(&dssdev->dev.kobj, &tpo_td043_attr_group); + sysfs_remove_group(&dssdev->dev->kobj, &tpo_td043_attr_group); regulator_put(tpo_td043->vcc_reg); } diff --git a/drivers/video/omap2/dss/Kconfig b/drivers/video/omap2/dss/Kconfig index cb0f145c7077..8f70a8300b84 100644 --- a/drivers/video/omap2/dss/Kconfig +++ b/drivers/video/omap2/dss/Kconfig @@ -1,5 +1,6 @@ menuconfig OMAP2_DSS tristate "OMAP2+ Display Subsystem support" + select VIDEOMODE_HELPERS help OMAP2+ Display Subsystem support. diff --git a/drivers/video/omap2/dss/apply.c b/drivers/video/omap2/dss/apply.c index a4b356a9780d..d6212d63cfb2 100644 --- a/drivers/video/omap2/dss/apply.c +++ b/drivers/video/omap2/dss/apply.c @@ -420,16 +420,26 @@ static void wait_pending_extra_info_updates(void) DSSWARN("timeout in wait_pending_extra_info_updates\n"); } -static inline struct omap_dss_device *dss_ovl_get_device(struct omap_overlay *ovl) +static struct omap_dss_device *dss_mgr_get_device(struct omap_overlay_manager *mgr) { - return ovl->manager ? - (ovl->manager->output ? ovl->manager->output->device : NULL) : - NULL; + struct omap_dss_device *dssdev; + + dssdev = mgr->output; + if (dssdev == NULL) + return NULL; + + while (dssdev->device) + dssdev = dssdev->device; + + if (dssdev->driver) + return dssdev; + else + return NULL; } -static inline struct omap_dss_device *dss_mgr_get_device(struct omap_overlay_manager *mgr) +static struct omap_dss_device *dss_ovl_get_device(struct omap_overlay *ovl) { - return mgr->output ? mgr->output->device : NULL; + return ovl->manager ? dss_mgr_get_device(ovl->manager) : NULL; } static int dss_mgr_wait_for_vsync(struct omap_overlay_manager *mgr) @@ -792,6 +802,18 @@ static void mgr_clear_shadow_dirty(struct omap_overlay_manager *mgr) } } +static int dss_mgr_connect_compat(struct omap_overlay_manager *mgr, + struct omap_dss_device *dst) +{ + return mgr->set_output(mgr, dst); +} + +static void dss_mgr_disconnect_compat(struct omap_overlay_manager *mgr, + struct omap_dss_device *dst) +{ + mgr->unset_output(mgr); +} + static void dss_mgr_start_update_compat(struct omap_overlay_manager *mgr) { struct mgr_priv_data *mp = get_mgr_priv(mgr); @@ -1156,7 +1178,7 @@ static void dss_mgr_get_info(struct omap_overlay_manager *mgr, } static int dss_mgr_set_output(struct omap_overlay_manager *mgr, - struct omap_dss_output *output) + struct omap_dss_device *output) { int r; @@ -1554,6 +1576,8 @@ static void dss_mgr_unregister_framedone_handler_compat(struct omap_overlay_mana } static const struct dss_mgr_ops apply_mgr_ops = { + .connect = dss_mgr_connect_compat, + .disconnect = dss_mgr_disconnect_compat, .start_update = dss_mgr_start_update_compat, .enable = dss_mgr_enable_compat, .disable = dss_mgr_disable_compat, @@ -1569,7 +1593,6 @@ static DEFINE_MUTEX(compat_init_lock); int omapdss_compat_init(void) { struct platform_device *pdev = dss_get_core_pdev(); - struct omap_dss_device *dssdev = NULL; int i, r; mutex_lock(&compat_init_lock); @@ -1579,7 +1602,7 @@ int omapdss_compat_init(void) apply_init_priv(); - dss_init_overlay_managers(pdev); + dss_init_overlay_managers_sysfs(pdev); dss_init_overlays(pdev); for (i = 0; i < omap_dss_get_num_overlay_managers(); i++) { @@ -1615,12 +1638,9 @@ int omapdss_compat_init(void) if (r) goto err_mgr_ops; - for_each_dss_dev(dssdev) { - r = display_init_sysfs(pdev, dssdev); - /* XXX uninit sysfs files on error */ - if (r) - goto err_disp_sysfs; - } + r = display_init_sysfs(pdev); + if (r) + goto err_disp_sysfs; dispc_runtime_get(); @@ -1637,12 +1657,13 @@ out: err_init_irq: dispc_runtime_put(); + display_uninit_sysfs(pdev); err_disp_sysfs: dss_uninstall_mgr_ops(); err_mgr_ops: - dss_uninit_overlay_managers(pdev); + dss_uninit_overlay_managers_sysfs(pdev); dss_uninit_overlays(pdev); compat_refcnt--; @@ -1656,7 +1677,6 @@ EXPORT_SYMBOL(omapdss_compat_init); void omapdss_compat_uninit(void) { struct platform_device *pdev = dss_get_core_pdev(); - struct omap_dss_device *dssdev = NULL; mutex_lock(&compat_init_lock); @@ -1665,12 +1685,11 @@ void omapdss_compat_uninit(void) dss_dispc_uninitialize_irq(); - for_each_dss_dev(dssdev) - display_uninit_sysfs(pdev, dssdev); + display_uninit_sysfs(pdev); dss_uninstall_mgr_ops(); - dss_uninit_overlay_managers(pdev); + dss_uninit_overlay_managers_sysfs(pdev); dss_uninit_overlays(pdev); out: mutex_unlock(&compat_init_lock); diff --git a/drivers/video/omap2/dss/core.c b/drivers/video/omap2/dss/core.c index 60cc6fee6548..1aeb274e30fc 100644 --- a/drivers/video/omap2/dss/core.c +++ b/drivers/video/omap2/dss/core.c @@ -53,6 +53,8 @@ static char *def_disp_name; module_param_named(def_disp, def_disp_name, charp, 0); MODULE_PARM_DESC(def_disp, "default display name"); +static bool dss_initialized; + const char *omapdss_get_default_display_name(void) { return core.default_display_name; @@ -66,6 +68,12 @@ enum omapdss_version omapdss_get_version(void) } EXPORT_SYMBOL(omapdss_get_version); +bool omapdss_is_initialized(void) +{ + return dss_initialized; +} +EXPORT_SYMBOL(omapdss_is_initialized); + struct platform_device *dss_get_core_pdev(void) { return core.pdev; @@ -80,7 +88,7 @@ struct regulator *dss_get_vdds_dsi(void) if (core.vdds_dsi_reg != NULL) return core.vdds_dsi_reg; - reg = regulator_get(&core.pdev->dev, "vdds_dsi"); + reg = devm_regulator_get(&core.pdev->dev, "vdds_dsi"); if (!IS_ERR(reg)) core.vdds_dsi_reg = reg; @@ -94,7 +102,7 @@ struct regulator *dss_get_vdds_sdi(void) if (core.vdds_sdi_reg != NULL) return core.vdds_sdi_reg; - reg = regulator_get(&core.pdev->dev, "vdds_sdi"); + reg = devm_regulator_get(&core.pdev->dev, "vdds_sdi"); if (!IS_ERR(reg)) core.vdds_sdi_reg = reg; @@ -235,6 +243,8 @@ static int __init omap_dss_probe(struct platform_device *pdev) if (def_disp_name) core.default_display_name = def_disp_name; + else if (pdata->default_display_name) + core.default_display_name = pdata->default_display_name; else if (pdata->default_device) core.default_display_name = pdata->default_device->name; @@ -282,37 +292,9 @@ static int dss_bus_match(struct device *dev, struct device_driver *driver) return strcmp(dssdev->driver_name, driver->name) == 0; } -static ssize_t device_name_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct omap_dss_device *dssdev = to_dss_device(dev); - return snprintf(buf, PAGE_SIZE, "%s\n", - dssdev->name ? - dssdev->name : ""); -} - -static struct device_attribute default_dev_attrs[] = { - __ATTR(name, S_IRUGO, device_name_show, NULL), - __ATTR_NULL, -}; - -static ssize_t driver_name_show(struct device_driver *drv, char *buf) -{ - struct omap_dss_driver *dssdrv = to_dss_driver(drv); - return snprintf(buf, PAGE_SIZE, "%s\n", - dssdrv->driver.name ? - dssdrv->driver.name : ""); -} -static struct driver_attribute default_drv_attrs[] = { - __ATTR(name, S_IRUGO, driver_name_show, NULL), - __ATTR_NULL, -}; - static struct bus_type dss_bus_type = { .name = "omapdss", .match = dss_bus_match, - .dev_attrs = default_dev_attrs, - .drv_attrs = default_drv_attrs, }; static void dss_bus_release(struct device *dev) @@ -369,6 +351,46 @@ static int dss_driver_remove(struct device *dev) return 0; } +static int omapdss_default_connect(struct omap_dss_device *dssdev) +{ + struct omap_dss_device *out; + struct omap_overlay_manager *mgr; + int r; + + out = dssdev->output; + + if (out == NULL) + return -ENODEV; + + mgr = omap_dss_get_overlay_manager(out->dispc_channel); + if (!mgr) + return -ENODEV; + + r = dss_mgr_connect(mgr, out); + if (r) + return r; + + return 0; +} + +static void omapdss_default_disconnect(struct omap_dss_device *dssdev) +{ + struct omap_dss_device *out; + struct omap_overlay_manager *mgr; + + out = dssdev->output; + + if (out == NULL) + return; + + mgr = out->manager; + + if (mgr == NULL) + return; + + dss_mgr_disconnect(mgr, out); +} + int omap_dss_register_driver(struct omap_dss_driver *dssdriver) { dssdriver->driver.bus = &dss_bus_type; @@ -382,6 +404,10 @@ int omap_dss_register_driver(struct omap_dss_driver *dssdriver) omapdss_default_get_recommended_bpp; if (dssdriver->get_timings == NULL) dssdriver->get_timings = omapdss_default_get_timings; + if (dssdriver->connect == NULL) + dssdriver->connect = omapdss_default_connect; + if (dssdriver->disconnect == NULL) + dssdriver->disconnect = omapdss_default_disconnect; return driver_register(&dssdriver->driver); } @@ -411,29 +437,33 @@ struct omap_dss_device *dss_alloc_and_init_device(struct device *parent) if (!dssdev) return NULL; - dssdev->dev.bus = &dss_bus_type; - dssdev->dev.parent = parent; - dssdev->dev.release = omap_dss_dev_release; - dev_set_name(&dssdev->dev, "display%d", disp_num_counter++); + dssdev->old_dev.bus = &dss_bus_type; + dssdev->old_dev.parent = parent; + dssdev->old_dev.release = omap_dss_dev_release; + dev_set_name(&dssdev->old_dev, "display%d", disp_num_counter++); - device_initialize(&dssdev->dev); + device_initialize(&dssdev->old_dev); return dssdev; } int dss_add_device(struct omap_dss_device *dssdev) { - return device_add(&dssdev->dev); + dssdev->dev = &dssdev->old_dev; + + omapdss_register_display(dssdev); + return device_add(&dssdev->old_dev); } void dss_put_device(struct omap_dss_device *dssdev) { - put_device(&dssdev->dev); + put_device(&dssdev->old_dev); } void dss_unregister_device(struct omap_dss_device *dssdev) { - device_unregister(&dssdev->dev); + device_unregister(&dssdev->old_dev); + omapdss_unregister_display(dssdev); } static int dss_unregister_dss_dev(struct device *dev, void *data) @@ -603,21 +633,13 @@ static int __init omap_dss_init(void) return r; } + dss_initialized = true; + return 0; } static void __exit omap_dss_exit(void) { - if (core.vdds_dsi_reg != NULL) { - regulator_put(core.vdds_dsi_reg); - core.vdds_dsi_reg = NULL; - } - - if (core.vdds_sdi_reg != NULL) { - regulator_put(core.vdds_sdi_reg); - core.vdds_sdi_reg = NULL; - } - omap_dss_unregister_drivers(); omap_dss_bus_unregister(); @@ -633,7 +655,15 @@ static int __init omap_dss_init(void) static int __init omap_dss_init2(void) { - return omap_dss_register_drivers(); + int r; + + r = omap_dss_register_drivers(); + if (r) + return r; + + dss_initialized = true; + + return 0; } core_initcall(omap_dss_init); diff --git a/drivers/video/omap2/dss/dispc-compat.c b/drivers/video/omap2/dss/dispc-compat.c index 928884c9a0a9..83779c2b292a 100644 --- a/drivers/video/omap2/dss/dispc-compat.c +++ b/drivers/video/omap2/dss/dispc-compat.c @@ -360,8 +360,7 @@ static void dispc_error_worker(struct work_struct *work) if (bit & errors) { DSSERR("FIFO UNDERFLOW on %s, disabling the overlay\n", ovl->name); - dispc_ovl_enable(ovl->id, false); - dispc_mgr_go(ovl->manager->id); + ovl->disable(ovl); msleep(50); } } diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c index b33b0169bb3b..02a7340111df 100644 --- a/drivers/video/omap2/dss/dispc.c +++ b/drivers/video/omap2/dss/dispc.c @@ -103,6 +103,7 @@ static struct { int irq; unsigned long core_clk_rate; + unsigned long tv_pclk_rate; u32 fifo_size[DISPC_MAX_NR_FIFOS]; /* maps which plane is using a fifo. fifo-id -> plane-id */ @@ -3071,22 +3072,15 @@ unsigned long dispc_mgr_pclk_rate(enum omap_channel channel) return r / pcd; } else { - enum dss_hdmi_venc_clk_source_select source; - - source = dss_get_hdmi_venc_clk_source(); - - switch (source) { - case DSS_VENC_TV_CLK: - return venc_get_pixel_clock(); - case DSS_HDMI_M_PCLK: - return hdmi_get_pixel_clock(); - default: - BUG(); - return 0; - } + return dispc.tv_pclk_rate; } } +void dispc_set_tv_pclk(unsigned long pclk) +{ + dispc.tv_pclk_rate = pclk; +} + unsigned long dispc_core_clk_rate(void) { return dispc.core_clk_rate; @@ -3710,6 +3704,8 @@ static int __init omap_dispchw_probe(struct platform_device *pdev) dispc_runtime_put(); + dss_init_overlay_managers(); + dss_debugfs_create_file("dispc", dispc_dump_regs); return 0; @@ -3723,6 +3719,8 @@ static int __exit omap_dispchw_remove(struct platform_device *pdev) { pm_runtime_disable(&pdev->dev); + dss_uninit_overlay_managers(); + return 0; } diff --git a/drivers/video/omap2/dss/display-sysfs.c b/drivers/video/omap2/dss/display-sysfs.c index 18211a9ab354..21d7f77df702 100644 --- a/drivers/video/omap2/dss/display-sysfs.c +++ b/drivers/video/omap2/dss/display-sysfs.c @@ -22,42 +22,69 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/jiffies.h> #include <linux/platform_device.h> +#include <linux/sysfs.h> #include <video/omapdss.h> #include "dss.h" -#include "dss_features.h" + +static struct omap_dss_device *to_dss_device_sysfs(struct device *dev) +{ + struct omap_dss_device *dssdev = NULL; + + for_each_dss_dev(dssdev) { + if (dssdev->dev == dev) { + omap_dss_put_device(dssdev); + return dssdev; + } + } + + return NULL; +} + +static ssize_t display_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); + + return snprintf(buf, PAGE_SIZE, "%s\n", + dssdev->name ? + dssdev->name : ""); +} static ssize_t display_enabled_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct omap_dss_device *dssdev = to_dss_device(dev); - bool enabled = dssdev->state != OMAP_DSS_DISPLAY_DISABLED; + struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); - return snprintf(buf, PAGE_SIZE, "%d\n", enabled); + return snprintf(buf, PAGE_SIZE, "%d\n", + omapdss_device_is_enabled(dssdev)); } static ssize_t display_enabled_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - struct omap_dss_device *dssdev = to_dss_device(dev); + struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); int r; - bool enabled; + bool enable; - r = strtobool(buf, &enabled); + r = strtobool(buf, &enable); if (r) return r; - if (enabled != (dssdev->state != OMAP_DSS_DISPLAY_DISABLED)) { - if (enabled) { - r = dssdev->driver->enable(dssdev); - if (r) - return r; - } else { - dssdev->driver->disable(dssdev); - } + if (enable == omapdss_device_is_enabled(dssdev)) + return size; + + if (omapdss_device_is_connected(dssdev) == false) + return -ENODEV; + + if (enable) { + r = dssdev->driver->enable(dssdev); + if (r) + return r; + } else { + dssdev->driver->disable(dssdev); } return size; @@ -66,7 +93,7 @@ static ssize_t display_enabled_store(struct device *dev, static ssize_t display_tear_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct omap_dss_device *dssdev = to_dss_device(dev); + struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); return snprintf(buf, PAGE_SIZE, "%d\n", dssdev->driver->get_te ? dssdev->driver->get_te(dssdev) : 0); @@ -75,7 +102,7 @@ static ssize_t display_tear_show(struct device *dev, static ssize_t display_tear_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - struct omap_dss_device *dssdev = to_dss_device(dev); + struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); int r; bool te; @@ -96,7 +123,7 @@ static ssize_t display_tear_store(struct device *dev, static ssize_t display_timings_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct omap_dss_device *dssdev = to_dss_device(dev); + struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); struct omap_video_timings t; if (!dssdev->driver->get_timings) @@ -113,7 +140,7 @@ static ssize_t display_timings_show(struct device *dev, static ssize_t display_timings_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - struct omap_dss_device *dssdev = to_dss_device(dev); + struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); struct omap_video_timings t = dssdev->panel.timings; int r, found; @@ -152,7 +179,7 @@ static ssize_t display_timings_store(struct device *dev, static ssize_t display_rotate_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct omap_dss_device *dssdev = to_dss_device(dev); + struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); int rotate; if (!dssdev->driver->get_rotate) return -ENOENT; @@ -163,7 +190,7 @@ static ssize_t display_rotate_show(struct device *dev, static ssize_t display_rotate_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - struct omap_dss_device *dssdev = to_dss_device(dev); + struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); int rot, r; if (!dssdev->driver->set_rotate || !dssdev->driver->get_rotate) @@ -183,7 +210,7 @@ static ssize_t display_rotate_store(struct device *dev, static ssize_t display_mirror_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct omap_dss_device *dssdev = to_dss_device(dev); + struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); int mirror; if (!dssdev->driver->get_mirror) return -ENOENT; @@ -194,7 +221,7 @@ static ssize_t display_mirror_show(struct device *dev, static ssize_t display_mirror_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - struct omap_dss_device *dssdev = to_dss_device(dev); + struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); int r; bool mirror; @@ -215,7 +242,7 @@ static ssize_t display_mirror_store(struct device *dev, static ssize_t display_wss_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct omap_dss_device *dssdev = to_dss_device(dev); + struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); unsigned int wss; if (!dssdev->driver->get_wss) @@ -229,7 +256,7 @@ static ssize_t display_wss_show(struct device *dev, static ssize_t display_wss_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - struct omap_dss_device *dssdev = to_dss_device(dev); + struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); u32 wss; int r; @@ -250,6 +277,7 @@ static ssize_t display_wss_store(struct device *dev, return size; } +static DEVICE_ATTR(name, S_IRUGO, display_name_show, NULL); static DEVICE_ATTR(enabled, S_IRUGO|S_IWUSR, display_enabled_show, display_enabled_store); static DEVICE_ATTR(tear_elim, S_IRUGO|S_IWUSR, @@ -263,59 +291,55 @@ static DEVICE_ATTR(mirror, S_IRUGO|S_IWUSR, static DEVICE_ATTR(wss, S_IRUGO|S_IWUSR, display_wss_show, display_wss_store); -static struct device_attribute *display_sysfs_attrs[] = { - &dev_attr_enabled, - &dev_attr_tear_elim, - &dev_attr_timings, - &dev_attr_rotate, - &dev_attr_mirror, - &dev_attr_wss, +static const struct attribute *display_sysfs_attrs[] = { + &dev_attr_name.attr, + &dev_attr_enabled.attr, + &dev_attr_tear_elim.attr, + &dev_attr_timings.attr, + &dev_attr_rotate.attr, + &dev_attr_mirror.attr, + &dev_attr_wss.attr, NULL }; -int display_init_sysfs(struct platform_device *pdev, - struct omap_dss_device *dssdev) +int display_init_sysfs(struct platform_device *pdev) { - struct device_attribute *attr; - int i, r; + struct omap_dss_device *dssdev = NULL; + int r; - /* create device sysfs files */ - i = 0; - while ((attr = display_sysfs_attrs[i++]) != NULL) { - r = device_create_file(&dssdev->dev, attr); - if (r) { - for (i = i - 2; i >= 0; i--) { - attr = display_sysfs_attrs[i]; - device_remove_file(&dssdev->dev, attr); - } + for_each_dss_dev(dssdev) { + struct kobject *kobj = &dssdev->dev->kobj; - DSSERR("failed to create sysfs file\n"); - return r; + r = sysfs_create_files(kobj, display_sysfs_attrs); + if (r) { + DSSERR("failed to create sysfs files\n"); + goto err; } - } - /* create display? sysfs links */ - r = sysfs_create_link(&pdev->dev.kobj, &dssdev->dev.kobj, - dev_name(&dssdev->dev)); - if (r) { - while ((attr = display_sysfs_attrs[i++]) != NULL) - device_remove_file(&dssdev->dev, attr); + r = sysfs_create_link(&pdev->dev.kobj, kobj, dssdev->alias); + if (r) { + sysfs_remove_files(kobj, display_sysfs_attrs); - DSSERR("failed to create sysfs display link\n"); - return r; + DSSERR("failed to create sysfs display link\n"); + goto err; + } } return 0; + +err: + display_uninit_sysfs(pdev); + + return r; } -void display_uninit_sysfs(struct platform_device *pdev, - struct omap_dss_device *dssdev) +void display_uninit_sysfs(struct platform_device *pdev) { - struct device_attribute *attr; - int i = 0; - - sysfs_remove_link(&pdev->dev.kobj, dev_name(&dssdev->dev)); + struct omap_dss_device *dssdev = NULL; - while ((attr = display_sysfs_attrs[i++]) != NULL) - device_remove_file(&dssdev->dev, attr); + for_each_dss_dev(dssdev) { + sysfs_remove_link(&pdev->dev.kobj, dssdev->alias); + sysfs_remove_files(&dssdev->dev->kobj, + display_sysfs_attrs); + } } diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c index 0aa8ad8f9667..fafe7c941a60 100644 --- a/drivers/video/omap2/dss/display.c +++ b/drivers/video/omap2/dss/display.c @@ -61,6 +61,7 @@ int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev) case OMAP_DISPLAY_TYPE_VENC: case OMAP_DISPLAY_TYPE_SDI: case OMAP_DISPLAY_TYPE_HDMI: + case OMAP_DISPLAY_TYPE_DVI: return 24; default: BUG(); @@ -76,110 +77,154 @@ void omapdss_default_get_timings(struct omap_dss_device *dssdev, } EXPORT_SYMBOL(omapdss_default_get_timings); -static int dss_suspend_device(struct device *dev, void *data) +int dss_suspend_all_devices(void) { - struct omap_dss_device *dssdev = to_dss_device(dev); - - if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { - dssdev->activate_after_resume = false; - return 0; - } - - dssdev->driver->disable(dssdev); - - dssdev->activate_after_resume = true; + struct omap_dss_device *dssdev = NULL; - return 0; -} + for_each_dss_dev(dssdev) { + if (!dssdev->driver) + continue; -int dss_suspend_all_devices(void) -{ - int r; - struct bus_type *bus = dss_get_bus(); - - r = bus_for_each_dev(bus, NULL, NULL, dss_suspend_device); - if (r) { - /* resume all displays that were suspended */ - dss_resume_all_devices(); - return r; + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { + dssdev->driver->disable(dssdev); + dssdev->activate_after_resume = true; + } else { + dssdev->activate_after_resume = false; + } } return 0; } -static int dss_resume_device(struct device *dev, void *data) +int dss_resume_all_devices(void) { - int r; - struct omap_dss_device *dssdev = to_dss_device(dev); + struct omap_dss_device *dssdev = NULL; - if (dssdev->activate_after_resume) { - r = dssdev->driver->enable(dssdev); - if (r) - return r; - } + for_each_dss_dev(dssdev) { + if (!dssdev->driver) + continue; - dssdev->activate_after_resume = false; + if (dssdev->activate_after_resume) { + dssdev->driver->enable(dssdev); + dssdev->activate_after_resume = false; + } + } return 0; } -int dss_resume_all_devices(void) +void dss_disable_all_devices(void) { - struct bus_type *bus = dss_get_bus(); + struct omap_dss_device *dssdev = NULL; + + for_each_dss_dev(dssdev) { + if (!dssdev->driver) + continue; - return bus_for_each_dev(bus, NULL, NULL, dss_resume_device); + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) + dssdev->driver->disable(dssdev); + } } -static int dss_disable_device(struct device *dev, void *data) +static LIST_HEAD(panel_list); +static DEFINE_MUTEX(panel_list_mutex); +static int disp_num_counter; + +int omapdss_register_display(struct omap_dss_device *dssdev) { - struct omap_dss_device *dssdev = to_dss_device(dev); + struct omap_dss_driver *drv = dssdev->driver; - if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) - dssdev->driver->disable(dssdev); + snprintf(dssdev->alias, sizeof(dssdev->alias), + "display%d", disp_num_counter++); + if (drv && drv->get_resolution == NULL) + drv->get_resolution = omapdss_default_get_resolution; + if (drv && drv->get_recommended_bpp == NULL) + drv->get_recommended_bpp = omapdss_default_get_recommended_bpp; + if (drv && drv->get_timings == NULL) + drv->get_timings = omapdss_default_get_timings; + + mutex_lock(&panel_list_mutex); + list_add_tail(&dssdev->panel_list, &panel_list); + mutex_unlock(&panel_list_mutex); return 0; } +EXPORT_SYMBOL(omapdss_register_display); -void dss_disable_all_devices(void) +void omapdss_unregister_display(struct omap_dss_device *dssdev) { - struct bus_type *bus = dss_get_bus(); - bus_for_each_dev(bus, NULL, NULL, dss_disable_device); + mutex_lock(&panel_list_mutex); + list_del(&dssdev->panel_list); + mutex_unlock(&panel_list_mutex); } +EXPORT_SYMBOL(omapdss_unregister_display); - -void omap_dss_get_device(struct omap_dss_device *dssdev) +struct omap_dss_device *omap_dss_get_device(struct omap_dss_device *dssdev) { - get_device(&dssdev->dev); + if (!try_module_get(dssdev->owner)) + return NULL; + + if (get_device(dssdev->dev) == NULL) { + module_put(dssdev->owner); + return NULL; + } + + return dssdev; } EXPORT_SYMBOL(omap_dss_get_device); void omap_dss_put_device(struct omap_dss_device *dssdev) { - put_device(&dssdev->dev); + put_device(dssdev->dev); + module_put(dssdev->owner); } EXPORT_SYMBOL(omap_dss_put_device); -/* ref count of the found device is incremented. ref count - * of from-device is decremented. */ +/* + * ref count of the found device is incremented. + * ref count of from-device is decremented. + */ struct omap_dss_device *omap_dss_get_next_device(struct omap_dss_device *from) { - struct device *dev; - struct device *dev_start = NULL; - struct omap_dss_device *dssdev = NULL; + struct list_head *l; + struct omap_dss_device *dssdev; + + mutex_lock(&panel_list_mutex); - int match(struct device *dev, void *data) - { - return 1; + if (list_empty(&panel_list)) { + dssdev = NULL; + goto out; } - if (from) - dev_start = &from->dev; - dev = bus_find_device(dss_get_bus(), dev_start, NULL, match); - if (dev) - dssdev = to_dss_device(dev); - if (from) - put_device(&from->dev); + if (from == NULL) { + dssdev = list_first_entry(&panel_list, struct omap_dss_device, + panel_list); + omap_dss_get_device(dssdev); + goto out; + } + + omap_dss_put_device(from); + + list_for_each(l, &panel_list) { + dssdev = list_entry(l, struct omap_dss_device, panel_list); + if (dssdev == from) { + if (list_is_last(l, &panel_list)) { + dssdev = NULL; + goto out; + } + + dssdev = list_entry(l->next, struct omap_dss_device, + panel_list); + omap_dss_get_device(dssdev); + goto out; + } + } + WARN(1, "'from' dssdev not found\n"); + + dssdev = NULL; +out: + mutex_unlock(&panel_list_mutex); return dssdev; } EXPORT_SYMBOL(omap_dss_get_next_device); @@ -198,24 +243,72 @@ struct omap_dss_device *omap_dss_find_device(void *data, } EXPORT_SYMBOL(omap_dss_find_device); -int omap_dss_start_device(struct omap_dss_device *dssdev) +void videomode_to_omap_video_timings(const struct videomode *vm, + struct omap_video_timings *ovt) { - if (!dssdev->driver) { - DSSDBG("no driver\n"); - return -ENODEV; - } - - if (!try_module_get(dssdev->dev.driver->owner)) { - return -ENODEV; - } - - return 0; + memset(ovt, 0, sizeof(*ovt)); + + ovt->pixel_clock = vm->pixelclock / 1000; + ovt->x_res = vm->hactive; + ovt->hbp = vm->hback_porch; + ovt->hfp = vm->hfront_porch; + ovt->hsw = vm->hsync_len; + ovt->y_res = vm->vactive; + ovt->vbp = vm->vback_porch; + ovt->vfp = vm->vfront_porch; + ovt->vsw = vm->vsync_len; + + ovt->vsync_level = vm->flags & DISPLAY_FLAGS_VSYNC_HIGH ? + OMAPDSS_SIG_ACTIVE_HIGH : + OMAPDSS_SIG_ACTIVE_LOW; + ovt->hsync_level = vm->flags & DISPLAY_FLAGS_HSYNC_HIGH ? + OMAPDSS_SIG_ACTIVE_HIGH : + OMAPDSS_SIG_ACTIVE_LOW; + ovt->de_level = vm->flags & DISPLAY_FLAGS_DE_HIGH ? + OMAPDSS_SIG_ACTIVE_HIGH : + OMAPDSS_SIG_ACTIVE_HIGH; + ovt->data_pclk_edge = vm->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE ? + OMAPDSS_DRIVE_SIG_RISING_EDGE : + OMAPDSS_DRIVE_SIG_FALLING_EDGE; + + ovt->sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES; } -EXPORT_SYMBOL(omap_dss_start_device); +EXPORT_SYMBOL(videomode_to_omap_video_timings); -void omap_dss_stop_device(struct omap_dss_device *dssdev) +void omap_video_timings_to_videomode(const struct omap_video_timings *ovt, + struct videomode *vm) { - module_put(dssdev->dev.driver->owner); + memset(vm, 0, sizeof(*vm)); + + vm->pixelclock = ovt->pixel_clock * 1000; + + vm->hactive = ovt->x_res; + vm->hback_porch = ovt->hbp; + vm->hfront_porch = ovt->hfp; + vm->hsync_len = ovt->hsw; + vm->vactive = ovt->y_res; + vm->vback_porch = ovt->vbp; + vm->vfront_porch = ovt->vfp; + vm->vsync_len = ovt->vsw; + + if (ovt->hsync_level == OMAPDSS_SIG_ACTIVE_HIGH) + vm->flags |= DISPLAY_FLAGS_HSYNC_HIGH; + else + vm->flags |= DISPLAY_FLAGS_HSYNC_LOW; + + if (ovt->vsync_level == OMAPDSS_SIG_ACTIVE_HIGH) + vm->flags |= DISPLAY_FLAGS_VSYNC_HIGH; + else + vm->flags |= DISPLAY_FLAGS_VSYNC_LOW; + + if (ovt->de_level == OMAPDSS_SIG_ACTIVE_HIGH) + vm->flags |= DISPLAY_FLAGS_DE_HIGH; + else + vm->flags |= DISPLAY_FLAGS_DE_LOW; + + if (ovt->data_pclk_edge == OMAPDSS_DRIVE_SIG_RISING_EDGE) + vm->flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE; + else + vm->flags |= DISPLAY_FLAGS_PIXDATA_NEGEDGE; } -EXPORT_SYMBOL(omap_dss_stop_device); - +EXPORT_SYMBOL(omap_video_timings_to_videomode); diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c index 0bdcd4192176..a6b331ef7763 100644 --- a/drivers/video/omap2/dss/dpi.c +++ b/drivers/video/omap2/dss/dpi.c @@ -37,6 +37,8 @@ #include "dss_features.h" static struct { + struct platform_device *pdev; + struct regulator *vdds_dsi_reg; struct platform_device *dsidev; @@ -46,7 +48,7 @@ static struct { struct dss_lcd_mgr_config mgr_config; int data_lines; - struct omap_dss_output output; + struct omap_dss_device output; } dpi; static struct platform_device *dpi_get_dsidev(enum omap_channel channel) @@ -345,7 +347,7 @@ static void dpi_config_lcd_manager(struct omap_overlay_manager *mgr) int omapdss_dpi_display_enable(struct omap_dss_device *dssdev) { - struct omap_dss_output *out = &dpi.output; + struct omap_dss_device *out = &dpi.output; int r; mutex_lock(&dpi.lock); @@ -362,12 +364,6 @@ int omapdss_dpi_display_enable(struct omap_dss_device *dssdev) goto err_no_out_mgr; } - r = omap_dss_start_device(dssdev); - if (r) { - DSSERR("failed to start device\n"); - goto err_start_dev; - } - if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI)) { r = regulator_enable(dpi.vdds_dsi_reg); if (r) @@ -422,8 +418,6 @@ err_get_dispc: if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI)) regulator_disable(dpi.vdds_dsi_reg); err_reg_enable: - omap_dss_stop_device(dssdev); -err_start_dev: err_no_out_mgr: err_no_reg: mutex_unlock(&dpi.lock); @@ -450,8 +444,6 @@ void omapdss_dpi_display_disable(struct omap_dss_device *dssdev) if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI)) regulator_disable(dpi.vdds_dsi_reg); - omap_dss_stop_device(dssdev); - mutex_unlock(&dpi.lock); } EXPORT_SYMBOL(omapdss_dpi_display_disable); @@ -469,6 +461,16 @@ void omapdss_dpi_set_timings(struct omap_dss_device *dssdev, } EXPORT_SYMBOL(omapdss_dpi_set_timings); +static void dpi_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + mutex_lock(&dpi.lock); + + *timings = dpi.timings; + + mutex_unlock(&dpi.lock); +} + int dpi_check_timings(struct omap_dss_device *dssdev, struct omap_video_timings *timings) { @@ -542,6 +544,50 @@ static int dpi_verify_dsi_pll(struct platform_device *dsidev) return 0; } +static int dpi_init_regulator(void) +{ + struct regulator *vdds_dsi; + + if (!dss_has_feature(FEAT_DPI_USES_VDDS_DSI)) + return 0; + + if (dpi.vdds_dsi_reg) + return 0; + + vdds_dsi = dss_get_vdds_dsi(); + + if (IS_ERR(vdds_dsi)) { + vdds_dsi = devm_regulator_get(&dpi.pdev->dev, "vdds_dsi"); + if (IS_ERR(vdds_dsi)) { + DSSERR("can't get VDDS_DSI regulator\n"); + return PTR_ERR(vdds_dsi); + } + } + + dpi.vdds_dsi_reg = vdds_dsi; + + return 0; +} + +static void dpi_init_pll(void) +{ + struct platform_device *dsidev; + + if (dpi.dsidev) + return; + + dsidev = dpi_get_dsidev(dpi.output.dispc_channel); + if (!dsidev) + return; + + if (dpi_verify_dsi_pll(dsidev)) { + DSSWARN("DSI PLL not operational\n"); + return; + } + + dpi.dsidev = dsidev; +} + /* * Return a hardcoded channel for the DPI output. This should work for * current use cases, but this can be later expanded to either resolve @@ -572,41 +618,6 @@ static enum omap_channel dpi_get_channel(void) } } -static int dpi_init_display(struct omap_dss_device *dssdev) -{ - struct platform_device *dsidev; - - DSSDBG("init_display\n"); - - if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI) && - dpi.vdds_dsi_reg == NULL) { - struct regulator *vdds_dsi; - - vdds_dsi = dss_get_vdds_dsi(); - - if (IS_ERR(vdds_dsi)) { - DSSERR("can't get VDDS_DSI regulator\n"); - return PTR_ERR(vdds_dsi); - } - - dpi.vdds_dsi_reg = vdds_dsi; - } - - dsidev = dpi_get_dsidev(dpi.output.dispc_channel); - - if (dsidev && dpi_verify_dsi_pll(dsidev)) { - dsidev = NULL; - DSSWARN("DSI PLL not operational\n"); - } - - if (dsidev) - DSSDBG("using DSI PLL for DPI clock\n"); - - dpi.dsidev = dsidev; - - return 0; -} - static struct omap_dss_device *dpi_find_dssdev(struct platform_device *pdev) { struct omap_dss_board_info *pdata = pdev->dev.platform_data; @@ -646,19 +657,18 @@ static int dpi_probe_pdata(struct platform_device *dpidev) if (!plat_dssdev) return 0; + r = dpi_init_regulator(); + if (r) + return r; + + dpi_init_pll(); + dssdev = dss_alloc_and_init_device(&dpidev->dev); if (!dssdev) return -ENOMEM; dss_copy_device_pdata(dssdev, plat_dssdev); - r = dpi_init_display(dssdev); - if (r) { - DSSERR("device %s init failed: %d\n", dssdev->name, r); - dss_put_device(dssdev); - return r; - } - r = omapdss_output_set_device(&dpi.output, dssdev); if (r) { DSSERR("failed to connect output to new device: %s\n", @@ -678,41 +688,108 @@ static int dpi_probe_pdata(struct platform_device *dpidev) return 0; } +static int dpi_connect(struct omap_dss_device *dssdev, + struct omap_dss_device *dst) +{ + struct omap_overlay_manager *mgr; + int r; + + r = dpi_init_regulator(); + if (r) + return r; + + dpi_init_pll(); + + mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); + if (!mgr) + return -ENODEV; + + r = dss_mgr_connect(mgr, dssdev); + if (r) + return r; + + r = omapdss_output_set_device(dssdev, dst); + if (r) { + DSSERR("failed to connect output to new device: %s\n", + dst->name); + dss_mgr_disconnect(mgr, dssdev); + return r; + } + + return 0; +} + +static void dpi_disconnect(struct omap_dss_device *dssdev, + struct omap_dss_device *dst) +{ + WARN_ON(dst != dssdev->device); + + if (dst != dssdev->device) + return; + + omapdss_output_unset_device(dssdev); + + if (dssdev->manager) + dss_mgr_disconnect(dssdev->manager, dssdev); +} + +static const struct omapdss_dpi_ops dpi_ops = { + .connect = dpi_connect, + .disconnect = dpi_disconnect, + + .enable = omapdss_dpi_display_enable, + .disable = omapdss_dpi_display_disable, + + .check_timings = dpi_check_timings, + .set_timings = omapdss_dpi_set_timings, + .get_timings = dpi_get_timings, + + .set_data_lines = omapdss_dpi_set_data_lines, +}; + static void dpi_init_output(struct platform_device *pdev) { - struct omap_dss_output *out = &dpi.output; + struct omap_dss_device *out = &dpi.output; - out->pdev = pdev; + out->dev = &pdev->dev; out->id = OMAP_DSS_OUTPUT_DPI; - out->type = OMAP_DISPLAY_TYPE_DPI; + out->output_type = OMAP_DISPLAY_TYPE_DPI; out->name = "dpi.0"; out->dispc_channel = dpi_get_channel(); + out->ops.dpi = &dpi_ops; + out->owner = THIS_MODULE; - dss_register_output(out); + omapdss_register_output(out); } static void __exit dpi_uninit_output(struct platform_device *pdev) { - struct omap_dss_output *out = &dpi.output; + struct omap_dss_device *out = &dpi.output; - dss_unregister_output(out); + omapdss_unregister_output(out); } static int omap_dpi_probe(struct platform_device *pdev) { int r; + dpi.pdev = pdev; + mutex_init(&dpi.lock); dpi_init_output(pdev); - r = dpi_probe_pdata(pdev); - if (r) { - dpi_uninit_output(pdev); - return r; + if (pdev->dev.platform_data) { + r = dpi_probe_pdata(pdev); + if (r) + goto err_probe; } return 0; + +err_probe: + dpi_uninit_output(pdev); + return r; } static int __exit omap_dpi_remove(struct platform_device *pdev) diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c index a73dedc33101..99a043b08f0d 100644 --- a/drivers/video/omap2/dss/dsi.c +++ b/drivers/video/omap2/dss/dsi.c @@ -363,7 +363,7 @@ struct dsi_data { enum omap_dss_dsi_mode mode; struct omap_dss_dsi_videomode_timings vm_timings; - struct omap_dss_output output; + struct omap_dss_device output; }; struct dsi_packet_sent_handler_data { @@ -383,12 +383,21 @@ static inline struct dsi_data *dsi_get_dsidrv_data(struct platform_device *dside static inline struct platform_device *dsi_get_dsidev_from_dssdev(struct omap_dss_device *dssdev) { - return dssdev->output->pdev; + /* HACK: dssdev can be either the panel device, when using old API, or + * the dsi device itself, when using the new API. So we solve this for + * now by checking the dssdev->id. This will be removed when the old API + * is removed. + */ + if (dssdev->id == OMAP_DSS_OUTPUT_DSI1 || + dssdev->id == OMAP_DSS_OUTPUT_DSI2) + return to_platform_device(dssdev->dev); + + return to_platform_device(dssdev->output->dev); } struct platform_device *dsi_get_dsidev_from_id(int module) { - struct omap_dss_output *out; + struct omap_dss_device *out; enum omap_dss_output_id id; switch (module) { @@ -404,7 +413,7 @@ struct platform_device *dsi_get_dsidev_from_id(int module) out = omap_dss_get_output(id); - return out ? out->pdev : NULL; + return out ? to_platform_device(out->dev) : NULL; } static inline void dsi_write_reg(struct platform_device *dsidev, @@ -1114,6 +1123,30 @@ void dsi_runtime_put(struct platform_device *dsidev) WARN_ON(r < 0 && r != -ENOSYS); } +static int dsi_regulator_init(struct platform_device *dsidev) +{ + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + struct regulator *vdds_dsi; + + if (dsi->vdds_dsi_reg != NULL) + return 0; + + vdds_dsi = devm_regulator_get(&dsi->pdev->dev, "vdds_dsi"); + + /* DT HACK: try VCXIO to make omapdss work for o4 sdp/panda */ + if (IS_ERR(vdds_dsi)) + vdds_dsi = devm_regulator_get(&dsi->pdev->dev, "VCXIO"); + + if (IS_ERR(vdds_dsi)) { + DSSERR("can't get VDDS_DSI regulator\n"); + return PTR_ERR(vdds_dsi); + } + + dsi->vdds_dsi_reg = vdds_dsi; + + return 0; +} + /* source clock for DSI PLL. this could also be PCLKFREE */ static inline void dsi_enable_pll_clock(struct platform_device *dsidev, bool enable) @@ -1592,22 +1625,9 @@ int dsi_pll_init(struct platform_device *dsidev, bool enable_hsclk, */ enable_hsclk = enable_hsdiv = true; - if (dsi->vdds_dsi_reg == NULL) { - struct regulator *vdds_dsi; - - vdds_dsi = regulator_get(&dsi->pdev->dev, "vdds_dsi"); - - /* DT HACK: try VCXIO to make omapdss work for o4 sdp/panda */ - if (IS_ERR(vdds_dsi)) - vdds_dsi = regulator_get(&dsi->pdev->dev, "VCXIO"); - - if (IS_ERR(vdds_dsi)) { - DSSERR("can't get VDDS_DSI regulator\n"); - return PTR_ERR(vdds_dsi); - } - - dsi->vdds_dsi_reg = vdds_dsi; - } + r = dsi_regulator_init(dsidev); + if (r) + return r; dsi_enable_pll_clock(dsidev, 1); /* @@ -4122,7 +4142,7 @@ int dsi_enable_video_output(struct omap_dss_device *dssdev, int channel) struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); struct omap_overlay_manager *mgr = dsi->output.manager; int bpp = dsi_get_pixel_size(dsi->pix_fmt); - struct omap_dss_output *out = &dsi->output; + struct omap_dss_device *out = &dsi->output; u8 data_type; u16 word_count; int r; @@ -4581,12 +4601,6 @@ int omapdss_dsi_display_enable(struct omap_dss_device *dssdev) mutex_lock(&dsi->lock); - r = omap_dss_start_device(dssdev); - if (r) { - DSSERR("failed to start device\n"); - goto err_start_dev; - } - r = dsi_runtime_get(dsidev); if (r) goto err_get_dsi; @@ -4607,8 +4621,6 @@ err_init_dsi: dsi_enable_pll_clock(dsidev, 0); dsi_runtime_put(dsidev); err_get_dsi: - omap_dss_stop_device(dssdev); -err_start_dev: mutex_unlock(&dsi->lock); DSSDBG("dsi_display_enable FAILED\n"); return r; @@ -4637,8 +4649,6 @@ void omapdss_dsi_display_disable(struct omap_dss_device *dssdev, dsi_runtime_put(dsidev); dsi_enable_pll_clock(dsidev, 0); - omap_dss_stop_device(dssdev); - mutex_unlock(&dsi->lock); } EXPORT_SYMBOL(omapdss_dsi_display_disable); @@ -5225,34 +5235,6 @@ static enum omap_channel dsi_get_channel(int module_id) } } -static int dsi_init_display(struct omap_dss_device *dssdev) -{ - struct platform_device *dsidev = - dsi_get_dsidev_from_id(dssdev->phy.dsi.module); - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - - DSSDBG("DSI init\n"); - - if (dsi->vdds_dsi_reg == NULL) { - struct regulator *vdds_dsi; - - vdds_dsi = regulator_get(&dsi->pdev->dev, "vdds_dsi"); - - /* DT HACK: try VCXIO to make omapdss work for o4 sdp/panda */ - if (IS_ERR(vdds_dsi)) - vdds_dsi = regulator_get(&dsi->pdev->dev, "VCXIO"); - - if (IS_ERR(vdds_dsi)) { - DSSERR("can't get VDDS_DSI regulator\n"); - return PTR_ERR(vdds_dsi); - } - - dsi->vdds_dsi_reg = vdds_dsi; - } - - return 0; -} - int omap_dsi_request_vc(struct omap_dss_device *dssdev, int *channel) { struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); @@ -5410,19 +5392,16 @@ static int dsi_probe_pdata(struct platform_device *dsidev) if (!plat_dssdev) return 0; + r = dsi_regulator_init(dsidev); + if (r) + return r; + dssdev = dss_alloc_and_init_device(&dsidev->dev); if (!dssdev) return -ENOMEM; dss_copy_device_pdata(dssdev, plat_dssdev); - r = dsi_init_display(dssdev); - if (r) { - DSSERR("device %s init failed: %d\n", dssdev->name, r); - dss_put_device(dssdev); - return r; - } - r = omapdss_output_set_device(&dsi->output, dssdev); if (r) { DSSERR("failed to connect output to new device: %s\n", @@ -5442,28 +5421,113 @@ static int dsi_probe_pdata(struct platform_device *dsidev) return 0; } +static int dsi_connect(struct omap_dss_device *dssdev, + struct omap_dss_device *dst) +{ + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct omap_overlay_manager *mgr; + int r; + + r = dsi_regulator_init(dsidev); + if (r) + return r; + + mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); + if (!mgr) + return -ENODEV; + + r = dss_mgr_connect(mgr, dssdev); + if (r) + return r; + + r = omapdss_output_set_device(dssdev, dst); + if (r) { + DSSERR("failed to connect output to new device: %s\n", + dssdev->name); + dss_mgr_disconnect(mgr, dssdev); + return r; + } + + return 0; +} + +static void dsi_disconnect(struct omap_dss_device *dssdev, + struct omap_dss_device *dst) +{ + WARN_ON(dst != dssdev->device); + + if (dst != dssdev->device) + return; + + omapdss_output_unset_device(dssdev); + + if (dssdev->manager) + dss_mgr_disconnect(dssdev->manager, dssdev); +} + +static const struct omapdss_dsi_ops dsi_ops = { + .connect = dsi_connect, + .disconnect = dsi_disconnect, + + .bus_lock = dsi_bus_lock, + .bus_unlock = dsi_bus_unlock, + + .enable = omapdss_dsi_display_enable, + .disable = omapdss_dsi_display_disable, + + .enable_hs = omapdss_dsi_vc_enable_hs, + + .configure_pins = omapdss_dsi_configure_pins, + .set_config = omapdss_dsi_set_config, + + .enable_video_output = dsi_enable_video_output, + .disable_video_output = dsi_disable_video_output, + + .update = omap_dsi_update, + + .enable_te = omapdss_dsi_enable_te, + + .request_vc = omap_dsi_request_vc, + .set_vc_id = omap_dsi_set_vc_id, + .release_vc = omap_dsi_release_vc, + + .dcs_write = dsi_vc_dcs_write, + .dcs_write_nosync = dsi_vc_dcs_write_nosync, + .dcs_read = dsi_vc_dcs_read, + + .gen_write = dsi_vc_generic_write, + .gen_write_nosync = dsi_vc_generic_write_nosync, + .gen_read = dsi_vc_generic_read, + + .bta_sync = dsi_vc_send_bta_sync, + + .set_max_rx_packet_size = dsi_vc_set_max_rx_packet_size, +}; + static void dsi_init_output(struct platform_device *dsidev) { struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - struct omap_dss_output *out = &dsi->output; + struct omap_dss_device *out = &dsi->output; - out->pdev = dsidev; + out->dev = &dsidev->dev; out->id = dsi->module_id == 0 ? OMAP_DSS_OUTPUT_DSI1 : OMAP_DSS_OUTPUT_DSI2; - out->type = OMAP_DISPLAY_TYPE_DSI; + out->output_type = OMAP_DISPLAY_TYPE_DSI; out->name = dsi->module_id == 0 ? "dsi.0" : "dsi.1"; out->dispc_channel = dsi_get_channel(dsi->module_id); + out->ops.dsi = &dsi_ops; + out->owner = THIS_MODULE; - dss_register_output(out); + omapdss_register_output(out); } static void dsi_uninit_output(struct platform_device *dsidev) { struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - struct omap_dss_output *out = &dsi->output; + struct omap_dss_device *out = &dsi->output; - dss_unregister_output(out); + omapdss_unregister_output(out); } /* DSI1 HW IP initialisation */ @@ -5563,12 +5627,10 @@ static int omap_dsihw_probe(struct platform_device *dsidev) dsi_init_output(dsidev); - r = dsi_probe_pdata(dsidev); - if (r) { - dsi_runtime_put(dsidev); - dsi_uninit_output(dsidev); - pm_runtime_disable(&dsidev->dev); - return r; + if (dsidev->dev.platform_data) { + r = dsi_probe_pdata(dsidev); + if (r) + goto err_probe; } dsi_runtime_put(dsidev); @@ -5586,6 +5648,9 @@ static int omap_dsihw_probe(struct platform_device *dsidev) #endif return 0; +err_probe: + dsi_runtime_put(dsidev); + dsi_uninit_output(dsidev); err_runtime_get: pm_runtime_disable(&dsidev->dev); return r; @@ -5603,14 +5668,9 @@ static int __exit omap_dsihw_remove(struct platform_device *dsidev) pm_runtime_disable(&dsidev->dev); - if (dsi->vdds_dsi_reg != NULL) { - if (dsi->vdds_dsi_enabled) { - regulator_disable(dsi->vdds_dsi_reg); - dsi->vdds_dsi_enabled = false; - } - - regulator_put(dsi->vdds_dsi_reg); - dsi->vdds_dsi_reg = NULL; + if (dsi->vdds_dsi_reg != NULL && dsi->vdds_dsi_enabled) { + regulator_disable(dsi->vdds_dsi_reg); + dsi->vdds_dsi_enabled = false; } return 0; diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c index 94f66f9f10a3..bd01608e67e2 100644 --- a/drivers/video/omap2/dss/dss.c +++ b/drivers/video/omap2/dss/dss.c @@ -157,7 +157,8 @@ static void dss_restore_context(void) int dss_get_ctx_loss_count(void) { - struct omap_dss_board_info *board_data = dss.pdev->dev.platform_data; + struct platform_device *core_pdev = dss_get_core_pdev(); + struct omap_dss_board_info *board_data = core_pdev->dev.platform_data; int cnt; if (!board_data->get_context_loss_count) diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h index 84758936429d..50a2362ef8f8 100644 --- a/drivers/video/omap2/dss/dss.h +++ b/drivers/video/omap2/dss/dss.h @@ -179,23 +179,19 @@ void dss_put_device(struct omap_dss_device *dssdev); void dss_copy_device_pdata(struct omap_dss_device *dst, const struct omap_dss_device *src); -/* output */ -void dss_register_output(struct omap_dss_output *out); -void dss_unregister_output(struct omap_dss_output *out); - /* display */ int dss_suspend_all_devices(void); int dss_resume_all_devices(void); void dss_disable_all_devices(void); -int display_init_sysfs(struct platform_device *pdev, - struct omap_dss_device *dssdev); -void display_uninit_sysfs(struct platform_device *pdev, - struct omap_dss_device *dssdev); +int display_init_sysfs(struct platform_device *pdev); +void display_uninit_sysfs(struct platform_device *pdev); /* manager */ -int dss_init_overlay_managers(struct platform_device *pdev); -void dss_uninit_overlay_managers(struct platform_device *pdev); +int dss_init_overlay_managers(void); +void dss_uninit_overlay_managers(void); +int dss_init_overlay_managers_sysfs(struct platform_device *pdev); +void dss_uninit_overlay_managers_sysfs(struct platform_device *pdev); int dss_mgr_simple_check(struct omap_overlay_manager *mgr, const struct omap_overlay_manager_info *info); int dss_mgr_check_timings(struct omap_overlay_manager *mgr, @@ -426,6 +422,7 @@ void dispc_mgr_set_clock_div(enum omap_channel channel, const struct dispc_clock_info *cinfo); int dispc_mgr_get_clock_div(enum omap_channel channel, struct dispc_clock_info *cinfo); +void dispc_set_tv_pclk(unsigned long pclk); u32 dispc_wb_get_framedone_irq(void); bool dispc_wb_go_busy(void); @@ -437,17 +434,8 @@ int dispc_wb_setup(const struct omap_dss_writeback_info *wi, bool mem_to_mem, const struct omap_video_timings *timings); /* VENC */ -#ifdef CONFIG_OMAP2_DSS_VENC int venc_init_platform_driver(void) __init; void venc_uninit_platform_driver(void) __exit; -unsigned long venc_get_pixel_clock(void); -#else -static inline unsigned long venc_get_pixel_clock(void) -{ - WARN("%s: VENC not compiled in, returning pclk as 0\n", __func__); - return 0; -} -#endif int omapdss_venc_display_enable(struct omap_dss_device *dssdev); void omapdss_venc_display_disable(struct omap_dss_device *dssdev); void omapdss_venc_set_timings(struct omap_dss_device *dssdev, @@ -464,17 +452,8 @@ int venc_panel_init(void); void venc_panel_exit(void); /* HDMI */ -#ifdef CONFIG_OMAP4_DSS_HDMI int hdmi_init_platform_driver(void) __init; void hdmi_uninit_platform_driver(void) __exit; -unsigned long hdmi_get_pixel_clock(void); -#else -static inline unsigned long hdmi_get_pixel_clock(void) -{ - WARN("%s: HDMI not compiled in, returning pclk as 0\n", __func__); - return 0; -} -#endif int omapdss_hdmi_display_enable(struct omap_dss_device *dssdev); void omapdss_hdmi_display_disable(struct omap_dss_device *dssdev); int omapdss_hdmi_core_enable(struct omap_dss_device *dssdev); diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c index 77dbe0cfb34c..b9cfebb378a2 100644 --- a/drivers/video/omap2/dss/dss_features.c +++ b/drivers/video/omap2/dss/dss_features.c @@ -797,7 +797,6 @@ static const struct ti_hdmi_ip_ops omap4_hdmi_functions = { .phy_enable = ti_hdmi_4xxx_phy_enable, .phy_disable = ti_hdmi_4xxx_phy_disable, .read_edid = ti_hdmi_4xxx_read_edid, - .detect = ti_hdmi_4xxx_detect, .pll_enable = ti_hdmi_4xxx_pll_enable, .pll_disable = ti_hdmi_4xxx_pll_disable, .video_enable = ti_hdmi_4xxx_wp_video_start, diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c index 17f4d55c621c..44a885b92825 100644 --- a/drivers/video/omap2/dss/hdmi.c +++ b/drivers/video/omap2/dss/hdmi.c @@ -70,7 +70,9 @@ static struct { int ls_oe_gpio; int hpd_gpio; - struct omap_dss_output output; + bool core_enabled; + + struct omap_dss_device output; } hdmi; /* @@ -328,6 +330,29 @@ static void hdmi_runtime_put(void) WARN_ON(r < 0 && r != -ENOSYS); } +static int hdmi_init_regulator(void) +{ + struct regulator *reg; + + if (hdmi.vdda_hdmi_dac_reg != NULL) + return 0; + + reg = devm_regulator_get(&hdmi.pdev->dev, "vdda_hdmi_dac"); + + /* DT HACK: try VDAC to make omapdss work for o4 sdp/panda */ + if (IS_ERR(reg)) + reg = devm_regulator_get(&hdmi.pdev->dev, "VDAC"); + + if (IS_ERR(reg)) { + DSSERR("can't get VDDA_HDMI_DAC regulator\n"); + return PTR_ERR(reg); + } + + hdmi.vdda_hdmi_dac_reg = reg; + + return 0; +} + static int hdmi_init_display(struct omap_dss_device *dssdev) { int r; @@ -342,22 +367,9 @@ static int hdmi_init_display(struct omap_dss_device *dssdev) dss_init_hdmi_ip_ops(&hdmi.ip_data, omapdss_get_version()); - if (hdmi.vdda_hdmi_dac_reg == NULL) { - struct regulator *reg; - - reg = devm_regulator_get(&hdmi.pdev->dev, "vdda_hdmi_dac"); - - /* DT HACK: try VDAC to make omapdss work for o4 sdp/panda */ - if (IS_ERR(reg)) - reg = devm_regulator_get(&hdmi.pdev->dev, "VDAC"); - - if (IS_ERR(reg)) { - DSSERR("can't get VDDA_HDMI_DAC regulator\n"); - return PTR_ERR(reg); - } - - hdmi.vdda_hdmi_dac_reg = reg; - } + r = hdmi_init_regulator(); + if (r) + return r; r = gpio_request_array(gpios, ARRAY_SIZE(gpios)); if (r) @@ -455,12 +467,6 @@ end: return cm; } -unsigned long hdmi_get_pixel_clock(void) -{ - /* HDMI Pixel Clock in Mhz */ - return hdmi.ip_data.cfg.timings.pixel_clock * 1000; -} - static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy, struct hdmi_pll_info *pi) { @@ -511,8 +517,10 @@ static int hdmi_power_on_core(struct omap_dss_device *dssdev) { int r; - gpio_set_value(hdmi.ct_cp_hpd_gpio, 1); - gpio_set_value(hdmi.ls_oe_gpio, 1); + if (gpio_is_valid(hdmi.ct_cp_hpd_gpio)) + gpio_set_value(hdmi.ct_cp_hpd_gpio, 1); + if (gpio_is_valid(hdmi.ls_oe_gpio)) + gpio_set_value(hdmi.ls_oe_gpio, 1); /* wait 300us after CT_CP_HPD for the 5V power output to reach 90% */ udelay(300); @@ -528,29 +536,37 @@ static int hdmi_power_on_core(struct omap_dss_device *dssdev) /* Make selection of HDMI in DSS */ dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK); + hdmi.core_enabled = true; + return 0; err_runtime_get: regulator_disable(hdmi.vdda_hdmi_dac_reg); err_vdac_enable: - gpio_set_value(hdmi.ct_cp_hpd_gpio, 0); - gpio_set_value(hdmi.ls_oe_gpio, 0); + if (gpio_is_valid(hdmi.ct_cp_hpd_gpio)) + gpio_set_value(hdmi.ct_cp_hpd_gpio, 0); + if (gpio_is_valid(hdmi.ls_oe_gpio)) + gpio_set_value(hdmi.ls_oe_gpio, 0); return r; } static void hdmi_power_off_core(struct omap_dss_device *dssdev) { + hdmi.core_enabled = false; + hdmi_runtime_put(); regulator_disable(hdmi.vdda_hdmi_dac_reg); - gpio_set_value(hdmi.ct_cp_hpd_gpio, 0); - gpio_set_value(hdmi.ls_oe_gpio, 0); + if (gpio_is_valid(hdmi.ct_cp_hpd_gpio)) + gpio_set_value(hdmi.ct_cp_hpd_gpio, 0); + if (gpio_is_valid(hdmi.ls_oe_gpio)) + gpio_set_value(hdmi.ls_oe_gpio, 0); } static int hdmi_power_on_full(struct omap_dss_device *dssdev) { int r; struct omap_video_timings *p; - struct omap_overlay_manager *mgr = dssdev->output->manager; + struct omap_overlay_manager *mgr = hdmi.output.manager; unsigned long phy; r = hdmi_power_on_core(dssdev); @@ -613,7 +629,7 @@ err_pll_enable: static void hdmi_power_off_full(struct omap_dss_device *dssdev) { - struct omap_overlay_manager *mgr = dssdev->output->manager; + struct omap_overlay_manager *mgr = hdmi.output.manager; dss_mgr_disable(mgr); @@ -653,9 +669,23 @@ void omapdss_hdmi_display_set_timing(struct omap_dss_device *dssdev, if (t != NULL) hdmi.ip_data.cfg = *t; + dispc_set_tv_pclk(t->timings.pixel_clock * 1000); + mutex_unlock(&hdmi.lock); } +static void omapdss_hdmi_display_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + const struct hdmi_config *cfg; + + cfg = hdmi_get_timings(); + if (cfg == NULL) + cfg = &vesa_timings[0]; + + memcpy(timings, &cfg->timings, sizeof(cfg->timings)); +} + static void hdmi_dump_regs(struct seq_file *s) { mutex_lock(&hdmi.lock); @@ -700,7 +730,7 @@ bool omapdss_hdmi_detect(void) r = hdmi_runtime_get(); BUG_ON(r); - r = hdmi.ip_data.ops->detect(&hdmi.ip_data); + r = gpio_get_value(hdmi.hpd_gpio); hdmi_runtime_put(); mutex_unlock(&hdmi.lock); @@ -710,7 +740,7 @@ bool omapdss_hdmi_detect(void) int omapdss_hdmi_display_enable(struct omap_dss_device *dssdev) { - struct omap_dss_output *out = dssdev->output; + struct omap_dss_device *out = &hdmi.output; int r = 0; DSSDBG("ENTER hdmi_display_enable\n"); @@ -723,25 +753,15 @@ int omapdss_hdmi_display_enable(struct omap_dss_device *dssdev) goto err0; } - hdmi.ip_data.hpd_gpio = hdmi.hpd_gpio; - - r = omap_dss_start_device(dssdev); - if (r) { - DSSERR("failed to start device\n"); - goto err0; - } - r = hdmi_power_on_full(dssdev); if (r) { DSSERR("failed to power on device\n"); - goto err1; + goto err0; } mutex_unlock(&hdmi.lock); return 0; -err1: - omap_dss_stop_device(dssdev); err0: mutex_unlock(&hdmi.lock); return r; @@ -755,8 +775,6 @@ void omapdss_hdmi_display_disable(struct omap_dss_device *dssdev) hdmi_power_off_full(dssdev); - omap_dss_stop_device(dssdev); - mutex_unlock(&hdmi.lock); } @@ -768,8 +786,6 @@ int omapdss_hdmi_core_enable(struct omap_dss_device *dssdev) mutex_lock(&hdmi.lock); - hdmi.ip_data.hpd_gpio = hdmi.hpd_gpio; - r = hdmi_power_on_core(dssdev); if (r) { DSSERR("failed to power on device\n"); @@ -1033,24 +1049,219 @@ static int hdmi_probe_pdata(struct platform_device *pdev) return 0; } +static int hdmi_connect(struct omap_dss_device *dssdev, + struct omap_dss_device *dst) +{ + struct omap_overlay_manager *mgr; + int r; + + dss_init_hdmi_ip_ops(&hdmi.ip_data, omapdss_get_version()); + + r = hdmi_init_regulator(); + if (r) + return r; + + mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); + if (!mgr) + return -ENODEV; + + r = dss_mgr_connect(mgr, dssdev); + if (r) + return r; + + r = omapdss_output_set_device(dssdev, dst); + if (r) { + DSSERR("failed to connect output to new device: %s\n", + dst->name); + dss_mgr_disconnect(mgr, dssdev); + return r; + } + + return 0; +} + +static void hdmi_disconnect(struct omap_dss_device *dssdev, + struct omap_dss_device *dst) +{ + WARN_ON(dst != dssdev->device); + + if (dst != dssdev->device) + return; + + omapdss_output_unset_device(dssdev); + + if (dssdev->manager) + dss_mgr_disconnect(dssdev->manager, dssdev); +} + +static int hdmi_read_edid(struct omap_dss_device *dssdev, + u8 *edid, int len) +{ + bool need_enable; + int r; + + need_enable = hdmi.core_enabled == false; + + if (need_enable) { + r = omapdss_hdmi_core_enable(dssdev); + if (r) + return r; + } + + r = omapdss_hdmi_read_edid(edid, len); + + if (need_enable) + omapdss_hdmi_core_disable(dssdev); + + return r; +} + +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) +static int omapdss_hdmi_audio_enable(struct omap_dss_device *dssdev) +{ + int r; + + mutex_lock(&hdmi.lock); + + if (!hdmi_mode_has_audio()) { + r = -EPERM; + goto err; + } + + r = hdmi_audio_enable(); + if (r) + goto err; + + mutex_unlock(&hdmi.lock); + return 0; + +err: + mutex_unlock(&hdmi.lock); + return r; +} + +static void omapdss_hdmi_audio_disable(struct omap_dss_device *dssdev) +{ + hdmi_audio_disable(); +} + +static int omapdss_hdmi_audio_start(struct omap_dss_device *dssdev) +{ + return hdmi_audio_start(); +} + +static void omapdss_hdmi_audio_stop(struct omap_dss_device *dssdev) +{ + hdmi_audio_stop(); +} + +static bool omapdss_hdmi_audio_supported(struct omap_dss_device *dssdev) +{ + bool r; + + mutex_lock(&hdmi.lock); + + r = hdmi_mode_has_audio(); + + mutex_unlock(&hdmi.lock); + return r; +} + +static int omapdss_hdmi_audio_config(struct omap_dss_device *dssdev, + struct omap_dss_audio *audio) +{ + int r; + + mutex_lock(&hdmi.lock); + + if (!hdmi_mode_has_audio()) { + r = -EPERM; + goto err; + } + + r = hdmi_audio_config(audio); + if (r) + goto err; + + mutex_unlock(&hdmi.lock); + return 0; + +err: + mutex_unlock(&hdmi.lock); + return r; +} +#else +static int omapdss_hdmi_audio_enable(struct omap_dss_device *dssdev) +{ + return -EPERM; +} + +static void omapdss_hdmi_audio_disable(struct omap_dss_device *dssdev) +{ +} + +static int omapdss_hdmi_audio_start(struct omap_dss_device *dssdev) +{ + return -EPERM; +} + +static void omapdss_hdmi_audio_stop(struct omap_dss_device *dssdev) +{ +} + +static bool omapdss_hdmi_audio_supported(struct omap_dss_device *dssdev) +{ + return false; +} + +static int omapdss_hdmi_audio_config(struct omap_dss_device *dssdev, + struct omap_dss_audio *audio) +{ + return -EPERM; +} +#endif + +static const struct omapdss_hdmi_ops hdmi_ops = { + .connect = hdmi_connect, + .disconnect = hdmi_disconnect, + + .enable = omapdss_hdmi_display_enable, + .disable = omapdss_hdmi_display_disable, + + .check_timings = omapdss_hdmi_display_check_timing, + .set_timings = omapdss_hdmi_display_set_timing, + .get_timings = omapdss_hdmi_display_get_timings, + + .read_edid = hdmi_read_edid, + + .audio_enable = omapdss_hdmi_audio_enable, + .audio_disable = omapdss_hdmi_audio_disable, + .audio_start = omapdss_hdmi_audio_start, + .audio_stop = omapdss_hdmi_audio_stop, + .audio_supported = omapdss_hdmi_audio_supported, + .audio_config = omapdss_hdmi_audio_config, +}; + static void hdmi_init_output(struct platform_device *pdev) { - struct omap_dss_output *out = &hdmi.output; + struct omap_dss_device *out = &hdmi.output; - out->pdev = pdev; + out->dev = &pdev->dev; out->id = OMAP_DSS_OUTPUT_HDMI; - out->type = OMAP_DISPLAY_TYPE_HDMI; + out->output_type = OMAP_DISPLAY_TYPE_HDMI; out->name = "hdmi.0"; out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT; + out->ops.hdmi = &hdmi_ops; + out->owner = THIS_MODULE; - dss_register_output(out); + omapdss_register_output(out); } static void __exit hdmi_uninit_output(struct platform_device *pdev) { - struct omap_dss_output *out = &hdmi.output; + struct omap_dss_device *out = &hdmi.output; - dss_unregister_output(out); + omapdss_unregister_output(out); } /* HDMI HW IP initialisation */ @@ -1065,16 +1276,18 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev) mutex_init(&hdmi.ip_data.lock); res = platform_get_resource(hdmi.pdev, IORESOURCE_MEM, 0); - if (!res) { - DSSERR("can't get IORESOURCE_MEM HDMI\n"); - return -EINVAL; - } /* Base address taken from platform */ hdmi.ip_data.base_wp = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(hdmi.ip_data.base_wp)) return PTR_ERR(hdmi.ip_data.base_wp); + hdmi.ip_data.irq = platform_get_irq(pdev, 0); + if (hdmi.ip_data.irq < 0) { + DSSERR("platform_get_irq failed\n"); + return -ENODEV; + } + r = hdmi_get_clocks(pdev); if (r) { DSSERR("can't get clocks\n"); @@ -1088,6 +1301,10 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev) hdmi.ip_data.pll_offset = HDMI_PLLCTRL; hdmi.ip_data.phy_offset = HDMI_PHY; + hdmi.ct_cp_hpd_gpio = -1; + hdmi.ls_oe_gpio = -1; + hdmi.hpd_gpio = -1; + hdmi_init_output(pdev); r = hdmi_panel_init(); @@ -1098,15 +1315,19 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev) dss_debugfs_create_file("hdmi", hdmi_dump_regs); - r = hdmi_probe_pdata(pdev); - if (r) { - hdmi_panel_exit(); - hdmi_uninit_output(pdev); - pm_runtime_disable(&pdev->dev); - return r; + if (pdev->dev.platform_data) { + r = hdmi_probe_pdata(pdev); + if (r) + goto err_probe; } return 0; + +err_probe: + hdmi_panel_exit(); + hdmi_uninit_output(pdev); + pm_runtime_disable(&pdev->dev); + return r; } static int __exit hdmi_remove_child(struct device *dev, void *data) diff --git a/drivers/video/omap2/dss/manager-sysfs.c b/drivers/video/omap2/dss/manager-sysfs.c index 9a2fb59b6f89..de7e7b5b1b7c 100644 --- a/drivers/video/omap2/dss/manager-sysfs.c +++ b/drivers/video/omap2/dss/manager-sysfs.c @@ -50,6 +50,7 @@ static ssize_t manager_display_store(struct omap_overlay_manager *mgr, int r = 0; size_t len = size; struct omap_dss_device *dssdev = NULL; + struct omap_dss_device *old_dssdev; int match(struct omap_dss_device *dssdev, void *data) { @@ -66,32 +67,44 @@ static ssize_t manager_display_store(struct omap_overlay_manager *mgr, if (len > 0 && dssdev == NULL) return -EINVAL; - if (dssdev) + if (dssdev) { DSSDBG("display %s found\n", dssdev->name); - if (mgr->output) { - r = mgr->unset_output(mgr); - if (r) { - DSSERR("failed to unset current output\n"); + if (omapdss_device_is_connected(dssdev)) { + DSSERR("new display is already connected\n"); + r = -EINVAL; + goto put_device; + } + + if (omapdss_device_is_enabled(dssdev)) { + DSSERR("new display is not disabled\n"); + r = -EINVAL; goto put_device; } } - if (dssdev) { - struct omap_dss_output *out = dssdev->output; - - /* - * a registered device should have an output connected to it - * already - */ - if (!out) { - DSSERR("device has no output connected to it\n"); + old_dssdev = mgr->get_device(mgr); + if (old_dssdev) { + if (omapdss_device_is_enabled(old_dssdev)) { + DSSERR("old display is not disabled\n"); + r = -EINVAL; goto put_device; } - r = mgr->set_output(mgr, out); + old_dssdev->driver->disconnect(old_dssdev); + } + + if (dssdev) { + r = dssdev->driver->connect(dssdev); if (r) { - DSSERR("failed to set manager output\n"); + DSSERR("failed to connect new device\n"); + goto put_device; + } + + old_dssdev = mgr->get_device(mgr); + if (old_dssdev != dssdev) { + DSSERR("failed to connect device to this manager\n"); + dssdev->driver->disconnect(dssdev); goto put_device; } @@ -509,4 +522,6 @@ void dss_manager_kobj_uninit(struct omap_overlay_manager *mgr) { kobject_del(&mgr->kobj); kobject_put(&mgr->kobj); + + memset(&mgr->kobj, 0, sizeof(mgr->kobj)); } diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c index 2551eaa14c42..1aac9b4191a9 100644 --- a/drivers/video/omap2/dss/manager.c +++ b/drivers/video/omap2/dss/manager.c @@ -36,9 +36,9 @@ static int num_managers; static struct omap_overlay_manager *managers; -int dss_init_overlay_managers(struct platform_device *pdev) +int dss_init_overlay_managers(void) { - int i, r; + int i; num_managers = dss_feat_get_num_mgrs(); @@ -76,6 +76,17 @@ int dss_init_overlay_managers(struct platform_device *pdev) dss_feat_get_supported_outputs(mgr->id); INIT_LIST_HEAD(&mgr->overlays); + } + + return 0; +} + +int dss_init_overlay_managers_sysfs(struct platform_device *pdev) +{ + int i, r; + + for (i = 0; i < num_managers; ++i) { + struct omap_overlay_manager *mgr = &managers[i]; r = dss_manager_kobj_init(mgr, pdev); if (r) @@ -85,18 +96,22 @@ int dss_init_overlay_managers(struct platform_device *pdev) return 0; } -void dss_uninit_overlay_managers(struct platform_device *pdev) +void dss_uninit_overlay_managers(void) +{ + kfree(managers); + managers = NULL; + num_managers = 0; +} + +void dss_uninit_overlay_managers_sysfs(struct platform_device *pdev) { int i; for (i = 0; i < num_managers; ++i) { struct omap_overlay_manager *mgr = &managers[i]; + dss_manager_kobj_uninit(mgr); } - - kfree(managers); - managers = NULL; - num_managers = 0; } int omap_dss_get_num_overlay_managers(void) diff --git a/drivers/video/omap2/dss/output.c b/drivers/video/omap2/dss/output.c index 5214df63e0a9..3f5c0a758b32 100644 --- a/drivers/video/omap2/dss/output.c +++ b/drivers/video/omap2/dss/output.c @@ -27,7 +27,7 @@ static LIST_HEAD(output_list); static DEFINE_MUTEX(output_lock); -int omapdss_output_set_device(struct omap_dss_output *out, +int omapdss_output_set_device(struct omap_dss_device *out, struct omap_dss_device *dssdev) { int r; @@ -41,7 +41,7 @@ int omapdss_output_set_device(struct omap_dss_output *out, goto err; } - if (out->type != dssdev->type) { + if (out->output_type != dssdev->type) { DSSERR("output type and display type don't match\n"); r = -EINVAL; goto err; @@ -60,7 +60,7 @@ err: } EXPORT_SYMBOL(omapdss_output_set_device); -int omapdss_output_unset_device(struct omap_dss_output *out) +int omapdss_output_unset_device(struct omap_dss_device *out) { int r; @@ -92,19 +92,22 @@ err: } EXPORT_SYMBOL(omapdss_output_unset_device); -void dss_register_output(struct omap_dss_output *out) +int omapdss_register_output(struct omap_dss_device *out) { list_add_tail(&out->list, &output_list); + return 0; } +EXPORT_SYMBOL(omapdss_register_output); -void dss_unregister_output(struct omap_dss_output *out) +void omapdss_unregister_output(struct omap_dss_device *out) { list_del(&out->list); } +EXPORT_SYMBOL(omapdss_unregister_output); -struct omap_dss_output *omap_dss_get_output(enum omap_dss_output_id id) +struct omap_dss_device *omap_dss_get_output(enum omap_dss_output_id id) { - struct omap_dss_output *out; + struct omap_dss_device *out; list_for_each_entry(out, &output_list, list) { if (out->id == id) @@ -115,6 +118,62 @@ struct omap_dss_output *omap_dss_get_output(enum omap_dss_output_id id) } EXPORT_SYMBOL(omap_dss_get_output); +struct omap_dss_device *omap_dss_find_output(const char *name) +{ + struct omap_dss_device *out; + + list_for_each_entry(out, &output_list, list) { + if (strcmp(out->name, name) == 0) + return omap_dss_get_device(out); + } + + return NULL; +} +EXPORT_SYMBOL(omap_dss_find_output); + +struct omap_dss_device *omap_dss_find_output_by_node(struct device_node *node) +{ + struct omap_dss_device *out; + + list_for_each_entry(out, &output_list, list) { + if (out->dev->of_node == node) + return omap_dss_get_device(out); + } + + return NULL; +} +EXPORT_SYMBOL(omap_dss_find_output_by_node); + +struct omap_dss_device *omapdss_find_output_from_display(struct omap_dss_device *dssdev) +{ + while (dssdev->output) + dssdev = dssdev->output; + + if (dssdev->id != 0) + return omap_dss_get_device(dssdev); + + return NULL; +} +EXPORT_SYMBOL(omapdss_find_output_from_display); + +struct omap_overlay_manager *omapdss_find_mgr_from_display(struct omap_dss_device *dssdev) +{ + struct omap_dss_device *out; + struct omap_overlay_manager *mgr; + + out = omapdss_find_output_from_display(dssdev); + + if (out == NULL) + return NULL; + + mgr = out->manager; + + omap_dss_put_device(out); + + return mgr; +} +EXPORT_SYMBOL(omapdss_find_mgr_from_display); + static const struct dss_mgr_ops *dss_mgr_ops; int dss_install_mgr_ops(const struct dss_mgr_ops *mgr_ops) @@ -134,6 +193,20 @@ void dss_uninstall_mgr_ops(void) } EXPORT_SYMBOL(dss_uninstall_mgr_ops); +int dss_mgr_connect(struct omap_overlay_manager *mgr, + struct omap_dss_device *dst) +{ + return dss_mgr_ops->connect(mgr, dst); +} +EXPORT_SYMBOL(dss_mgr_connect); + +void dss_mgr_disconnect(struct omap_overlay_manager *mgr, + struct omap_dss_device *dst) +{ + dss_mgr_ops->disconnect(mgr, dst); +} +EXPORT_SYMBOL(dss_mgr_disconnect); + void dss_mgr_set_timings(struct omap_overlay_manager *mgr, const struct omap_video_timings *timings) { diff --git a/drivers/video/omap2/dss/rfbi.c b/drivers/video/omap2/dss/rfbi.c index 1a17dd1447dc..fdfe6e6f25df 100644 --- a/drivers/video/omap2/dss/rfbi.c +++ b/drivers/video/omap2/dss/rfbi.c @@ -117,7 +117,7 @@ static struct { int data_lines; struct rfbi_timings intf_timings; - struct omap_dss_output output; + struct omap_dss_device output; } rfbi; static inline void rfbi_write_reg(const struct rfbi_reg idx, u32 val) @@ -312,7 +312,7 @@ static int rfbi_transfer_area(struct omap_dss_device *dssdev, { u32 l; int r; - struct omap_overlay_manager *mgr = dssdev->output->manager; + struct omap_overlay_manager *mgr = rfbi.output.manager; u16 width = rfbi.timings.x_res; u16 height = rfbi.timings.y_res; @@ -852,7 +852,7 @@ static void rfbi_dump_regs(struct seq_file *s) static void rfbi_config_lcd_manager(struct omap_dss_device *dssdev) { - struct omap_overlay_manager *mgr = dssdev->output->manager; + struct omap_overlay_manager *mgr = rfbi.output.manager; struct dss_lcd_mgr_config mgr_config; mgr_config.io_pad_mode = DSS_IO_PAD_MODE_RFBI; @@ -890,7 +890,7 @@ static void rfbi_config_lcd_manager(struct omap_dss_device *dssdev) int omapdss_rfbi_display_enable(struct omap_dss_device *dssdev) { - struct omap_dss_output *out = dssdev->output; + struct omap_dss_device *out = &rfbi.output; int r; if (out == NULL || out->manager == NULL) { @@ -902,12 +902,6 @@ int omapdss_rfbi_display_enable(struct omap_dss_device *dssdev) if (r) return r; - r = omap_dss_start_device(dssdev); - if (r) { - DSSERR("failed to start device\n"); - goto err0; - } - r = dss_mgr_register_framedone_handler(out->manager, framedone_callback, NULL); if (r) { @@ -924,8 +918,6 @@ int omapdss_rfbi_display_enable(struct omap_dss_device *dssdev) return 0; err1: - omap_dss_stop_device(dssdev); -err0: rfbi_runtime_put(); return r; } @@ -933,11 +925,10 @@ EXPORT_SYMBOL(omapdss_rfbi_display_enable); void omapdss_rfbi_display_disable(struct omap_dss_device *dssdev) { - struct omap_dss_output *out = dssdev->output; + struct omap_dss_device *out = &rfbi.output; dss_mgr_unregister_framedone_handler(out->manager, framedone_callback, NULL); - omap_dss_stop_device(dssdev); rfbi_runtime_put(); } @@ -1022,22 +1013,23 @@ static int rfbi_probe_pdata(struct platform_device *rfbidev) static void rfbi_init_output(struct platform_device *pdev) { - struct omap_dss_output *out = &rfbi.output; + struct omap_dss_device *out = &rfbi.output; - out->pdev = pdev; + out->dev = &pdev->dev; out->id = OMAP_DSS_OUTPUT_DBI; - out->type = OMAP_DISPLAY_TYPE_DBI; + out->output_type = OMAP_DISPLAY_TYPE_DBI; out->name = "rfbi.0"; out->dispc_channel = OMAP_DSS_CHANNEL_LCD; + out->owner = THIS_MODULE; - dss_register_output(out); + omapdss_register_output(out); } static void __exit rfbi_uninit_output(struct platform_device *pdev) { - struct omap_dss_output *out = &rfbi.output; + struct omap_dss_device *out = &rfbi.output; - dss_unregister_output(out); + omapdss_unregister_output(out); } /* RFBI HW IP initialisation */ @@ -1093,15 +1085,16 @@ static int omap_rfbihw_probe(struct platform_device *pdev) rfbi_init_output(pdev); - r = rfbi_probe_pdata(pdev); - if (r) { - rfbi_uninit_output(pdev); - pm_runtime_disable(&pdev->dev); - return r; + if (pdev->dev.platform_data) { + r = rfbi_probe_pdata(pdev); + if (r) + goto err_probe; } return 0; +err_probe: + rfbi_uninit_output(pdev); err_runtime_get: pm_runtime_disable(&pdev->dev); return r; diff --git a/drivers/video/omap2/dss/sdi.c b/drivers/video/omap2/dss/sdi.c index 0bcd30272f69..856af2e89760 100644 --- a/drivers/video/omap2/dss/sdi.c +++ b/drivers/video/omap2/dss/sdi.c @@ -31,6 +31,8 @@ #include "dss.h" static struct { + struct platform_device *pdev; + bool update_enabled; struct regulator *vdds_sdi_reg; @@ -38,7 +40,7 @@ static struct { struct omap_video_timings timings; int datapairs; - struct omap_dss_output output; + struct omap_dss_device output; } sdi; struct sdi_clk_calc_ctx { @@ -109,7 +111,7 @@ static int sdi_calc_clock_div(unsigned long pclk, static void sdi_config_lcd_manager(struct omap_dss_device *dssdev) { - struct omap_overlay_manager *mgr = dssdev->output->manager; + struct omap_overlay_manager *mgr = sdi.output.manager; sdi.mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS; @@ -124,7 +126,7 @@ static void sdi_config_lcd_manager(struct omap_dss_device *dssdev) int omapdss_sdi_display_enable(struct omap_dss_device *dssdev) { - struct omap_dss_output *out = dssdev->output; + struct omap_dss_device *out = &sdi.output; struct omap_video_timings *t = &sdi.timings; struct dss_clock_info dss_cinfo; struct dispc_clock_info dispc_cinfo; @@ -136,12 +138,6 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev) return -ENODEV; } - r = omap_dss_start_device(dssdev); - if (r) { - DSSERR("failed to start device\n"); - goto err_start_dev; - } - r = regulator_enable(sdi.vdds_sdi_reg); if (r) goto err_reg_enable; @@ -213,15 +209,13 @@ err_calc_clock_div: err_get_dispc: regulator_disable(sdi.vdds_sdi_reg); err_reg_enable: - omap_dss_stop_device(dssdev); -err_start_dev: return r; } EXPORT_SYMBOL(omapdss_sdi_display_enable); void omapdss_sdi_display_disable(struct omap_dss_device *dssdev) { - struct omap_overlay_manager *mgr = dssdev->output->manager; + struct omap_overlay_manager *mgr = sdi.output.manager; dss_mgr_disable(mgr); @@ -230,8 +224,6 @@ void omapdss_sdi_display_disable(struct omap_dss_device *dssdev) dispc_runtime_put(); regulator_disable(sdi.vdds_sdi_reg); - - omap_dss_stop_device(dssdev); } EXPORT_SYMBOL(omapdss_sdi_display_disable); @@ -242,29 +234,51 @@ void omapdss_sdi_set_timings(struct omap_dss_device *dssdev, } EXPORT_SYMBOL(omapdss_sdi_set_timings); +static void sdi_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + *timings = sdi.timings; +} + +static int sdi_check_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct omap_overlay_manager *mgr = sdi.output.manager; + + if (mgr && !dispc_mgr_timings_ok(mgr->id, timings)) + return -EINVAL; + + if (timings->pixel_clock == 0) + return -EINVAL; + + return 0; +} + void omapdss_sdi_set_datapairs(struct omap_dss_device *dssdev, int datapairs) { sdi.datapairs = datapairs; } EXPORT_SYMBOL(omapdss_sdi_set_datapairs); -static int sdi_init_display(struct omap_dss_device *dssdev) +static int sdi_init_regulator(void) { - DSSDBG("SDI init\n"); + struct regulator *vdds_sdi; - if (sdi.vdds_sdi_reg == NULL) { - struct regulator *vdds_sdi; + if (sdi.vdds_sdi_reg) + return 0; - vdds_sdi = dss_get_vdds_sdi(); + vdds_sdi = dss_get_vdds_sdi(); + if (IS_ERR(vdds_sdi)) { + vdds_sdi = devm_regulator_get(&sdi.pdev->dev, "vdds_sdi"); if (IS_ERR(vdds_sdi)) { DSSERR("can't get VDDS_SDI regulator\n"); return PTR_ERR(vdds_sdi); } - - sdi.vdds_sdi_reg = vdds_sdi; } + sdi.vdds_sdi_reg = vdds_sdi; + return 0; } @@ -313,7 +327,7 @@ static int sdi_probe_pdata(struct platform_device *sdidev) dss_copy_device_pdata(dssdev, plat_dssdev); - r = sdi_init_display(dssdev); + r = sdi_init_regulator(); if (r) { DSSERR("device %s init failed: %d\n", dssdev->name, r); dss_put_device(dssdev); @@ -339,39 +353,104 @@ static int sdi_probe_pdata(struct platform_device *sdidev) return 0; } +static int sdi_connect(struct omap_dss_device *dssdev, + struct omap_dss_device *dst) +{ + struct omap_overlay_manager *mgr; + int r; + + r = sdi_init_regulator(); + if (r) + return r; + + mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); + if (!mgr) + return -ENODEV; + + r = dss_mgr_connect(mgr, dssdev); + if (r) + return r; + + r = omapdss_output_set_device(dssdev, dst); + if (r) { + DSSERR("failed to connect output to new device: %s\n", + dst->name); + dss_mgr_disconnect(mgr, dssdev); + return r; + } + + return 0; +} + +static void sdi_disconnect(struct omap_dss_device *dssdev, + struct omap_dss_device *dst) +{ + WARN_ON(dst != dssdev->device); + + if (dst != dssdev->device) + return; + + omapdss_output_unset_device(dssdev); + + if (dssdev->manager) + dss_mgr_disconnect(dssdev->manager, dssdev); +} + +static const struct omapdss_sdi_ops sdi_ops = { + .connect = sdi_connect, + .disconnect = sdi_disconnect, + + .enable = omapdss_sdi_display_enable, + .disable = omapdss_sdi_display_disable, + + .check_timings = sdi_check_timings, + .set_timings = omapdss_sdi_set_timings, + .get_timings = sdi_get_timings, + + .set_datapairs = omapdss_sdi_set_datapairs, +}; + static void sdi_init_output(struct platform_device *pdev) { - struct omap_dss_output *out = &sdi.output; + struct omap_dss_device *out = &sdi.output; - out->pdev = pdev; + out->dev = &pdev->dev; out->id = OMAP_DSS_OUTPUT_SDI; - out->type = OMAP_DISPLAY_TYPE_SDI; + out->output_type = OMAP_DISPLAY_TYPE_SDI; out->name = "sdi.0"; out->dispc_channel = OMAP_DSS_CHANNEL_LCD; + out->ops.sdi = &sdi_ops; + out->owner = THIS_MODULE; - dss_register_output(out); + omapdss_register_output(out); } static void __exit sdi_uninit_output(struct platform_device *pdev) { - struct omap_dss_output *out = &sdi.output; + struct omap_dss_device *out = &sdi.output; - dss_unregister_output(out); + omapdss_unregister_output(out); } static int omap_sdi_probe(struct platform_device *pdev) { int r; + sdi.pdev = pdev; + sdi_init_output(pdev); - r = sdi_probe_pdata(pdev); - if (r) { - sdi_uninit_output(pdev); - return r; + if (pdev->dev.platform_data) { + r = sdi_probe_pdata(pdev); + if (r) + goto err_probe; } return 0; + +err_probe: + sdi_uninit_output(pdev); + return r; } static int __exit omap_sdi_remove(struct platform_device *pdev) diff --git a/drivers/video/omap2/dss/ti_hdmi.h b/drivers/video/omap2/dss/ti_hdmi.h index 216aa704f9d7..45215f44617c 100644 --- a/drivers/video/omap2/dss/ti_hdmi.h +++ b/drivers/video/omap2/dss/ti_hdmi.h @@ -73,8 +73,6 @@ struct ti_hdmi_ip_ops { int (*read_edid)(struct hdmi_ip_data *ip_data, u8 *edid, int len); - bool (*detect)(struct hdmi_ip_data *ip_data); - int (*pll_enable)(struct hdmi_ip_data *ip_data); void (*pll_disable)(struct hdmi_ip_data *ip_data); @@ -155,19 +153,18 @@ struct hdmi_ip_data { unsigned long core_av_offset; unsigned long pll_offset; unsigned long phy_offset; + int irq; const struct ti_hdmi_ip_ops *ops; struct hdmi_config cfg; struct hdmi_pll_info pll_data; struct hdmi_core_infoframe_avi avi_cfg; /* ti_hdmi_4xxx_ip private data. These should be in a separate struct */ - int hpd_gpio; struct mutex lock; }; int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data); void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data); int ti_hdmi_4xxx_read_edid(struct hdmi_ip_data *ip_data, u8 *edid, int len); -bool ti_hdmi_4xxx_detect(struct hdmi_ip_data *ip_data); int ti_hdmi_4xxx_wp_video_start(struct hdmi_ip_data *ip_data); void ti_hdmi_4xxx_wp_video_stop(struct hdmi_ip_data *ip_data); int ti_hdmi_4xxx_pll_enable(struct hdmi_ip_data *ip_data); diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c index e18b222ed739..e242ed85cb07 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c @@ -28,7 +28,6 @@ #include <linux/delay.h> #include <linux/string.h> #include <linux/seq_file.h> -#include <linux/gpio.h> #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) #include <sound/asound.h> #include <sound/asoundef.h> @@ -38,6 +37,9 @@ #include "dss.h" #include "dss_features.h" +#define HDMI_IRQ_LINK_CONNECT (1 << 25) +#define HDMI_IRQ_LINK_DISCONNECT (1 << 26) + static inline void hdmi_write_reg(void __iomem *base_addr, const u16 idx, u32 val) { @@ -233,37 +235,39 @@ void ti_hdmi_4xxx_pll_disable(struct hdmi_ip_data *ip_data) hdmi_set_pll_pwr(ip_data, HDMI_PLLPWRCMD_ALLOFF); } -static int hdmi_check_hpd_state(struct hdmi_ip_data *ip_data) +static irqreturn_t hdmi_irq_handler(int irq, void *data) { - bool hpd; - int r; - - mutex_lock(&ip_data->lock); - - hpd = gpio_get_value(ip_data->hpd_gpio); + struct hdmi_ip_data *ip_data = data; + void __iomem *wp_base = hdmi_wp_base(ip_data); + u32 irqstatus; + + irqstatus = hdmi_read_reg(wp_base, HDMI_WP_IRQSTATUS); + hdmi_write_reg(wp_base, HDMI_WP_IRQSTATUS, irqstatus); + /* flush posted write */ + hdmi_read_reg(wp_base, HDMI_WP_IRQSTATUS); + + if ((irqstatus & HDMI_IRQ_LINK_CONNECT) && + irqstatus & HDMI_IRQ_LINK_DISCONNECT) { + /* + * If we get both connect and disconnect interrupts at the same + * time, turn off the PHY, clear interrupts, and restart, which + * raises connect interrupt if a cable is connected, or nothing + * if cable is not connected. + */ + hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF); - if (hpd) - r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_TXON); - else - r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_LDOON); + hdmi_write_reg(wp_base, HDMI_WP_IRQSTATUS, + HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT); + /* flush posted write */ + hdmi_read_reg(wp_base, HDMI_WP_IRQSTATUS); - if (r) { - DSSERR("Failed to %s PHY TX power\n", - hpd ? "enable" : "disable"); - goto err; + hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_LDOON); + } else if (irqstatus & HDMI_IRQ_LINK_CONNECT) { + hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_TXON); + } else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) { + hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_LDOON); } -err: - mutex_unlock(&ip_data->lock); - return r; -} - -static irqreturn_t hpd_irq_handler(int irq, void *data) -{ - struct hdmi_ip_data *ip_data = data; - - hdmi_check_hpd_state(ip_data); - return IRQ_HANDLED; } @@ -272,6 +276,12 @@ int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data) u16 r = 0; void __iomem *phy_base = hdmi_phy_base(ip_data); + hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_IRQENABLE_CLR, + 0xffffffff); + + hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_IRQSTATUS, + HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT); + r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_LDOON); if (r) return r; @@ -297,29 +307,23 @@ int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data) /* Write to phy address 3 to change the polarity control */ REG_FLD_MOD(phy_base, HDMI_TXPHY_PAD_CFG_CTRL, 0x1, 27, 27); - r = request_threaded_irq(gpio_to_irq(ip_data->hpd_gpio), - NULL, hpd_irq_handler, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | - IRQF_ONESHOT, "hpd", ip_data); + r = request_threaded_irq(ip_data->irq, NULL, hdmi_irq_handler, + IRQF_ONESHOT, "OMAP HDMI", ip_data); if (r) { - DSSERR("HPD IRQ request failed\n"); + DSSERR("HDMI IRQ request failed\n"); hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF); return r; } - r = hdmi_check_hpd_state(ip_data); - if (r) { - free_irq(gpio_to_irq(ip_data->hpd_gpio), ip_data); - hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF); - return r; - } + hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_IRQENABLE_SET, + HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT); return 0; } void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data) { - free_irq(gpio_to_irq(ip_data->hpd_gpio), ip_data); + free_irq(ip_data->irq, ip_data); hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF); } @@ -476,11 +480,6 @@ int ti_hdmi_4xxx_read_edid(struct hdmi_ip_data *ip_data, return l; } -bool ti_hdmi_4xxx_detect(struct hdmi_ip_data *ip_data) -{ - return gpio_get_value(ip_data->hpd_gpio); -} - static void hdmi_core_init(struct hdmi_core_video_config *video_cfg, struct hdmi_core_infoframe_avi *avi_cfg, struct hdmi_core_packet_enable_repeat *repeat_cfg) diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h index 8366ae19e82e..6ef2f929a76d 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h @@ -33,6 +33,7 @@ #define HDMI_WP_IRQSTATUS 0x28 #define HDMI_WP_PWR_CTRL 0x40 #define HDMI_WP_IRQENABLE_SET 0x2C +#define HDMI_WP_IRQENABLE_CLR 0x30 #define HDMI_WP_VIDEO_CFG 0x50 #define HDMI_WP_VIDEO_SIZE 0x60 #define HDMI_WP_VIDEO_TIMING_H 0x68 diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c index 74fdb3ee209e..496a106fe823 100644 --- a/drivers/video/omap2/dss/venc.c +++ b/drivers/video/omap2/dss/venc.c @@ -304,7 +304,7 @@ static struct { enum omap_dss_venc_type type; bool invert_polarity; - struct omap_dss_output output; + struct omap_dss_device output; } venc; static inline void venc_write_reg(int idx, u32 val) @@ -429,7 +429,7 @@ static const struct venc_config *venc_timings_to_config( static int venc_power_on(struct omap_dss_device *dssdev) { - struct omap_overlay_manager *mgr = dssdev->output->manager; + struct omap_overlay_manager *mgr = venc.output.manager; u32 l; int r; @@ -480,7 +480,7 @@ err0: static void venc_power_off(struct omap_dss_device *dssdev) { - struct omap_overlay_manager *mgr = dssdev->output->manager; + struct omap_overlay_manager *mgr = venc.output.manager; venc_write_reg(VENC_OUTPUT_CONTROL, 0); dss_set_dac_pwrdn_bgz(0); @@ -492,15 +492,9 @@ static void venc_power_off(struct omap_dss_device *dssdev) venc_runtime_put(); } -unsigned long venc_get_pixel_clock(void) -{ - /* VENC Pixel Clock in Mhz */ - return 13500000; -} - int omapdss_venc_display_enable(struct omap_dss_device *dssdev) { - struct omap_dss_output *out = dssdev->output; + struct omap_dss_device *out = &venc.output; int r; DSSDBG("venc_display_enable\n"); @@ -513,23 +507,15 @@ int omapdss_venc_display_enable(struct omap_dss_device *dssdev) goto err0; } - r = omap_dss_start_device(dssdev); - if (r) { - DSSERR("failed to start device\n"); - goto err0; - } - r = venc_power_on(dssdev); if (r) - goto err1; + goto err0; venc.wss_data = 0; mutex_unlock(&venc.venc_lock); return 0; -err1: - omap_dss_stop_device(dssdev); err0: mutex_unlock(&venc.venc_lock); return r; @@ -543,8 +529,6 @@ void omapdss_venc_display_disable(struct omap_dss_device *dssdev) venc_power_off(dssdev); - omap_dss_stop_device(dssdev); - mutex_unlock(&venc.venc_lock); } @@ -561,6 +545,8 @@ void omapdss_venc_set_timings(struct omap_dss_device *dssdev, venc.timings = *timings; + dispc_set_tv_pclk(13500000); + mutex_unlock(&venc.venc_lock); } @@ -578,6 +564,16 @@ int omapdss_venc_check_timings(struct omap_dss_device *dssdev, return -EINVAL; } +static void venc_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + mutex_lock(&venc.venc_lock); + + *timings = venc.timings; + + mutex_unlock(&venc.venc_lock); +} + u32 omapdss_venc_get_wss(struct omap_dss_device *dssdev) { /* Invert due to VENC_L21_WC_CTL:INV=1 */ @@ -633,23 +629,22 @@ void omapdss_venc_invert_vid_out_polarity(struct omap_dss_device *dssdev, mutex_unlock(&venc.venc_lock); } -static int venc_init_display(struct omap_dss_device *dssdev) +static int venc_init_regulator(void) { - DSSDBG("init_display\n"); - - if (venc.vdda_dac_reg == NULL) { - struct regulator *vdda_dac; + struct regulator *vdda_dac; - vdda_dac = regulator_get(&venc.pdev->dev, "vdda_dac"); + if (venc.vdda_dac_reg != NULL) + return 0; - if (IS_ERR(vdda_dac)) { - DSSERR("can't get VDDA_DAC regulator\n"); - return PTR_ERR(vdda_dac); - } + vdda_dac = devm_regulator_get(&venc.pdev->dev, "vdda_dac"); - venc.vdda_dac_reg = vdda_dac; + if (IS_ERR(vdda_dac)) { + DSSERR("can't get VDDA_DAC regulator\n"); + return PTR_ERR(vdda_dac); } + venc.vdda_dac_reg = vdda_dac; + return 0; } @@ -765,19 +760,16 @@ static int venc_probe_pdata(struct platform_device *vencdev) if (!plat_dssdev) return 0; + r = venc_init_regulator(); + if (r) + return r; + dssdev = dss_alloc_and_init_device(&vencdev->dev); if (!dssdev) return -ENOMEM; dss_copy_device_pdata(dssdev, plat_dssdev); - r = venc_init_display(dssdev); - if (r) { - DSSERR("device %s init failed: %d\n", dssdev->name, r); - dss_put_device(dssdev); - return r; - } - r = omapdss_output_set_device(&venc.output, dssdev); if (r) { DSSERR("failed to connect output to new device: %s\n", @@ -797,24 +789,87 @@ static int venc_probe_pdata(struct platform_device *vencdev) return 0; } +static int venc_connect(struct omap_dss_device *dssdev, + struct omap_dss_device *dst) +{ + struct omap_overlay_manager *mgr; + int r; + + r = venc_init_regulator(); + if (r) + return r; + + mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); + if (!mgr) + return -ENODEV; + + r = dss_mgr_connect(mgr, dssdev); + if (r) + return r; + + r = omapdss_output_set_device(dssdev, dst); + if (r) { + DSSERR("failed to connect output to new device: %s\n", + dst->name); + dss_mgr_disconnect(mgr, dssdev); + return r; + } + + return 0; +} + +static void venc_disconnect(struct omap_dss_device *dssdev, + struct omap_dss_device *dst) +{ + WARN_ON(dst != dssdev->device); + + if (dst != dssdev->device) + return; + + omapdss_output_unset_device(dssdev); + + if (dssdev->manager) + dss_mgr_disconnect(dssdev->manager, dssdev); +} + +static const struct omapdss_atv_ops venc_ops = { + .connect = venc_connect, + .disconnect = venc_disconnect, + + .enable = omapdss_venc_display_enable, + .disable = omapdss_venc_display_disable, + + .check_timings = omapdss_venc_check_timings, + .set_timings = omapdss_venc_set_timings, + .get_timings = venc_get_timings, + + .set_type = omapdss_venc_set_type, + .invert_vid_out_polarity = omapdss_venc_invert_vid_out_polarity, + + .set_wss = omapdss_venc_set_wss, + .get_wss = omapdss_venc_get_wss, +}; + static void venc_init_output(struct platform_device *pdev) { - struct omap_dss_output *out = &venc.output; + struct omap_dss_device *out = &venc.output; - out->pdev = pdev; + out->dev = &pdev->dev; out->id = OMAP_DSS_OUTPUT_VENC; - out->type = OMAP_DISPLAY_TYPE_VENC; + out->output_type = OMAP_DISPLAY_TYPE_VENC; out->name = "venc.0"; out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT; + out->ops.atv = &venc_ops; + out->owner = THIS_MODULE; - dss_register_output(out); + omapdss_register_output(out); } static void __exit venc_uninit_output(struct platform_device *pdev) { - struct omap_dss_output *out = &venc.output; + struct omap_dss_device *out = &venc.output; - dss_unregister_output(out); + omapdss_unregister_output(out); } /* VENC HW IP initialisation */ @@ -866,16 +921,17 @@ static int omap_venchw_probe(struct platform_device *pdev) venc_init_output(pdev); - r = venc_probe_pdata(pdev); - if (r) { - venc_panel_exit(); - venc_uninit_output(pdev); - pm_runtime_disable(&pdev->dev); - return r; + if (pdev->dev.platform_data) { + r = venc_probe_pdata(pdev); + if (r) + goto err_probe; } return 0; +err_probe: + venc_panel_exit(); + venc_uninit_output(pdev); err_panel_init: err_runtime_get: pm_runtime_disable(&pdev->dev); @@ -886,11 +942,6 @@ static int __exit omap_venchw_remove(struct platform_device *pdev) { dss_unregister_child_devices(&pdev->dev); - if (venc.vdda_dac_reg != NULL) { - regulator_put(venc.vdda_dac_reg); - venc.vdda_dac_reg = NULL; - } - venc_panel_exit(); venc_uninit_output(pdev); diff --git a/drivers/video/omap2/dss/venc_panel.c b/drivers/video/omap2/dss/venc_panel.c index 0d2b1a0834a0..f7d92c57bd73 100644 --- a/drivers/video/omap2/dss/venc_panel.c +++ b/drivers/video/omap2/dss/venc_panel.c @@ -107,19 +107,19 @@ static int venc_panel_probe(struct omap_dss_device *dssdev) dssdev->panel.timings = default_timings; - return device_create_file(&dssdev->dev, &dev_attr_output_type); + return device_create_file(dssdev->dev, &dev_attr_output_type); } static void venc_panel_remove(struct omap_dss_device *dssdev) { - device_remove_file(&dssdev->dev, &dev_attr_output_type); + device_remove_file(dssdev->dev, &dev_attr_output_type); } static int venc_panel_enable(struct omap_dss_device *dssdev) { int r; - dev_dbg(&dssdev->dev, "venc_panel_enable\n"); + dev_dbg(dssdev->dev, "venc_panel_enable\n"); mutex_lock(&venc_panel.lock); @@ -150,7 +150,7 @@ err: static void venc_panel_disable(struct omap_dss_device *dssdev) { - dev_dbg(&dssdev->dev, "venc_panel_disable\n"); + dev_dbg(dssdev->dev, "venc_panel_disable\n"); mutex_lock(&venc_panel.lock); @@ -167,7 +167,7 @@ end: static void venc_panel_set_timings(struct omap_dss_device *dssdev, struct omap_video_timings *timings) { - dev_dbg(&dssdev->dev, "venc_panel_set_timings\n"); + dev_dbg(dssdev->dev, "venc_panel_set_timings\n"); mutex_lock(&venc_panel.lock); @@ -180,21 +180,21 @@ static void venc_panel_set_timings(struct omap_dss_device *dssdev, static int venc_panel_check_timings(struct omap_dss_device *dssdev, struct omap_video_timings *timings) { - dev_dbg(&dssdev->dev, "venc_panel_check_timings\n"); + dev_dbg(dssdev->dev, "venc_panel_check_timings\n"); return omapdss_venc_check_timings(dssdev, timings); } static u32 venc_panel_get_wss(struct omap_dss_device *dssdev) { - dev_dbg(&dssdev->dev, "venc_panel_get_wss\n"); + dev_dbg(dssdev->dev, "venc_panel_get_wss\n"); return omapdss_venc_get_wss(dssdev); } static int venc_panel_set_wss(struct omap_dss_device *dssdev, u32 wss) { - dev_dbg(&dssdev->dev, "venc_panel_set_wss\n"); + dev_dbg(dssdev->dev, "venc_panel_set_wss\n"); return omapdss_venc_set_wss(dssdev, wss); } diff --git a/drivers/video/omap2/omapfb/omapfb-ioctl.c b/drivers/video/omap2/omapfb/omapfb-ioctl.c index d30b45d72649..146b6f5428db 100644 --- a/drivers/video/omap2/omapfb/omapfb-ioctl.c +++ b/drivers/video/omap2/omapfb/omapfb-ioctl.c @@ -770,12 +770,17 @@ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg) case OMAPFB_WAITFORVSYNC: DBG("ioctl WAITFORVSYNC\n"); - if (!display || !display->output || !display->output->manager) { + + if (!display) { r = -EINVAL; break; } - mgr = display->output->manager; + mgr = omapdss_find_mgr_from_display(display); + if (!mgr) { + r = -EINVAL; + break; + } r = mgr->wait_for_vsync(mgr); break; diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c index c84bb8a4d0c4..27d6905683f3 100644 --- a/drivers/video/omap2/omapfb/omapfb-main.c +++ b/drivers/video/omap2/omapfb/omapfb-main.c @@ -1853,6 +1853,8 @@ static void omapfb_free_resources(struct omapfb2_device *fbdev) if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) dssdev->driver->disable(dssdev); + dssdev->driver->disconnect(dssdev); + omap_dss_put_device(dssdev); } @@ -2363,27 +2365,26 @@ static int omapfb_init_connections(struct omapfb2_device *fbdev, int i, r; struct omap_overlay_manager *mgr; - if (!def_dssdev->output) { - dev_err(fbdev->dev, "no output for the default display\n"); - return -EINVAL; + r = def_dssdev->driver->connect(def_dssdev); + if (r) { + dev_err(fbdev->dev, "failed to connect default display\n"); + return r; } for (i = 0; i < fbdev->num_displays; ++i) { struct omap_dss_device *dssdev = fbdev->displays[i].dssdev; - struct omap_dss_output *out = dssdev->output; - - mgr = omap_dss_get_overlay_manager(out->dispc_channel); - if (!mgr || !out) + if (dssdev == def_dssdev) continue; - if (mgr->output) - mgr->unset_output(mgr); - - mgr->set_output(mgr, out); + /* + * We don't care if the connect succeeds or not. We just want to + * connect as many displays as possible. + */ + dssdev->driver->connect(dssdev); } - mgr = def_dssdev->output->manager; + mgr = omapdss_find_mgr_from_display(def_dssdev); if (!mgr) { dev_err(fbdev->dev, "no ovl manager for the default display\n"); @@ -2416,6 +2417,9 @@ static int omapfb_probe(struct platform_device *pdev) DBG("omapfb_probe\n"); + if (omapdss_is_initialized() == false) + return -EPROBE_DEFER; + if (pdev->num_resources != 0) { dev_err(&pdev->dev, "probed for an unknown device\n"); r = -ENODEV; @@ -2499,7 +2503,7 @@ static int omapfb_probe(struct platform_device *pdev) if (def_display == NULL) { dev_err(fbdev->dev, "failed to find default display\n"); - r = -EINVAL; + r = -EPROBE_DEFER; goto cleanup; } diff --git a/drivers/video/omap2/vrfb.c b/drivers/video/omap2/vrfb.c index 5261229c79af..f346b02eee1d 100644 --- a/drivers/video/omap2/vrfb.c +++ b/drivers/video/omap2/vrfb.c @@ -353,11 +353,6 @@ static int __init vrfb_probe(struct platform_device *pdev) /* first resource is the register res, the rest are vrfb contexts */ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - dev_err(&pdev->dev, "can't get vrfb base address\n"); - return -EINVAL; - } - vrfb_base = devm_ioremap_resource(&pdev->dev, mem); if (IS_ERR(vrfb_base)) return PTR_ERR(vrfb_base); diff --git a/drivers/video/ps3fb.c b/drivers/video/ps3fb.c index d9f08c653d62..dbfe2c18a434 100644 --- a/drivers/video/ps3fb.c +++ b/drivers/video/ps3fb.c @@ -710,7 +710,7 @@ static int ps3fb_mmap(struct fb_info *info, struct vm_area_struct *vma) r = vm_iomap_memory(vma, info->fix.smem_start, info->fix.smem_len); dev_dbg(info->device, "ps3fb: mmap framebuffer P(%lx)->V(%lx)\n", - info->fix.smem_start + vma->vm_pgoff << PAGE_SHIFT, + info->fix.smem_start + (vma->vm_pgoff << PAGE_SHIFT), vma->vm_start); return r; diff --git a/drivers/video/simplefb.c b/drivers/video/simplefb.c new file mode 100644 index 000000000000..e2e9e3e61b72 --- /dev/null +++ b/drivers/video/simplefb.c @@ -0,0 +1,234 @@ +/* + * Simplest possible simple frame-buffer driver, as a platform device + * + * Copyright (c) 2013, Stephen Warren + * + * Based on q40fb.c, which was: + * Copyright (C) 2001 Richard Zidlicky <rz@linux-m68k.org> + * + * Also based on offb.c, which was: + * Copyright (C) 1997 Geert Uytterhoeven + * Copyright (C) 1996 Paul Mackerras + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include <linux/errno.h> +#include <linux/fb.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +static struct fb_fix_screeninfo simplefb_fix = { + .id = "simple", + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_TRUECOLOR, + .accel = FB_ACCEL_NONE, +}; + +static struct fb_var_screeninfo simplefb_var = { + .height = -1, + .width = -1, + .activate = FB_ACTIVATE_NOW, + .vmode = FB_VMODE_NONINTERLACED, +}; + +static int simplefb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *info) +{ + u32 *pal = info->pseudo_palette; + u32 cr = red >> (16 - info->var.red.length); + u32 cg = green >> (16 - info->var.green.length); + u32 cb = blue >> (16 - info->var.blue.length); + u32 value; + + if (regno >= 16) + return -EINVAL; + + value = (cr << info->var.red.offset) | + (cg << info->var.green.offset) | + (cb << info->var.blue.offset); + if (info->var.transp.length > 0) { + u32 mask = (1 << info->var.transp.length) - 1; + mask <<= info->var.transp.offset; + value |= mask; + } + pal[regno] = value; + + return 0; +} + +static struct fb_ops simplefb_ops = { + .owner = THIS_MODULE, + .fb_setcolreg = simplefb_setcolreg, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +struct simplefb_format { + const char *name; + u32 bits_per_pixel; + struct fb_bitfield red; + struct fb_bitfield green; + struct fb_bitfield blue; + struct fb_bitfield transp; +}; + +static struct simplefb_format simplefb_formats[] = { + { "r5g6b5", 16, {11, 5}, {5, 6}, {0, 5}, {0, 0} }, +}; + +struct simplefb_params { + u32 width; + u32 height; + u32 stride; + struct simplefb_format *format; +}; + +static int simplefb_parse_dt(struct platform_device *pdev, + struct simplefb_params *params) +{ + struct device_node *np = pdev->dev.of_node; + int ret; + const char *format; + int i; + + ret = of_property_read_u32(np, "width", ¶ms->width); + if (ret) { + dev_err(&pdev->dev, "Can't parse width property\n"); + return ret; + } + + ret = of_property_read_u32(np, "height", ¶ms->height); + if (ret) { + dev_err(&pdev->dev, "Can't parse height property\n"); + return ret; + } + + ret = of_property_read_u32(np, "stride", ¶ms->stride); + if (ret) { + dev_err(&pdev->dev, "Can't parse stride property\n"); + return ret; + } + + ret = of_property_read_string(np, "format", &format); + if (ret) { + dev_err(&pdev->dev, "Can't parse format property\n"); + return ret; + } + params->format = NULL; + for (i = 0; i < ARRAY_SIZE(simplefb_formats); i++) { + if (strcmp(format, simplefb_formats[i].name)) + continue; + params->format = &simplefb_formats[i]; + break; + } + if (!params->format) { + dev_err(&pdev->dev, "Invalid format value\n"); + return -EINVAL; + } + + return 0; +} + +static int simplefb_probe(struct platform_device *pdev) +{ + int ret; + struct simplefb_params params; + struct fb_info *info; + struct resource *mem; + + if (fb_get_options("simplefb", NULL)) + return -ENODEV; + + ret = simplefb_parse_dt(pdev, ¶ms); + if (ret) + return ret; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "No memory resource\n"); + return -EINVAL; + } + + info = framebuffer_alloc(sizeof(u32) * 16, &pdev->dev); + if (!info) + return -ENOMEM; + platform_set_drvdata(pdev, info); + + info->fix = simplefb_fix; + info->fix.smem_start = mem->start; + info->fix.smem_len = resource_size(mem); + info->fix.line_length = params.stride; + + info->var = simplefb_var; + info->var.xres = params.width; + info->var.yres = params.height; + info->var.xres_virtual = params.width; + info->var.yres_virtual = params.height; + info->var.bits_per_pixel = params.format->bits_per_pixel; + info->var.red = params.format->red; + info->var.green = params.format->green; + info->var.blue = params.format->blue; + info->var.transp = params.format->transp; + + info->fbops = &simplefb_ops; + info->flags = FBINFO_DEFAULT; + info->screen_base = devm_ioremap(&pdev->dev, info->fix.smem_start, + info->fix.smem_len); + if (!info->screen_base) { + framebuffer_release(info); + return -ENODEV; + } + info->pseudo_palette = (void *)(info + 1); + + ret = register_framebuffer(info); + if (ret < 0) { + dev_err(&pdev->dev, "Unable to register simplefb: %d\n", ret); + framebuffer_release(info); + return ret; + } + + dev_info(&pdev->dev, "fb%d: simplefb registered!\n", info->node); + + return 0; +} + +static int simplefb_remove(struct platform_device *pdev) +{ + struct fb_info *info = platform_get_drvdata(pdev); + + unregister_framebuffer(info); + framebuffer_release(info); + + return 0; +} + +static const struct of_device_id simplefb_of_match[] = { + { .compatible = "simple-framebuffer", }, + { }, +}; +MODULE_DEVICE_TABLE(of, simplefb_of_match); + +static struct platform_driver simplefb_driver = { + .driver = { + .name = "simple-framebuffer", + .owner = THIS_MODULE, + .of_match_table = simplefb_of_match, + }, + .probe = simplefb_probe, + .remove = simplefb_remove, +}; +module_platform_driver(simplefb_driver); + +MODULE_AUTHOR("Stephen Warren <swarren@wwwdotorg.org>"); +MODULE_DESCRIPTION("Simple framebuffer driver"); +MODULE_LICENSE("GPL v2"); |