diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-12-17 20:26:17 +0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-12-17 20:26:17 +0400 |
commit | 3c2e81ef344a90bb0a39d84af6878b4aeff568a2 (patch) | |
tree | bd8c8b23466174899d2fe4d35af6e1e838edb068 /drivers/gpu/drm/exynos/exynos_hdmi.c | |
parent | 221392c3ad0432e39fd74a349364f66cb0ed78f6 (diff) | |
parent | 55bde6b1442fed8af67b92d21acce67db454c9f9 (diff) | |
download | linux-3c2e81ef344a90bb0a39d84af6878b4aeff568a2.tar.xz |
Merge branch 'drm-next' of git://people.freedesktop.org/~airlied/linux
Pull DRM updates from Dave Airlie:
"This is the one and only next pull for 3.8, we had a regression we
found last week, so I was waiting for that to resolve itself, and I
ended up with some Intel fixes on top as well.
Highlights:
- new driver: nvidia tegra 20/30/hdmi support
- radeon: add support for previously unused DMA engines, more HDMI
regs, eviction speeds ups and fixes
- i915: HSW support enable, agp removal on GEN6, seqno wrapping
- exynos: IPP subsystem support (image post proc), HDMI
- nouveau: display class reworking, nv20->40 z compression
- ttm: start of locking fixes, rcu usage for lookups,
- core: documentation updates, docbook integration, monotonic clock
usage, move from connector to object properties"
* 'drm-next' of git://people.freedesktop.org/~airlied/linux: (590 commits)
drm/exynos: add gsc ipp driver
drm/exynos: add rotator ipp driver
drm/exynos: add fimc ipp driver
drm/exynos: add iommu support for ipp
drm/exynos: add ipp subsystem
drm/exynos: support device tree for fimd
radeon: fix regression with eviction since evict caching changes
drm/radeon: add more pedantic checks in the CP DMA checker
drm/radeon: bump version for CS ioctl support for async DMA
drm/radeon: enable the async DMA rings in the CS ioctl
drm/radeon: add VM CS parser support for async DMA on cayman/TN/SI
drm/radeon/kms: add evergreen/cayman CS parser for async DMA (v2)
drm/radeon/kms: add 6xx/7xx CS parser for async DMA (v2)
drm/radeon: fix htile buffer size computation for command stream checker
drm/radeon: fix fence locking in the pageflip callback
drm/radeon: make indirect register access concurrency-safe
drm/radeon: add W|RREG32_IDX for MM_INDEX|DATA based mmio accesss
drm/exynos: support extended screen coordinate of fimd
drm/exynos: fix x, y coordinates for right bottom pixel
drm/exynos: fix fb offset calculation for plane
...
Diffstat (limited to 'drivers/gpu/drm/exynos/exynos_hdmi.c')
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_hdmi.c | 324 |
1 files changed, 248 insertions, 76 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 2c115f8a62a3..2c46b6c0b82c 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -50,6 +50,29 @@ #define MAX_HEIGHT 1080 #define get_hdmi_context(dev) platform_get_drvdata(to_platform_device(dev)) +/* AVI header and aspect ratio */ +#define HDMI_AVI_VERSION 0x02 +#define HDMI_AVI_LENGTH 0x0D +#define AVI_PIC_ASPECT_RATIO_16_9 (2 << 4) +#define AVI_SAME_AS_PIC_ASPECT_RATIO 8 + +/* AUI header info */ +#define HDMI_AUI_VERSION 0x01 +#define HDMI_AUI_LENGTH 0x0A + +/* HDMI infoframe to configure HDMI out packet header, AUI and AVI */ +enum HDMI_PACKET_TYPE { + /* refer to Table 5-8 Packet Type in HDMI specification v1.4a */ + /* InfoFrame packet type */ + HDMI_PACKET_TYPE_INFOFRAME = 0x80, + /* Vendor-Specific InfoFrame */ + HDMI_PACKET_TYPE_VSI = HDMI_PACKET_TYPE_INFOFRAME + 1, + /* Auxiliary Video information InfoFrame */ + HDMI_PACKET_TYPE_AVI = HDMI_PACKET_TYPE_INFOFRAME + 2, + /* Audio information InfoFrame */ + HDMI_PACKET_TYPE_AUI = HDMI_PACKET_TYPE_INFOFRAME + 4 +}; + enum hdmi_type { HDMI_TYPE13, HDMI_TYPE14, @@ -74,6 +97,7 @@ struct hdmi_context { struct mutex hdmi_mutex; void __iomem *regs; + void *parent_ctx; int external_irq; int internal_irq; @@ -84,7 +108,6 @@ struct hdmi_context { int cur_conf; struct hdmi_resources res; - void *parent_ctx; int hpd_gpio; @@ -182,6 +205,7 @@ struct hdmi_v13_conf { int height; int vrefresh; bool interlace; + int cea_video_id; const u8 *hdmiphy_data; const struct hdmi_v13_preset_conf *conf; }; @@ -353,15 +377,20 @@ static const struct hdmi_v13_preset_conf hdmi_v13_conf_1080p60 = { }; static const struct hdmi_v13_conf hdmi_v13_confs[] = { - { 1280, 720, 60, false, hdmiphy_v13_conf74_25, &hdmi_v13_conf_720p60 }, - { 1280, 720, 50, false, hdmiphy_v13_conf74_25, &hdmi_v13_conf_720p60 }, - { 720, 480, 60, false, hdmiphy_v13_conf27_027, &hdmi_v13_conf_480p }, - { 1920, 1080, 50, true, hdmiphy_v13_conf74_25, &hdmi_v13_conf_1080i50 }, - { 1920, 1080, 50, false, hdmiphy_v13_conf148_5, - &hdmi_v13_conf_1080p50 }, - { 1920, 1080, 60, true, hdmiphy_v13_conf74_25, &hdmi_v13_conf_1080i60 }, - { 1920, 1080, 60, false, hdmiphy_v13_conf148_5, - &hdmi_v13_conf_1080p60 }, + { 1280, 720, 60, false, 4, hdmiphy_v13_conf74_25, + &hdmi_v13_conf_720p60 }, + { 1280, 720, 50, false, 19, hdmiphy_v13_conf74_25, + &hdmi_v13_conf_720p60 }, + { 720, 480, 60, false, 3, hdmiphy_v13_conf27_027, + &hdmi_v13_conf_480p }, + { 1920, 1080, 50, true, 20, hdmiphy_v13_conf74_25, + &hdmi_v13_conf_1080i50 }, + { 1920, 1080, 50, false, 31, hdmiphy_v13_conf148_5, + &hdmi_v13_conf_1080p50 }, + { 1920, 1080, 60, true, 5, hdmiphy_v13_conf74_25, + &hdmi_v13_conf_1080i60 }, + { 1920, 1080, 60, false, 16, hdmiphy_v13_conf148_5, + &hdmi_v13_conf_1080p60 }, }; /* HDMI Version 1.4 */ @@ -479,6 +508,7 @@ struct hdmi_conf { int height; int vrefresh; bool interlace; + int cea_video_id; const u8 *hdmiphy_data; const struct hdmi_preset_conf *conf; }; @@ -934,16 +964,21 @@ static const struct hdmi_preset_conf hdmi_conf_1080p60 = { }; static const struct hdmi_conf hdmi_confs[] = { - { 720, 480, 60, false, hdmiphy_conf27_027, &hdmi_conf_480p60 }, - { 1280, 720, 50, false, hdmiphy_conf74_25, &hdmi_conf_720p50 }, - { 1280, 720, 60, false, hdmiphy_conf74_25, &hdmi_conf_720p60 }, - { 1920, 1080, 50, true, hdmiphy_conf74_25, &hdmi_conf_1080i50 }, - { 1920, 1080, 60, true, hdmiphy_conf74_25, &hdmi_conf_1080i60 }, - { 1920, 1080, 30, false, hdmiphy_conf74_176, &hdmi_conf_1080p30 }, - { 1920, 1080, 50, false, hdmiphy_conf148_5, &hdmi_conf_1080p50 }, - { 1920, 1080, 60, false, hdmiphy_conf148_5, &hdmi_conf_1080p60 }, + { 720, 480, 60, false, 3, hdmiphy_conf27_027, &hdmi_conf_480p60 }, + { 1280, 720, 50, false, 19, hdmiphy_conf74_25, &hdmi_conf_720p50 }, + { 1280, 720, 60, false, 4, hdmiphy_conf74_25, &hdmi_conf_720p60 }, + { 1920, 1080, 50, true, 20, hdmiphy_conf74_25, &hdmi_conf_1080i50 }, + { 1920, 1080, 60, true, 5, hdmiphy_conf74_25, &hdmi_conf_1080i60 }, + { 1920, 1080, 30, false, 34, hdmiphy_conf74_176, &hdmi_conf_1080p30 }, + { 1920, 1080, 50, false, 31, hdmiphy_conf148_5, &hdmi_conf_1080p50 }, + { 1920, 1080, 60, false, 16, hdmiphy_conf148_5, &hdmi_conf_1080p60 }, }; +struct hdmi_infoframe { + enum HDMI_PACKET_TYPE type; + u8 ver; + u8 len; +}; static inline u32 hdmi_reg_read(struct hdmi_context *hdata, u32 reg_id) { @@ -1267,6 +1302,88 @@ static int hdmi_conf_index(struct hdmi_context *hdata, return hdmi_v14_conf_index(mode); } +static u8 hdmi_chksum(struct hdmi_context *hdata, + u32 start, u8 len, u32 hdr_sum) +{ + int i; + + /* hdr_sum : header0 + header1 + header2 + * start : start address of packet byte1 + * len : packet bytes - 1 */ + for (i = 0; i < len; ++i) + hdr_sum += 0xff & hdmi_reg_read(hdata, start + i * 4); + + /* return 2's complement of 8 bit hdr_sum */ + return (u8)(~(hdr_sum & 0xff) + 1); +} + +static void hdmi_reg_infoframe(struct hdmi_context *hdata, + struct hdmi_infoframe *infoframe) +{ + u32 hdr_sum; + u8 chksum; + u32 aspect_ratio; + u32 mod; + u32 vic; + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + mod = hdmi_reg_read(hdata, HDMI_MODE_SEL); + if (hdata->dvi_mode) { + hdmi_reg_writeb(hdata, HDMI_VSI_CON, + HDMI_VSI_CON_DO_NOT_TRANSMIT); + hdmi_reg_writeb(hdata, HDMI_AVI_CON, + HDMI_AVI_CON_DO_NOT_TRANSMIT); + hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_NO_TRAN); + return; + } + + switch (infoframe->type) { + case HDMI_PACKET_TYPE_AVI: + hdmi_reg_writeb(hdata, HDMI_AVI_CON, HDMI_AVI_CON_EVERY_VSYNC); + hdmi_reg_writeb(hdata, HDMI_AVI_HEADER0, infoframe->type); + hdmi_reg_writeb(hdata, HDMI_AVI_HEADER1, infoframe->ver); + hdmi_reg_writeb(hdata, HDMI_AVI_HEADER2, infoframe->len); + hdr_sum = infoframe->type + infoframe->ver + infoframe->len; + + /* Output format zero hardcoded ,RGB YBCR selection */ + hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(1), 0 << 5 | + AVI_ACTIVE_FORMAT_VALID | + AVI_UNDERSCANNED_DISPLAY_VALID); + + aspect_ratio = AVI_PIC_ASPECT_RATIO_16_9; + + hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(2), aspect_ratio | + AVI_SAME_AS_PIC_ASPECT_RATIO); + + if (hdata->type == HDMI_TYPE13) + vic = hdmi_v13_confs[hdata->cur_conf].cea_video_id; + else + vic = hdmi_confs[hdata->cur_conf].cea_video_id; + + hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(4), vic); + + chksum = hdmi_chksum(hdata, HDMI_AVI_BYTE(1), + infoframe->len, hdr_sum); + DRM_DEBUG_KMS("AVI checksum = 0x%x\n", chksum); + hdmi_reg_writeb(hdata, HDMI_AVI_CHECK_SUM, chksum); + break; + case HDMI_PACKET_TYPE_AUI: + hdmi_reg_writeb(hdata, HDMI_AUI_CON, 0x02); + hdmi_reg_writeb(hdata, HDMI_AUI_HEADER0, infoframe->type); + hdmi_reg_writeb(hdata, HDMI_AUI_HEADER1, infoframe->ver); + hdmi_reg_writeb(hdata, HDMI_AUI_HEADER2, infoframe->len); + hdr_sum = infoframe->type + infoframe->ver + infoframe->len; + chksum = hdmi_chksum(hdata, HDMI_AUI_BYTE(1), + infoframe->len, hdr_sum); + DRM_DEBUG_KMS("AUI checksum = 0x%x\n", chksum); + hdmi_reg_writeb(hdata, HDMI_AUI_CHECK_SUM, chksum); + break; + default: + break; + } +} + static bool hdmi_is_connected(void *ctx) { struct hdmi_context *hdata = ctx; @@ -1293,6 +1410,7 @@ static int hdmi_get_edid(void *ctx, struct drm_connector *connector, DRM_DEBUG_KMS("%s : width[%d] x height[%d]\n", (hdata->dvi_mode ? "dvi monitor" : "hdmi monitor"), raw_edid->width_cm, raw_edid->height_cm); + kfree(raw_edid); } else { return -ENODEV; } @@ -1541,6 +1659,8 @@ static void hdmi_conf_reset(struct hdmi_context *hdata) static void hdmi_conf_init(struct hdmi_context *hdata) { + struct hdmi_infoframe infoframe; + /* disable HPD interrupts */ hdmi_reg_writemask(hdata, HDMI_INTC_CON, 0, HDMI_INTC_EN_GLOBAL | HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG); @@ -1575,9 +1695,17 @@ static void hdmi_conf_init(struct hdmi_context *hdata) hdmi_reg_writeb(hdata, HDMI_V13_AUI_CON, 0x02); hdmi_reg_writeb(hdata, HDMI_V13_ACR_CON, 0x04); } else { + infoframe.type = HDMI_PACKET_TYPE_AVI; + infoframe.ver = HDMI_AVI_VERSION; + infoframe.len = HDMI_AVI_LENGTH; + hdmi_reg_infoframe(hdata, &infoframe); + + infoframe.type = HDMI_PACKET_TYPE_AUI; + infoframe.ver = HDMI_AUI_VERSION; + infoframe.len = HDMI_AUI_LENGTH; + hdmi_reg_infoframe(hdata, &infoframe); + /* enable AVI packet every vsync, fixes purple line problem */ - hdmi_reg_writeb(hdata, HDMI_AVI_CON, 0x02); - hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(1), 2 << 5); hdmi_reg_writemask(hdata, HDMI_CON_1, 2, 3 << 5); } } @@ -1875,6 +2003,24 @@ static void hdmiphy_conf_reset(struct hdmi_context *hdata) mdelay(10); } +static void hdmiphy_poweron(struct hdmi_context *hdata) +{ + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + if (hdata->type == HDMI_TYPE14) + hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, 0, + HDMI_PHY_POWER_OFF_EN); +} + +static void hdmiphy_poweroff(struct hdmi_context *hdata) +{ + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + if (hdata->type == HDMI_TYPE14) + hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, ~0, + HDMI_PHY_POWER_OFF_EN); +} + static void hdmiphy_conf_apply(struct hdmi_context *hdata) { const u8 *hdmiphy_data; @@ -1978,9 +2124,18 @@ static void hdmi_mode_fixup(void *ctx, struct drm_connector *connector, index = hdmi_v14_conf_index(m); if (index >= 0) { + struct drm_mode_object base; + struct list_head head; + DRM_INFO("desired mode doesn't exist so\n"); DRM_INFO("use the most suitable mode among modes.\n"); + + /* preserve display mode header while copying. */ + head = adjusted_mode->head; + base = adjusted_mode->base; memcpy(adjusted_mode, m, sizeof(*m)); + adjusted_mode->head = head; + adjusted_mode->base = base; break; } } @@ -2034,12 +2189,12 @@ static void hdmi_poweron(struct hdmi_context *hdata) mutex_unlock(&hdata->hdmi_mutex); - pm_runtime_get_sync(hdata->dev); - regulator_bulk_enable(res->regul_count, res->regul_bulk); clk_enable(res->hdmiphy); clk_enable(res->hdmi); clk_enable(res->sclk_hdmi); + + hdmiphy_poweron(hdata); } static void hdmi_poweroff(struct hdmi_context *hdata) @@ -2058,14 +2213,13 @@ static void hdmi_poweroff(struct hdmi_context *hdata) * its reset state seems to meet the condition. */ hdmiphy_conf_reset(hdata); + hdmiphy_poweroff(hdata); clk_disable(res->sclk_hdmi); clk_disable(res->hdmi); clk_disable(res->hdmiphy); regulator_bulk_disable(res->regul_count, res->regul_bulk); - pm_runtime_put_sync(hdata->dev); - mutex_lock(&hdata->hdmi_mutex); hdata->powered = false; @@ -2078,16 +2232,18 @@ static void hdmi_dpms(void *ctx, int mode) { struct hdmi_context *hdata = ctx; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + DRM_DEBUG_KMS("[%d] %s mode %d\n", __LINE__, __func__, mode); switch (mode) { case DRM_MODE_DPMS_ON: - hdmi_poweron(hdata); + if (pm_runtime_suspended(hdata->dev)) + pm_runtime_get_sync(hdata->dev); break; case DRM_MODE_DPMS_STANDBY: case DRM_MODE_DPMS_SUSPEND: case DRM_MODE_DPMS_OFF: - hdmi_poweroff(hdata); + if (!pm_runtime_suspended(hdata->dev)) + pm_runtime_put_sync(hdata->dev); break; default: DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode); @@ -2166,27 +2322,27 @@ static int __devinit hdmi_resources_init(struct hdmi_context *hdata) memset(res, 0, sizeof(*res)); /* get clocks, power */ - res->hdmi = clk_get(dev, "hdmi"); + res->hdmi = devm_clk_get(dev, "hdmi"); if (IS_ERR_OR_NULL(res->hdmi)) { DRM_ERROR("failed to get clock 'hdmi'\n"); goto fail; } - res->sclk_hdmi = clk_get(dev, "sclk_hdmi"); + res->sclk_hdmi = devm_clk_get(dev, "sclk_hdmi"); if (IS_ERR_OR_NULL(res->sclk_hdmi)) { DRM_ERROR("failed to get clock 'sclk_hdmi'\n"); goto fail; } - res->sclk_pixel = clk_get(dev, "sclk_pixel"); + res->sclk_pixel = devm_clk_get(dev, "sclk_pixel"); if (IS_ERR_OR_NULL(res->sclk_pixel)) { DRM_ERROR("failed to get clock 'sclk_pixel'\n"); goto fail; } - res->sclk_hdmiphy = clk_get(dev, "sclk_hdmiphy"); + res->sclk_hdmiphy = devm_clk_get(dev, "sclk_hdmiphy"); if (IS_ERR_OR_NULL(res->sclk_hdmiphy)) { DRM_ERROR("failed to get clock 'sclk_hdmiphy'\n"); goto fail; } - res->hdmiphy = clk_get(dev, "hdmiphy"); + res->hdmiphy = devm_clk_get(dev, "hdmiphy"); if (IS_ERR_OR_NULL(res->hdmiphy)) { DRM_ERROR("failed to get clock 'hdmiphy'\n"); goto fail; @@ -2194,7 +2350,7 @@ static int __devinit hdmi_resources_init(struct hdmi_context *hdata) clk_set_parent(res->sclk_hdmi, res->sclk_pixel); - res->regul_bulk = kzalloc(ARRAY_SIZE(supply) * + res->regul_bulk = devm_kzalloc(dev, ARRAY_SIZE(supply) * sizeof(res->regul_bulk[0]), GFP_KERNEL); if (!res->regul_bulk) { DRM_ERROR("failed to get memory for regulators\n"); @@ -2204,7 +2360,7 @@ static int __devinit hdmi_resources_init(struct hdmi_context *hdata) res->regul_bulk[i].supply = supply[i]; res->regul_bulk[i].consumer = NULL; } - ret = regulator_bulk_get(dev, ARRAY_SIZE(supply), res->regul_bulk); + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(supply), res->regul_bulk); if (ret) { DRM_ERROR("failed to get regulators\n"); goto fail; @@ -2217,28 +2373,6 @@ fail: return -ENODEV; } -static int hdmi_resources_cleanup(struct hdmi_context *hdata) -{ - struct hdmi_resources *res = &hdata->res; - - regulator_bulk_free(res->regul_count, res->regul_bulk); - /* kfree is NULL-safe */ - kfree(res->regul_bulk); - if (!IS_ERR_OR_NULL(res->hdmiphy)) - clk_put(res->hdmiphy); - if (!IS_ERR_OR_NULL(res->sclk_hdmiphy)) - clk_put(res->sclk_hdmiphy); - if (!IS_ERR_OR_NULL(res->sclk_pixel)) - clk_put(res->sclk_pixel); - if (!IS_ERR_OR_NULL(res->sclk_hdmi)) - clk_put(res->sclk_hdmi); - if (!IS_ERR_OR_NULL(res->hdmi)) - clk_put(res->hdmi); - memset(res, 0, sizeof(*res)); - - return 0; -} - static struct i2c_client *hdmi_ddc, *hdmi_hdmiphy; void hdmi_attach_ddc_client(struct i2c_client *ddc) @@ -2306,6 +2440,7 @@ static struct platform_device_id hdmi_driver_types[] = { } }; +#ifdef CONFIG_OF static struct of_device_id hdmi_match_types[] = { { .compatible = "samsung,exynos5-hdmi", @@ -2314,6 +2449,7 @@ static struct of_device_id hdmi_match_types[] = { /* end node */ } }; +#endif static int __devinit hdmi_probe(struct platform_device *pdev) { @@ -2366,6 +2502,8 @@ static int __devinit hdmi_probe(struct platform_device *pdev) const struct of_device_id *match; match = of_match_node(of_match_ptr(hdmi_match_types), pdev->dev.of_node); + if (match == NULL) + return -ENODEV; hdata->type = (enum hdmi_type)match->data; } else { hdata->type = (enum hdmi_type)platform_get_device_id @@ -2378,36 +2516,32 @@ static int __devinit hdmi_probe(struct platform_device *pdev) ret = hdmi_resources_init(hdata); if (ret) { - ret = -EINVAL; DRM_ERROR("hdmi_resources_init failed\n"); - goto err_data; + return -EINVAL; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { DRM_ERROR("failed to find registers\n"); - ret = -ENOENT; - goto err_resource; + return -ENOENT; } hdata->regs = devm_request_and_ioremap(&pdev->dev, res); if (!hdata->regs) { DRM_ERROR("failed to map registers\n"); - ret = -ENXIO; - goto err_resource; + return -ENXIO; } - ret = gpio_request(hdata->hpd_gpio, "HPD"); + ret = devm_gpio_request(&pdev->dev, hdata->hpd_gpio, "HPD"); if (ret) { DRM_ERROR("failed to request HPD gpio\n"); - goto err_resource; + return ret; } /* DDC i2c driver */ if (i2c_add_driver(&ddc_driver)) { DRM_ERROR("failed to register ddc i2c driver\n"); - ret = -ENOENT; - goto err_gpio; + return -ENOENT; } hdata->ddc_port = hdmi_ddc; @@ -2470,11 +2604,6 @@ err_hdmiphy: i2c_del_driver(&hdmiphy_driver); err_ddc: i2c_del_driver(&ddc_driver); -err_gpio: - gpio_free(hdata->hpd_gpio); -err_resource: - hdmi_resources_cleanup(hdata); -err_data: return ret; } @@ -2491,9 +2620,6 @@ static int __devexit hdmi_remove(struct platform_device *pdev) free_irq(hdata->internal_irq, hdata); free_irq(hdata->external_irq, hdata); - gpio_free(hdata->hpd_gpio); - - hdmi_resources_cleanup(hdata); /* hdmiphy i2c driver */ i2c_del_driver(&hdmiphy_driver); @@ -2509,6 +2635,8 @@ static int hdmi_suspend(struct device *dev) struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); struct hdmi_context *hdata = ctx->ctx; + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + disable_irq(hdata->internal_irq); disable_irq(hdata->external_irq); @@ -2516,6 +2644,11 @@ static int hdmi_suspend(struct device *dev) if (ctx->drm_dev) drm_helper_hpd_irq_event(ctx->drm_dev); + if (pm_runtime_suspended(dev)) { + DRM_DEBUG_KMS("%s : Already suspended\n", __func__); + return 0; + } + hdmi_poweroff(hdata); return 0; @@ -2526,13 +2659,52 @@ static int hdmi_resume(struct device *dev) struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); struct hdmi_context *hdata = ctx->ctx; + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + hdata->hpd = gpio_get_value(hdata->hpd_gpio); + enable_irq(hdata->external_irq); enable_irq(hdata->internal_irq); + + if (!pm_runtime_suspended(dev)) { + DRM_DEBUG_KMS("%s : Already resumed\n", __func__); + return 0; + } + + hdmi_poweron(hdata); + return 0; } #endif -static SIMPLE_DEV_PM_OPS(hdmi_pm_ops, hdmi_suspend, hdmi_resume); +#ifdef CONFIG_PM_RUNTIME +static int hdmi_runtime_suspend(struct device *dev) +{ + struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); + struct hdmi_context *hdata = ctx->ctx; + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + hdmi_poweroff(hdata); + + return 0; +} + +static int hdmi_runtime_resume(struct device *dev) +{ + struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); + struct hdmi_context *hdata = ctx->ctx; + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + hdmi_poweron(hdata); + + return 0; +} +#endif + +static const struct dev_pm_ops hdmi_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(hdmi_suspend, hdmi_resume) + SET_RUNTIME_PM_OPS(hdmi_runtime_suspend, hdmi_runtime_resume, NULL) +}; struct platform_driver hdmi_driver = { .probe = hdmi_probe, @@ -2542,6 +2714,6 @@ struct platform_driver hdmi_driver = { .name = "exynos-hdmi", .owner = THIS_MODULE, .pm = &hdmi_pm_ops, - .of_match_table = hdmi_match_types, + .of_match_table = of_match_ptr(hdmi_match_types), }, }; |