diff options
Diffstat (limited to 'drivers/gpu/drm/exynos/exynos_hdmi.c')
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_hdmi.c | 472 |
1 files changed, 287 insertions, 185 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index c021ddc1ffb4..9a6d652a3ef2 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -33,38 +33,42 @@ #include <linux/regulator/consumer.h> #include <linux/io.h> #include <linux/of.h> +#include <linux/i2c.h> #include <linux/of_gpio.h> #include <linux/hdmi.h> #include <drm/exynos_drm.h> #include "exynos_drm_drv.h" -#include "exynos_drm_hdmi.h" - -#include "exynos_hdmi.h" +#include "exynos_mixer.h" #include <linux/gpio.h> #include <media/s5p_hdmi.h> -#define MAX_WIDTH 1920 -#define MAX_HEIGHT 1080 -#define get_hdmi_context(dev) platform_get_drvdata(to_platform_device(dev)) +#define get_hdmi_display(dev) platform_get_drvdata(to_platform_device(dev)) +#define ctx_from_connector(c) container_of(c, struct hdmi_context, connector) /* 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 +#define AVI_SAME_AS_PIC_ASPECT_RATIO 0x8 +#define AVI_4_3_CENTER_RATIO 0x9 +#define AVI_16_9_CENTER_RATIO 0xa enum hdmi_type { HDMI_TYPE13, HDMI_TYPE14, }; +struct hdmi_driver_data { + unsigned int type; + unsigned int is_apb_phy:1; +}; + struct hdmi_resources { struct clk *hdmi; struct clk *sclk_hdmi; @@ -162,6 +166,7 @@ struct hdmi_v14_conf { struct hdmi_conf_regs { int pixel_clock; int cea_video_id; + enum hdmi_picture_aspect aspect_ratio; union { struct hdmi_v13_conf v13_conf; struct hdmi_v14_conf v14_conf; @@ -171,16 +176,17 @@ struct hdmi_conf_regs { struct hdmi_context { struct device *dev; struct drm_device *drm_dev; + struct drm_connector connector; + struct drm_encoder *encoder; bool hpd; bool powered; bool dvi_mode; struct mutex hdmi_mutex; void __iomem *regs; - void *parent_ctx; int irq; - struct i2c_client *ddc_port; + struct i2c_adapter *ddc_adpt; struct i2c_client *hdmiphy_port; /* current hdmiphy conf regs */ @@ -198,6 +204,14 @@ struct hdmiphy_config { u8 conf[32]; }; +struct hdmi_driver_data exynos4212_hdmi_driver_data = { + .type = HDMI_TYPE14, +}; + +struct hdmi_driver_data exynos5_hdmi_driver_data = { + .type = HDMI_TYPE14, +}; + /* list of phy config settings */ static const struct hdmiphy_config hdmiphy_v13_configs[] = { { @@ -303,6 +317,24 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = { }, }, { + .pixel_clock = 71000000, + .conf = { + 0x01, 0x91, 0x1e, 0x15, 0x40, 0x3c, 0xce, 0x08, + 0x04, 0x20, 0xb2, 0xd8, 0x45, 0xa0, 0xac, 0x80, + 0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86, + 0x54, 0xad, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80, + }, + }, + { + .pixel_clock = 73250000, + .conf = { + 0x01, 0xd1, 0x1f, 0x15, 0x40, 0x18, 0xe9, 0x08, + 0x02, 0xa0, 0xb7, 0xd8, 0x45, 0xa0, 0xac, 0x80, + 0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86, + 0x54, 0xa8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80, + }, + }, + { .pixel_clock = 74176000, .conf = { 0x01, 0xd1, 0x3e, 0x35, 0x40, 0x5b, 0xde, 0x08, @@ -330,6 +362,15 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = { }, }, { + .pixel_clock = 88750000, + .conf = { + 0x01, 0x91, 0x25, 0x17, 0x40, 0x30, 0xfe, 0x08, + 0x06, 0x20, 0xde, 0xd8, 0x45, 0xa0, 0xac, 0x80, + 0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86, + 0x54, 0x8a, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80, + }, + }, + { .pixel_clock = 106500000, .conf = { 0x01, 0xd1, 0x2c, 0x12, 0x40, 0x0c, 0x09, 0x08, @@ -348,6 +389,24 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = { }, }, { + .pixel_clock = 115500000, + .conf = { + 0x01, 0xd1, 0x30, 0x1a, 0x40, 0x40, 0x10, 0x04, + 0x04, 0xa0, 0x21, 0xd9, 0x45, 0xa0, 0xac, 0x80, + 0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86, + 0x54, 0xaa, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80, + }, + }, + { + .pixel_clock = 119000000, + .conf = { + 0x01, 0x91, 0x32, 0x14, 0x40, 0x60, 0xd8, 0x08, + 0x06, 0x20, 0x2a, 0xd9, 0x45, 0xa0, 0xac, 0x80, + 0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86, + 0x54, 0x9d, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80, + }, + }, + { .pixel_clock = 146250000, .conf = { 0x01, 0xd1, 0x3d, 0x15, 0x40, 0x18, 0xfd, 0x08, @@ -668,7 +727,6 @@ static void hdmi_reg_infoframe(struct hdmi_context *hdata, { u32 hdr_sum; u8 chksum; - u32 aspect_ratio; u32 mod; u32 vic; @@ -697,10 +755,28 @@ static void hdmi_reg_infoframe(struct hdmi_context *hdata, 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); + /* + * Set the aspect ratio as per the mode, mentioned in + * Table 9 AVI InfoFrame Data Byte 2 of CEA-861-D Standard + */ + switch (hdata->mode_conf.aspect_ratio) { + case HDMI_PICTURE_ASPECT_4_3: + hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(2), + hdata->mode_conf.aspect_ratio | + AVI_4_3_CENTER_RATIO); + break; + case HDMI_PICTURE_ASPECT_16_9: + hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(2), + hdata->mode_conf.aspect_ratio | + AVI_16_9_CENTER_RATIO); + break; + case HDMI_PICTURE_ASPECT_NONE: + default: + hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(2), + hdata->mode_conf.aspect_ratio | + AVI_SAME_AS_PIC_ASPECT_RATIO); + break; + } vic = hdata->mode_conf.cea_video_id; hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(4), vic); @@ -728,31 +804,46 @@ static void hdmi_reg_infoframe(struct hdmi_context *hdata, } } -static bool hdmi_is_connected(void *ctx) +static enum drm_connector_status hdmi_detect(struct drm_connector *connector, + bool force) { - struct hdmi_context *hdata = ctx; + struct hdmi_context *hdata = ctx_from_connector(connector); + + return hdata->hpd ? connector_status_connected : + connector_status_disconnected; +} - return hdata->hpd; +static void hdmi_connector_destroy(struct drm_connector *connector) +{ } -static struct edid *hdmi_get_edid(void *ctx, struct drm_connector *connector) +static struct drm_connector_funcs hdmi_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .fill_modes = drm_helper_probe_single_connector_modes, + .detect = hdmi_detect, + .destroy = hdmi_connector_destroy, +}; + +static int hdmi_get_modes(struct drm_connector *connector) { - struct edid *raw_edid; - struct hdmi_context *hdata = ctx; + struct hdmi_context *hdata = ctx_from_connector(connector); + struct edid *edid; - if (!hdata->ddc_port) - return ERR_PTR(-ENODEV); + if (!hdata->ddc_adpt) + return -ENODEV; - raw_edid = drm_get_edid(connector, hdata->ddc_port->adapter); - if (!raw_edid) - return ERR_PTR(-ENODEV); + edid = drm_get_edid(connector, hdata->ddc_adpt); + if (!edid) + return -ENODEV; - hdata->dvi_mode = !drm_detect_hdmi_monitor(raw_edid); + hdata->dvi_mode = !drm_detect_hdmi_monitor(edid); 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); + edid->width_cm, edid->height_cm); - return raw_edid; + drm_mode_connector_update_edid_property(connector, edid); + + return drm_add_edid_modes(connector, edid); } static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock) @@ -777,9 +868,10 @@ static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock) return -EINVAL; } -static int hdmi_check_mode(void *ctx, struct drm_display_mode *mode) +static int hdmi_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) { - struct hdmi_context *hdata = ctx; + struct hdmi_context *hdata = ctx_from_connector(connector); int ret; DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n", @@ -787,12 +879,103 @@ static int hdmi_check_mode(void *ctx, struct drm_display_mode *mode) (mode->flags & DRM_MODE_FLAG_INTERLACE) ? true : false, mode->clock * 1000); + ret = mixer_check_mode(mode); + if (ret) + return MODE_BAD; + ret = hdmi_find_phy_conf(hdata, mode->clock * 1000); if (ret < 0) + return MODE_BAD; + + return MODE_OK; +} + +static struct drm_encoder *hdmi_best_encoder(struct drm_connector *connector) +{ + struct hdmi_context *hdata = ctx_from_connector(connector); + + return hdata->encoder; +} + +static struct drm_connector_helper_funcs hdmi_connector_helper_funcs = { + .get_modes = hdmi_get_modes, + .mode_valid = hdmi_mode_valid, + .best_encoder = hdmi_best_encoder, +}; + +static int hdmi_create_connector(struct exynos_drm_display *display, + struct drm_encoder *encoder) +{ + struct hdmi_context *hdata = display->ctx; + struct drm_connector *connector = &hdata->connector; + int ret; + + hdata->encoder = encoder; + connector->interlace_allowed = true; + connector->polled = DRM_CONNECTOR_POLL_HPD; + + ret = drm_connector_init(hdata->drm_dev, connector, + &hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA); + if (ret) { + DRM_ERROR("Failed to initialize connector with drm\n"); return ret; + } + + drm_connector_helper_add(connector, &hdmi_connector_helper_funcs); + drm_sysfs_connector_add(connector); + drm_mode_connector_attach_encoder(connector, encoder); + + return 0; +} + +static int hdmi_initialize(struct exynos_drm_display *display, + struct drm_device *drm_dev) +{ + struct hdmi_context *hdata = display->ctx; + + hdata->drm_dev = drm_dev; + return 0; } +static void hdmi_mode_fixup(struct exynos_drm_display *display, + struct drm_connector *connector, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_display_mode *m; + int mode_ok; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + drm_mode_set_crtcinfo(adjusted_mode, 0); + + mode_ok = hdmi_mode_valid(connector, adjusted_mode); + + /* just return if user desired mode exists. */ + if (mode_ok == MODE_OK) + return; + + /* + * otherwise, find the most suitable mode among modes and change it + * to adjusted_mode. + */ + list_for_each_entry(m, &connector->modes, head) { + mode_ok = hdmi_mode_valid(connector, m); + + if (mode_ok == MODE_OK) { + DRM_INFO("desired mode doesn't exist so\n"); + DRM_INFO("use the most suitable mode among modes.\n"); + + DRM_DEBUG_KMS("Adjusted Mode: [%d]x[%d] [%d]Hz\n", + m->hdisplay, m->vdisplay, m->vrefresh); + + drm_mode_copy(adjusted_mode, m); + break; + } + } +} + static void hdmi_set_acr(u32 freq, u8 *acr) { u32 n, cts; @@ -1421,6 +1604,7 @@ static void hdmi_v13_mode_set(struct hdmi_context *hdata, hdata->mode_conf.cea_video_id = drm_match_cea_mode((struct drm_display_mode *)m); hdata->mode_conf.pixel_clock = m->clock * 1000; + hdata->mode_conf.aspect_ratio = m->picture_aspect_ratio; hdmi_set_reg(core->h_blank, 2, m->htotal - m->hdisplay); hdmi_set_reg(core->h_v_line, 3, (m->htotal << 12) | m->vtotal); @@ -1517,6 +1701,7 @@ static void hdmi_v14_mode_set(struct hdmi_context *hdata, hdata->mode_conf.cea_video_id = drm_match_cea_mode((struct drm_display_mode *)m); hdata->mode_conf.pixel_clock = m->clock * 1000; + hdata->mode_conf.aspect_ratio = m->picture_aspect_ratio; hdmi_set_reg(core->h_blank, 2, m->htotal - m->hdisplay); hdmi_set_reg(core->v_line, 2, m->vtotal); @@ -1618,9 +1803,10 @@ static void hdmi_v14_mode_set(struct hdmi_context *hdata, hdmi_set_reg(tg->tg_3d, 1, 0x0); } -static void hdmi_mode_set(void *ctx, struct drm_display_mode *mode) +static void hdmi_mode_set(struct exynos_drm_display *display, + struct drm_display_mode *mode) { - struct hdmi_context *hdata = ctx; + struct hdmi_context *hdata = display->ctx; struct drm_display_mode *m = mode; DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%s\n", @@ -1634,16 +1820,9 @@ static void hdmi_mode_set(void *ctx, struct drm_display_mode *mode) hdmi_v14_mode_set(hdata, mode); } -static void hdmi_get_max_resol(void *ctx, unsigned int *width, - unsigned int *height) -{ - *width = MAX_WIDTH; - *height = MAX_HEIGHT; -} - -static void hdmi_commit(void *ctx) +static void hdmi_commit(struct exynos_drm_display *display) { - struct hdmi_context *hdata = ctx; + struct hdmi_context *hdata = display->ctx; mutex_lock(&hdata->hdmi_mutex); if (!hdata->powered) { @@ -1655,8 +1834,9 @@ static void hdmi_commit(void *ctx) hdmi_conf_apply(hdata); } -static void hdmi_poweron(struct hdmi_context *hdata) +static void hdmi_poweron(struct exynos_drm_display *display) { + struct hdmi_context *hdata = display->ctx; struct hdmi_resources *res = &hdata->res; mutex_lock(&hdata->hdmi_mutex); @@ -1669,6 +1849,8 @@ static void hdmi_poweron(struct hdmi_context *hdata) mutex_unlock(&hdata->hdmi_mutex); + pm_runtime_get_sync(hdata->dev); + if (regulator_bulk_enable(res->regul_count, res->regul_bulk)) DRM_DEBUG_KMS("failed to enable regulator bulk\n"); @@ -1677,10 +1859,12 @@ static void hdmi_poweron(struct hdmi_context *hdata) clk_prepare_enable(res->sclk_hdmi); hdmiphy_poweron(hdata); + hdmi_commit(display); } -static void hdmi_poweroff(struct hdmi_context *hdata) +static void hdmi_poweroff(struct exynos_drm_display *display) { + struct hdmi_context *hdata = display->ctx; struct hdmi_resources *res = &hdata->res; mutex_lock(&hdata->hdmi_mutex); @@ -1700,30 +1884,27 @@ static void hdmi_poweroff(struct hdmi_context *hdata) clk_disable_unprepare(res->hdmiphy); regulator_bulk_disable(res->regul_count, res->regul_bulk); - mutex_lock(&hdata->hdmi_mutex); + pm_runtime_put_sync(hdata->dev); + mutex_lock(&hdata->hdmi_mutex); hdata->powered = false; out: mutex_unlock(&hdata->hdmi_mutex); } -static void hdmi_dpms(void *ctx, int mode) +static void hdmi_dpms(struct exynos_drm_display *display, int mode) { - struct hdmi_context *hdata = ctx; - DRM_DEBUG_KMS("mode %d\n", mode); switch (mode) { case DRM_MODE_DPMS_ON: - if (pm_runtime_suspended(hdata->dev)) - pm_runtime_get_sync(hdata->dev); + hdmi_poweron(display); break; case DRM_MODE_DPMS_STANDBY: case DRM_MODE_DPMS_SUSPEND: case DRM_MODE_DPMS_OFF: - if (!pm_runtime_suspended(hdata->dev)) - pm_runtime_put_sync(hdata->dev); + hdmi_poweroff(display); break; default: DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode); @@ -1731,30 +1912,30 @@ static void hdmi_dpms(void *ctx, int mode) } } -static struct exynos_hdmi_ops hdmi_ops = { - /* display */ - .is_connected = hdmi_is_connected, - .get_edid = hdmi_get_edid, - .check_mode = hdmi_check_mode, - - /* manager */ +static struct exynos_drm_display_ops hdmi_display_ops = { + .initialize = hdmi_initialize, + .create_connector = hdmi_create_connector, + .mode_fixup = hdmi_mode_fixup, .mode_set = hdmi_mode_set, - .get_max_resol = hdmi_get_max_resol, - .commit = hdmi_commit, .dpms = hdmi_dpms, + .commit = hdmi_commit, +}; + +static struct exynos_drm_display hdmi_display = { + .type = EXYNOS_DISPLAY_TYPE_HDMI, + .ops = &hdmi_display_ops, }; static irqreturn_t hdmi_irq_thread(int irq, void *arg) { - struct exynos_drm_hdmi_context *ctx = arg; - struct hdmi_context *hdata = ctx->ctx; + struct hdmi_context *hdata = arg; mutex_lock(&hdata->hdmi_mutex); hdata->hpd = gpio_get_value(hdata->hpd_gpio); mutex_unlock(&hdata->hdmi_mutex); - if (ctx->drm_dev) - drm_helper_hpd_irq_event(ctx->drm_dev); + if (hdata->drm_dev) + drm_helper_hpd_irq_event(hdata->drm_dev); return IRQ_HANDLED; } @@ -1830,20 +2011,6 @@ fail: return -ENODEV; } -static struct i2c_client *hdmi_ddc, *hdmi_hdmiphy; - -void hdmi_attach_ddc_client(struct i2c_client *ddc) -{ - if (ddc) - hdmi_ddc = ddc; -} - -void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy) -{ - if (hdmiphy) - hdmi_hdmiphy = hdmiphy; -} - static struct s5p_hdmi_platform_data *drm_hdmi_dt_parse_pdata (struct device *dev) { @@ -1871,10 +2038,10 @@ err_data: static struct of_device_id hdmi_match_types[] = { { .compatible = "samsung,exynos5-hdmi", - .data = (void *)HDMI_TYPE14, + .data = &exynos5_hdmi_driver_data, }, { .compatible = "samsung,exynos4212-hdmi", - .data = (void *)HDMI_TYPE14, + .data = &exynos4212_hdmi_driver_data, }, { /* end node */ } @@ -1883,11 +2050,12 @@ static struct of_device_id hdmi_match_types[] = { static int hdmi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct exynos_drm_hdmi_context *drm_hdmi_ctx; struct hdmi_context *hdata; struct s5p_hdmi_platform_data *pdata; struct resource *res; const struct of_device_id *match; + struct device_node *ddc_node, *phy_node; + struct hdmi_driver_data *drv_data; int ret; if (!dev->of_node) @@ -1897,25 +2065,20 @@ static int hdmi_probe(struct platform_device *pdev) if (!pdata) return -EINVAL; - drm_hdmi_ctx = devm_kzalloc(dev, sizeof(*drm_hdmi_ctx), GFP_KERNEL); - if (!drm_hdmi_ctx) - return -ENOMEM; - hdata = devm_kzalloc(dev, sizeof(struct hdmi_context), GFP_KERNEL); if (!hdata) return -ENOMEM; mutex_init(&hdata->hdmi_mutex); - drm_hdmi_ctx->ctx = (void *)hdata; - hdata->parent_ctx = (void *)drm_hdmi_ctx; - - platform_set_drvdata(pdev, drm_hdmi_ctx); + platform_set_drvdata(pdev, &hdmi_display); match = of_match_node(hdmi_match_types, dev->of_node); if (!match) return -ENODEV; - hdata->type = (enum hdmi_type)match->data; + + drv_data = (struct hdmi_driver_data *)match->data; + hdata->type = drv_data->type; hdata->hpd_gpio = pdata->hpd_gpio; hdata->dev = dev; @@ -1938,21 +2101,34 @@ static int hdmi_probe(struct platform_device *pdev) } /* DDC i2c driver */ - if (i2c_add_driver(&ddc_driver)) { - DRM_ERROR("failed to register ddc i2c driver\n"); - return -ENOENT; + ddc_node = of_parse_phandle(dev->of_node, "ddc", 0); + if (!ddc_node) { + DRM_ERROR("Failed to find ddc node in device tree\n"); + return -ENODEV; + } + hdata->ddc_adpt = of_find_i2c_adapter_by_node(ddc_node); + if (!hdata->ddc_adpt) { + DRM_ERROR("Failed to get ddc i2c adapter by node\n"); + return -ENODEV; } - hdata->ddc_port = hdmi_ddc; + /* Not support APB PHY yet. */ + if (drv_data->is_apb_phy) + return -EPERM; /* hdmiphy i2c driver */ - if (i2c_add_driver(&hdmiphy_driver)) { - DRM_ERROR("failed to register hdmiphy i2c driver\n"); - ret = -ENOENT; + phy_node = of_parse_phandle(dev->of_node, "phy", 0); + if (!phy_node) { + DRM_ERROR("Failed to find hdmiphy node in device tree\n"); + ret = -ENODEV; + goto err_ddc; + } + hdata->hdmiphy_port = of_find_i2c_device_by_node(phy_node); + if (!hdata->hdmiphy_port) { + DRM_ERROR("Failed to get hdmi phy i2c client from node\n"); + ret = -ENODEV; goto err_ddc; } - - hdata->hdmiphy_port = hdmi_hdmiphy; hdata->irq = gpio_to_irq(hdata->hpd_gpio); if (hdata->irq < 0) { @@ -1966,119 +2142,45 @@ static int hdmi_probe(struct platform_device *pdev) ret = devm_request_threaded_irq(dev, hdata->irq, NULL, hdmi_irq_thread, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - "hdmi", drm_hdmi_ctx); + "hdmi", hdata); if (ret) { DRM_ERROR("failed to register hdmi interrupt\n"); goto err_hdmiphy; } - /* Attach HDMI Driver to common hdmi. */ - exynos_hdmi_drv_attach(drm_hdmi_ctx); - - /* register specific callbacks to common hdmi. */ - exynos_hdmi_ops_register(&hdmi_ops); - pm_runtime_enable(dev); + hdmi_display.ctx = hdata; + exynos_drm_display_register(&hdmi_display); + return 0; err_hdmiphy: - i2c_del_driver(&hdmiphy_driver); + put_device(&hdata->hdmiphy_port->dev); err_ddc: - i2c_del_driver(&ddc_driver); + put_device(&hdata->ddc_adpt->dev); return ret; } static int hdmi_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct exynos_drm_display *display = get_hdmi_display(dev); + struct hdmi_context *hdata = display->ctx; - pm_runtime_disable(dev); - - /* hdmiphy i2c driver */ - i2c_del_driver(&hdmiphy_driver); - /* DDC i2c driver */ - i2c_del_driver(&ddc_driver); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int hdmi_suspend(struct device *dev) -{ - struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); - struct hdmi_context *hdata = ctx->ctx; - - disable_irq(hdata->irq); - - hdata->hpd = false; - if (ctx->drm_dev) - drm_helper_hpd_irq_event(ctx->drm_dev); - - if (pm_runtime_suspended(dev)) { - DRM_DEBUG_KMS("Already suspended\n"); - return 0; - } - - hdmi_poweroff(hdata); + put_device(&hdata->hdmiphy_port->dev); + put_device(&hdata->ddc_adpt->dev); + pm_runtime_disable(&pdev->dev); return 0; } -static int hdmi_resume(struct device *dev) -{ - struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); - struct hdmi_context *hdata = ctx->ctx; - - hdata->hpd = gpio_get_value(hdata->hpd_gpio); - - enable_irq(hdata->irq); - - if (!pm_runtime_suspended(dev)) { - DRM_DEBUG_KMS("Already resumed\n"); - return 0; - } - - hdmi_poweron(hdata); - - return 0; -} -#endif - -#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; - - 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; - - 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, .remove = hdmi_remove, .driver = { .name = "exynos-hdmi", .owner = THIS_MODULE, - .pm = &hdmi_pm_ops, .of_match_table = hdmi_match_types, }, }; |