This patch implements exynos_hdmi_cdf.c which is a glue component between exynos DRM and hdmi cdf panel. It is a platform driver register through exynos_drm_drv.c. Exynos_hdmi.c is modified to register hdmi as display panel. exynos_hdmi_cdf.c registers for exynos hdmi display entity and if successful, proceeds for mode setting. Signed-off-by: Rahul Sharma <rahul.sharma@xxxxxxxxxxx> --- drivers/gpu/drm/exynos/Kconfig | 6 + drivers/gpu/drm/exynos/Makefile | 1 + drivers/gpu/drm/exynos/exynos_drm_drv.c | 24 ++ drivers/gpu/drm/exynos/exynos_drm_drv.h | 1 + drivers/gpu/drm/exynos/exynos_hdmi.c | 445 ++++++++++++++++--------------- drivers/gpu/drm/exynos/exynos_hdmi_cdf.c | 370 +++++++++++++++++++++++++ include/video/exynos_hdmi.h | 25 ++ 7 files changed, 658 insertions(+), 214 deletions(-) create mode 100644 drivers/gpu/drm/exynos/exynos_hdmi_cdf.c create mode 100644 include/video/exynos_hdmi.h diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 1d1f1e5..309e62a 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -34,6 +34,12 @@ config DRM_EXYNOS_HDMI help Choose this option if you want to use Exynos HDMI for DRM. +config DRM_EXYNOS_HDMI_CDF + bool "Exynos DRM HDMI using CDF" + depends on DRM_EXYNOS_HDMI && DRM_EXYNOS && !VIDEO_SAMSUNG_S5P_TV + help + Choose this option if you want to use Exynos HDMI for DRM using CDF. + config DRM_EXYNOS_VIDI bool "Exynos DRM Virtual Display" depends on DRM_EXYNOS diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile index 639b49e..e946ed6 100644 --- a/drivers/gpu/drm/exynos/Makefile +++ b/drivers/gpu/drm/exynos/Makefile @@ -20,5 +20,6 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_IPP) += exynos_drm_ipp.o exynosdrm-$(CONFIG_DRM_EXYNOS_FIMC) += exynos_drm_fimc.o exynosdrm-$(CONFIG_DRM_EXYNOS_ROTATOR) += exynos_drm_rotator.o exynosdrm-$(CONFIG_DRM_EXYNOS_GSC) += exynos_drm_gsc.o +exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI_CDF) += exynos_hdmi_cdf.o obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 3da5c2d..7876c3c 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -40,6 +40,9 @@ /* platform device pointer for eynos drm device. */ static struct platform_device *exynos_drm_pdev; +/* platform device pointer for eynos hdmi cdf device. */ +static struct platform_device *exynos_hdmi_cdf_pdev; + static int exynos_drm_load(struct drm_device *dev, unsigned long flags) { struct exynos_drm_private *private; @@ -331,6 +334,18 @@ static int __init exynos_drm_init(void) #endif #ifdef CONFIG_DRM_EXYNOS_HDMI + + ret = platform_driver_register(&hdmi_cdf_driver); + if (ret < 0) + goto out_hdmi_cdf_driver; + + exynos_hdmi_cdf_pdev = platform_device_register_simple( + "exynos-hdmi-cdf", -1, NULL, 0); + if (IS_ERR_OR_NULL(exynos_hdmi_cdf_pdev)) { + ret = PTR_ERR(exynos_hdmi_cdf_pdev); + goto out_hdmi_cdf_device; + } + ret = platform_driver_register(&hdmi_driver); if (ret < 0) goto out_hdmi; @@ -438,6 +453,13 @@ out_common_hdmi: out_mixer: platform_driver_unregister(&hdmi_driver); out_hdmi: + +out_hdmi_cdf_device: + platform_device_unregister(exynos_hdmi_cdf_pdev); + +out_hdmi_cdf_driver: + platform_driver_unregister(&hdmi_cdf_driver); + #endif #ifdef CONFIG_DRM_EXYNOS_FIMD @@ -480,6 +502,8 @@ static void __exit exynos_drm_exit(void) platform_driver_unregister(&exynos_drm_common_hdmi_driver); platform_driver_unregister(&mixer_driver); platform_driver_unregister(&hdmi_driver); + platform_driver_unregister(&hdmi_cdf_driver); + platform_device_unregister(exynos_hdmi_cdf_pdev); #endif #ifdef CONFIG_DRM_EXYNOS_VIDI diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index b9e51bc..961fe14 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -332,6 +332,7 @@ void exynos_platform_device_hdmi_unregister(void); extern struct platform_driver fimd_driver; extern struct platform_driver hdmi_driver; extern struct platform_driver mixer_driver; +extern struct platform_driver hdmi_cdf_driver; extern struct platform_driver exynos_drm_common_hdmi_driver; extern struct platform_driver vidi_driver; extern struct platform_driver g2d_driver; diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 6f844b1..548cd32 100755 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -34,13 +34,12 @@ #include <linux/regulator/consumer.h> #include <linux/io.h> #include <linux/of_gpio.h> +#include <video/display.h> +#include "video/exynos_hdmi.h" #include <plat/gpio-cfg.h> #include <drm/exynos_drm.h> -#include "exynos_drm_drv.h" -#include "exynos_drm_hdmi.h" - #include "exynos_hdmi.h" #include <linux/gpio.h> @@ -157,14 +156,12 @@ struct hdmi_v14_conf { struct hdmi_context { struct device *dev; - struct drm_device *drm_dev; bool hpd; bool powered; bool dvi_mode; struct mutex hdmi_mutex; void __iomem *regs; - void *parent_ctx; int external_irq; int internal_irq; @@ -180,6 +177,7 @@ struct hdmi_context { int hpd_gpio; enum hdmi_type type; + struct display_entity entity; }; /* HDMI Version 1.3 */ @@ -973,39 +971,8 @@ static void hdmi_reg_infoframe(struct hdmi_context *hdata, } } -static bool hdmi_is_connected(void *ctx) -{ - struct hdmi_context *hdata = ctx; - - return hdata->hpd; -} - -static int hdmi_get_edid(void *ctx, struct drm_connector *connector, - u8 *edid, int len) -{ - struct edid *raw_edid; - struct hdmi_context *hdata = ctx; - - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - if (!hdata->ddc_port) - return -ENODEV; - - raw_edid = drm_get_edid(connector, hdata->ddc_port->adapter); - if (raw_edid) { - hdata->dvi_mode = !drm_detect_hdmi_monitor(raw_edid); - memcpy(edid, raw_edid, min((1 + raw_edid->extensions) - * EDID_LENGTH, len)); - 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; - } - - return 0; -} +extern int generic_drm_get_edid(struct i2c_adapter *adapter, + struct display_entity_edid *edid); static int hdmi_v13_check_timing(struct fb_videomode *check_timing) { @@ -1061,22 +1028,6 @@ static int hdmi_v14_check_timing(struct fb_videomode *check_timing) return -EINVAL; } -static int hdmi_check_timing(void *ctx, struct fb_videomode *timing) -{ - struct hdmi_context *hdata = ctx; - - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - DRM_DEBUG_KMS("[%d]x[%d] [%d]Hz [%x]\n", timing->xres, - timing->yres, timing->refresh, - timing->vmode); - - if (hdata->type == HDMI_TYPE13) - return hdmi_v13_check_timing(timing); - else - return hdmi_v14_check_timing(timing); -} - static void hdmi_set_acr(u32 freq, u8 *acr) { u32 n, cts; @@ -1143,15 +1094,22 @@ static void hdmi_reg_acr(struct hdmi_context *hdata, u8 *acr) hdmi_reg_writeb(hdata, HDMI_ACR_CON, 4); } -static void hdmi_audio_init(struct hdmi_context *hdata) +static void hdmi_spdif_audio_init(struct hdmi_context *hdata, + const struct display_entity_audio_params *params) +{ + DRM_ERROR("SPDIF AUDIO NOT IMPLEMENTED YET"); +} + +static void hdmi_i2s_audio_init(struct hdmi_context *hdata, + const struct display_entity_audio_params *params) { u32 sample_rate, bits_per_sample, frame_size_code; u32 data_num, bit_ch, sample_frq; u32 val; u8 acr[7]; - sample_rate = 44100; - bits_per_sample = 16; + sample_rate = params->sf; + bits_per_sample = params->bits_per_sample; frame_size_code = 0; switch (bits_per_sample) { @@ -1685,8 +1643,6 @@ static void hdmi_conf_apply(struct hdmi_context *hdata) hdmi_conf_init(hdata); mutex_unlock(&hdata->hdmi_mutex); - hdmi_audio_init(hdata); - /* setting core registers */ hdmi_timing_apply(hdata); hdmi_audio_control(hdata, true); @@ -1694,58 +1650,6 @@ static void hdmi_conf_apply(struct hdmi_context *hdata) hdmi_regs_dump(hdata, "start"); } -static void hdmi_mode_fixup(void *ctx, struct drm_connector *connector, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct drm_display_mode *m; - struct hdmi_context *hdata = ctx; - int index; - - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - drm_mode_set_crtcinfo(adjusted_mode, 0); - - if (hdata->type == HDMI_TYPE13) - index = hdmi_v13_conf_index(adjusted_mode); - else - index = hdmi_v14_find_phy_conf(adjusted_mode->clock * 1000); - - /* just return if user desired mode exists. */ - if (index >= 0) - return; - - /* - * otherwise, find the most suitable mode among modes and change it - * to adjusted_mode. - */ - list_for_each_entry(m, &connector->modes, head) { - if (hdata->type == HDMI_TYPE13) - index = hdmi_v13_conf_index(m); - else - index = hdmi_v14_find_phy_conf(m->clock * 1000); - - 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"); - - DRM_DEBUG_KMS("Adjusted Mode: [%d]x[%d] [%d]Hz\n", - m->hdisplay, m->vdisplay, m->vrefresh); - - /* 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; - } - } -} - static void hdmi_set_reg(u8 *reg_pair, int num_bytes, u32 value) { int i; @@ -1862,42 +1766,6 @@ static void hdmi_v14_mode_set(struct hdmi_context *hdata, } -static void hdmi_mode_set(void *ctx, void *mode) -{ - struct hdmi_context *hdata = ctx; - int conf_idx; - - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - if (hdata->type == HDMI_TYPE13) { - conf_idx = hdmi_v13_conf_index(mode); - if (conf_idx >= 0) - hdata->cur_conf = conf_idx; - else - DRM_DEBUG_KMS("not supported mode\n"); - } else { - hdmi_v14_mode_set(hdata, mode); - } -} - -static void hdmi_get_max_resol(void *ctx, unsigned int *width, - unsigned int *height) -{ - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - *width = MAX_WIDTH; - *height = MAX_HEIGHT; -} - -static void hdmi_commit(void *ctx) -{ - struct hdmi_context *hdata = ctx; - - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - hdmi_conf_apply(hdata); -} - static void hdmi_poweron(struct hdmi_context *hdata) { struct hdmi_resources *res = &hdata->res; @@ -1953,62 +1821,215 @@ out: mutex_unlock(&hdata->hdmi_mutex); } -static void hdmi_dpms(void *ctx, int mode) +int hdmi_get_size(struct display_entity *ent, + unsigned int *width, unsigned int *height) { - struct hdmi_context *hdata = ctx; + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - DRM_DEBUG_KMS("[%d] %s mode %d\n", __LINE__, __func__, mode); + *width = MAX_WIDTH; + *height = MAX_HEIGHT; - switch (mode) { - case DRM_MODE_DPMS_ON: - 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: + return 0; +} + +void hdmi_send_hpdevent(struct display_entity *entity, int hpd) +{ + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + display_entity_notify_event_subscriber(entity, + DISPLAY_ENTITY_HDMI_HOTPLUG, hpd); +} + +int hdmi_get_hpdstate(struct display_entity *entity, unsigned int *hpd_state) +{ + struct hdmi_context *hdata = + container_of(entity, struct hdmi_context, entity); + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + if (hpd_state) { + *hpd_state = hdata->hpd; + return 0; + } + return -1; +} + +int hdmi_get_edid(struct display_entity *entity, + struct display_entity_edid *edid) +{ + struct hdmi_context *hdata = + container_of(entity, struct hdmi_context, entity); + struct edid *raw_edid; + int ret; + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + if (!hdata->ddc_port) + return -ENODEV; + + ret = generic_drm_get_edid(hdata->ddc_port->adapter, edid); + if (ret) { + DRM_ERROR("[%d]%s, Edid Read Fail!!! ret = %d\n", + __LINE__, __func__, ret); + return -EINVAL; + } + + raw_edid = (struct edid *)edid->edid; + + if (raw_edid) + hdata->dvi_mode = !drm_detect_hdmi_monitor(raw_edid); + else + return -ENODEV; + + return 0; +} + +int hdmi_check_mode(struct display_entity *entity, + const struct videomode *mode) +{ + struct hdmi_context *hdata = + container_of(entity, struct hdmi_context, entity); + struct fb_videomode *timing = (struct fb_videomode *)mode; + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + DRM_DEBUG_KMS("[%d]x[%d] [%d]Hz [%x]\n", timing->xres, + timing->yres, timing->refresh, + timing->vmode); + + if (hdata->type == HDMI_TYPE13) + return hdmi_v13_check_timing(timing); + else + return hdmi_v14_check_timing(timing); +} + +int hdmi_set_mode(struct display_entity *entity, + const struct videomode *mode) +{ + struct hdmi_context *hdata = + container_of(entity, struct hdmi_context, entity); + struct drm_display_mode *m = (struct drm_display_mode *)mode; + int conf_idx; + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + if (hdata->type == HDMI_TYPE13) { + conf_idx = hdmi_v13_conf_index(m); + if (conf_idx >= 0) + hdata->cur_conf = conf_idx; + else + DRM_DEBUG_KMS("not supported mode\n"); + } else { + hdmi_v14_mode_set(hdata, m); + } + + return 0; +} + +int hdmi_update(struct display_entity *entity) +{ + struct hdmi_context *hdata = + container_of(entity, struct hdmi_context, entity); + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + hdmi_conf_apply(hdata); + return 0; +} + +int hdmi_set_state(struct display_entity *entity, + enum display_entity_state state) +{ + struct hdmi_context *hdata = + container_of(entity, struct hdmi_context, entity); + + DRM_DEBUG_KMS("[%d] %s %d\n", __LINE__, __func__, state); + + switch (state) { + case DISPLAY_ENTITY_STATE_OFF: + case DISPLAY_ENTITY_STATE_STANDBY: if (!pm_runtime_suspended(hdata->dev)) pm_runtime_put_sync(hdata->dev); break; - default: - DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode); + + case DISPLAY_ENTITY_STATE_ON: + if (pm_runtime_suspended(hdata->dev)) + pm_runtime_get_sync(hdata->dev); break; + default: + return -EINVAL; } + return 0; +} + +int hdmi_init_audio(struct display_entity *entity, + const struct display_entity_audio_params *params) +{ + struct hdmi_context *hdata = + container_of(entity, struct hdmi_context, entity); + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + if (params->type == DISPLAY_ENTITY_AUDIO_I2S) + hdmi_i2s_audio_init(hdata, params); + else if (params->type == DISPLAY_ENTITY_AUDIO_SPDIF) + hdmi_spdif_audio_init(hdata, params); + else + return -EINVAL; + + return 0; +} + + +int hdmi_set_audiostate(struct display_entity *entity, + enum display_entity_audiostate state) +{ + struct hdmi_context *hdata = + container_of(entity, struct hdmi_context, entity); + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + if (state == DISPLAY_ENTITY_AUDIOSTATE_ON) + hdmi_audio_control(hdata, true); + else + hdmi_audio_control(hdata, false); + + return 0; } -static struct exynos_hdmi_ops hdmi_ops = { - /* display */ - .is_connected = hdmi_is_connected, - .get_edid = hdmi_get_edid, - .check_timing = hdmi_check_timing, - - /* manager */ - .mode_fixup = hdmi_mode_fixup, - .mode_set = hdmi_mode_set, - .get_max_resol = hdmi_get_max_resol, - .commit = hdmi_commit, - .dpms = hdmi_dpms, +struct display_entity_control_ops entity_ctrl_ops = { + .get_size = hdmi_get_size, + .update = hdmi_update, + .set_state = hdmi_set_state, + .set_mode = hdmi_set_mode, +}; + +struct display_entity_hdmi_control_ops hdmi_ctrl_ops = { + .get_edid = hdmi_get_edid, + .check_mode = hdmi_check_mode, + .init_audio = hdmi_init_audio, + .set_audiostate = hdmi_set_audiostate, +}; + +struct exynos_hdmi_control_ops exynos_hdmi_ctrl_ops = { + .get_hpdstate = hdmi_get_hpdstate, }; static irqreturn_t hdmi_external_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); + hdmi_send_hpdevent(&hdata->entity, hdata->hpd); return IRQ_HANDLED; } static irqreturn_t hdmi_internal_irq_thread(int irq, void *arg) { - struct exynos_drm_hdmi_context *ctx = arg; - struct hdmi_context *hdata = ctx->ctx; + struct hdmi_context *hdata = arg; u32 intc_flag; intc_flag = hdmi_reg_read(hdata, HDMI_INTC_FLAG); @@ -2017,16 +2038,17 @@ static irqreturn_t hdmi_internal_irq_thread(int irq, void *arg) DRM_DEBUG_KMS("unplugged\n"); hdmi_reg_writemask(hdata, HDMI_INTC_FLAG, ~0, HDMI_INTC_FLAG_HPD_UNPLUG); + hdata->hpd = 0; + hdmi_send_hpdevent(&hdata->entity, hdata->hpd); } if (intc_flag & HDMI_INTC_FLAG_HPD_PLUG) { DRM_DEBUG_KMS("plugged\n"); hdmi_reg_writemask(hdata, HDMI_INTC_FLAG, ~0, HDMI_INTC_FLAG_HPD_PLUG); + hdata->hpd = 1; + hdmi_send_hpdevent(&hdata->entity, hdata->hpd); } - if (ctx->drm_dev) - drm_helper_hpd_irq_event(ctx->drm_dev); - return IRQ_HANDLED; } @@ -2176,16 +2198,20 @@ static struct of_device_id hdmi_match_types[] = { }; #endif +static void hdmi_release(struct display_entity *entity) +{ + DRM_DEBUG_KMS("[%d][%s]\n", __LINE__, __func__); +} + 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; int ret; - DRM_DEBUG_KMS("[%d]\n", __LINE__); + DRM_DEBUG_KMS("[%d][%s]\n", __LINE__, __func__); if (pdev->dev.of_node) { pdata = drm_hdmi_dt_parse_pdata(dev); @@ -2202,13 +2228,6 @@ static int hdmi_probe(struct platform_device *pdev) return -EINVAL; } - drm_hdmi_ctx = devm_kzalloc(&pdev->dev, sizeof(*drm_hdmi_ctx), - GFP_KERNEL); - if (!drm_hdmi_ctx) { - DRM_ERROR("failed to allocate common hdmi context.\n"); - return -ENOMEM; - } - hdata = devm_kzalloc(&pdev->dev, sizeof(struct hdmi_context), GFP_KERNEL); if (!hdata) { @@ -2218,10 +2237,7 @@ static int hdmi_probe(struct platform_device *pdev) 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, hdata); if (dev->of_node) { const struct of_device_id *match; @@ -2299,7 +2315,7 @@ static int hdmi_probe(struct platform_device *pdev) ret = request_threaded_irq(hdata->external_irq, NULL, hdmi_external_irq_thread, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - "hdmi_external", drm_hdmi_ctx); + "hdmi_external", hdata); if (ret) { DRM_ERROR("failed to register hdmi external interrupt\n"); goto err_hdmiphy; @@ -2307,24 +2323,31 @@ static int hdmi_probe(struct platform_device *pdev) ret = request_threaded_irq(hdata->internal_irq, NULL, hdmi_internal_irq_thread, IRQF_ONESHOT, - "hdmi_internal", drm_hdmi_ctx); + "hdmi_internal", hdata); if (ret) { DRM_ERROR("failed to register hdmi internal interrupt\n"); goto err_free_irq; } - /* Attach HDMI Driver to common hdmi. */ - exynos_hdmi_drv_attach(drm_hdmi_ctx); + pm_runtime_enable(dev); - /* register specific callbacks to common hdmi. */ - exynos_hdmi_ops_register(&hdmi_ops); + hdata->entity.dev = &pdev->dev; + hdata->entity.release = hdmi_release; + hdata->entity.ops.ctrl = &entity_ctrl_ops; + hdata->entity.opt_ctrl.hdmi = &hdmi_ctrl_ops; - pm_runtime_enable(dev); + hdata->entity.private = &exynos_hdmi_ctrl_ops; + + ret = display_entity_register(&hdata->entity); + if (ret < 0) { + DRM_ERROR("[%d][%s]\n", __LINE__, __func__); + return ret; + } return 0; err_free_irq: - free_irq(hdata->external_irq, drm_hdmi_ctx); + free_irq(hdata->external_irq, hdata); err_hdmiphy: i2c_del_driver(&hdmiphy_driver); err_ddc: @@ -2335,8 +2358,7 @@ err_ddc: static int hdmi_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct exynos_drm_hdmi_context *ctx = platform_get_drvdata(pdev); - struct hdmi_context *hdata = ctx->ctx; + struct hdmi_context *hdata = platform_get_drvdata(pdev); DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); @@ -2357,8 +2379,7 @@ static int hdmi_remove(struct platform_device *pdev) #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; + struct hdmi_context *hdata = get_hdmi_context(dev); DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); @@ -2366,8 +2387,7 @@ static int hdmi_suspend(struct device *dev) disable_irq(hdata->external_irq); hdata->hpd = false; - if (ctx->drm_dev) - drm_helper_hpd_irq_event(ctx->drm_dev); + hdmi_send_hpdevent(&hdata->entity, hdata->hpd); if (pm_runtime_suspended(dev)) { DRM_DEBUG_KMS("%s : Already suspended\n", __func__); @@ -2381,8 +2401,7 @@ static int hdmi_suspend(struct device *dev) static int hdmi_resume(struct device *dev) { - struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); - struct hdmi_context *hdata = ctx->ctx; + struct hdmi_context *hdata = get_hdmi_context(dev); DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); @@ -2405,8 +2424,7 @@ static int hdmi_resume(struct device *dev) #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; + struct hdmi_context *hdata = get_hdmi_context(dev); DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); hdmi_poweroff(hdata); @@ -2416,8 +2434,7 @@ static int hdmi_runtime_suspend(struct device *dev) static int hdmi_runtime_resume(struct device *dev) { - struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); - struct hdmi_context *hdata = ctx->ctx; + struct hdmi_context *hdata = get_hdmi_context(dev); DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); hdmi_poweron(hdata); diff --git a/drivers/gpu/drm/exynos/exynos_hdmi_cdf.c b/drivers/gpu/drm/exynos/exynos_hdmi_cdf.c new file mode 100644 index 0000000..e1c862f --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_hdmi_cdf.c @@ -0,0 +1,370 @@ +/* + * Copyright (C) 2011 Samsung Electronics Co.Ltd + * Authors: + * Seung-Woo Kim <sw0312.kim@xxxxxxxxxxx> + * Inki Dae <inki.dae@xxxxxxxxxxx> + * Joonyoung Shim <jy0922.shim@xxxxxxxxxxx> + * + * Based on drivers/media/video/s5p-tv/hdmi_drv.c + * + * 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 <drm/drmP.h> +#include <drm/drm_edid.h> +#include <drm/drm_crtc_helper.h> + +#include "regs-hdmi.h" + +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <video/display.h> +#include "video/exynos_hdmi.h" + +#include <drm/exynos_drm.h> +#include "exynos_drm_drv.h" +#include "exynos_drm_hdmi.h" +#include <linux/of_platform.h> +#include <linux/platform_device.h> + +#include "exynos_hdmi.h" + +#define get_hdmi_context(dev) platform_get_drvdata(to_platform_device(dev)) + +struct hdmi_cdf_context { + struct device *dev; + struct drm_device *drm_dev; + unsigned int hpd; + struct display_entity *entity; + struct display_entity_notifier notf; + struct display_event_subscriber subscriber; + void *parent_ctx; +}; + +extern bool hdmi_cdf_is_connected(void *ctx) +{ + struct hdmi_cdf_context *hdata = (struct hdmi_cdf_context *)ctx; + + DRM_DEBUG_KMS("[%d] %s hpd %d\n", __LINE__, __func__, hdata->hpd); + return (bool)hdata->hpd; +} + +extern int hdmi_cdf_get_edid(void *ctx, struct drm_connector *connector, + u8 *edid, int len) +{ + struct hdmi_cdf_context *hdata = (struct hdmi_cdf_context *)ctx; + struct display_entity_edid edid_st; + struct edid *raw_edid; + int ret; + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + ret = display_entity_hdmi_get_edid(hdata->entity, &edid_st); + if (ret) { + DRM_ERROR("[%d]%s, Edid Read Fail!!! ret = %d\n", + __LINE__, __func__, ret); + return -EINVAL; + } + + raw_edid = (struct edid *)edid_st.edid; + + if (raw_edid) { + memcpy(edid, raw_edid, min(edid_st.len, len)); + kfree(raw_edid); + } else { + return -ENODEV; + } + + return 0; +} + +extern int hdmi_cdf_check_timing(void *ctx, struct fb_videomode *timing) +{ + struct hdmi_cdf_context *hdata = (struct hdmi_cdf_context *)ctx; + int ret; + + ret = display_entity_hdmi_check_mode(hdata->entity, + (struct videomode *)timing); + if (ret) { + DRM_DEBUG_KMS("[%d]%s, Mode NOT Supported! %dx%d@%d%s\n", + __LINE__, __func__, timing->xres, timing->yres, + timing->refresh, timing->flag + & FB_VMODE_INTERLACED ? "I" : "P"); + return -EINVAL; + } + + DRM_DEBUG_KMS("[%d]%s, Mode Supported! %dx%d@%d%s\n", __LINE__, + __func__, timing->xres, timing->yres, timing->refresh, + timing->flag & FB_VMODE_INTERLACED ? "I" : "P"); + + return 0; +} + +extern int hdmi_cdf_power_on(void *ctx, int mode) +{ + return 0; +} + +extern void hdmi_cdf_mode_fixup(void *ctx, struct drm_connector *connector, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct hdmi_cdf_context *hdata = (struct hdmi_cdf_context *)ctx; + struct drm_display_mode *m; + struct fb_videomode timing; + int index; + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + drm_mode_set_crtcinfo(adjusted_mode, 0); + + timing.xres = adjusted_mode->hdisplay; + timing.yres = adjusted_mode->vdisplay; + timing.refresh = adjusted_mode->vrefresh; + timing.pixclock = adjusted_mode->clock * 1000; + timing.flag = ((adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) ? + FB_VMODE_INTERLACED : 0); + + index = display_entity_hdmi_check_mode(hdata->entity, + (struct videomode *)&timing); + + /* just return if user desired mode exists. */ + if (index == 0) + return; + + /* + * otherwise, find the most suitable mode among modes and change it + * to adjusted_mode. + */ + list_for_each_entry(m, &connector->modes, head) { + + timing.xres = m->hdisplay; + timing.yres = m->vdisplay; + timing.refresh = m->vrefresh; + timing.pixclock = m->clock * 1000; + timing.flag = ((m->flags & DRM_MODE_FLAG_INTERLACE) ? + FB_VMODE_INTERLACED : 0); + + index = display_entity_hdmi_check_mode(hdata->entity, + (struct videomode *)&timing); + + if (index == 0) { + struct drm_mode_object base; + struct list_head head; + + DRM_INFO("desired mode doesn't exist so\n"); + DRM_INFO("use most suitable mode.\n"); + + DRM_DEBUG_KMS("Adjusted Mode: [%d]x[%d][%d]Hz\n", + m->hdisplay, m->vdisplay, m->vrefresh); + + /* 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; + } + } +} + +extern void hdmi_cdf_mode_set(void *ctx, void *mode) +{ + struct hdmi_cdf_context *hdata = (struct hdmi_cdf_context *)ctx; + struct drm_display_mode *m = mode; + int ret; + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + ret = display_entity_set_mode(hdata->entity, (struct videomode *)m); + if (ret) { + DRM_DEBUG_KMS("[%d]%s, Mode NOT Set! %dx%d@%d%s\n", + __LINE__, __func__, m->hdisplay, m->vdisplay, + m->vrefresh, (m->flags & DRM_MODE_FLAG_INTERLACE) + ? "I" : "P"); + } +} + +extern void hdmi_cdf_get_max_resol(void *ctx, unsigned int *width, + unsigned int *height) +{ + struct hdmi_cdf_context *hdata = (struct hdmi_cdf_context *)ctx; + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + display_entity_get_size(hdata->entity, width, height); +} + +extern void hdmi_cdf_commit(void *ctx) +{ + struct hdmi_cdf_context *hdata = (struct hdmi_cdf_context *)ctx; + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + display_entity_update(hdata->entity); +} + +extern void hdmi_cdf_dpms(void *ctx, int mode) +{ + struct hdmi_cdf_context *hdata = (struct hdmi_cdf_context *)ctx; + enum display_entity_state state; + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + switch (mode) { + case DRM_MODE_DPMS_ON: + state = DISPLAY_ENTITY_STATE_ON; + break; + case DRM_MODE_DPMS_STANDBY: + state = DISPLAY_ENTITY_STATE_STANDBY; + break; + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + state = DISPLAY_ENTITY_STATE_STANDBY; + break; + default: + DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode); + return; + } + + display_entity_set_state(hdata->entity, state); +} + +void event_notify(struct display_entity *entity, + enum display_entity_event_type type, unsigned int value, + void *context) +{ + struct hdmi_cdf_context *hdata = (struct hdmi_cdf_context *)context; + struct exynos_drm_hdmi_context *ctx = get_hdmi_context(hdata->dev); + + if (type == DISPLAY_ENTITY_HDMI_HOTPLUG) { + DRM_DEBUG_KMS("[%d][%s] hpd(%d)\n", + __LINE__, __func__, value); + hdata->hpd = value; + + if (ctx->drm_dev) + drm_helper_hpd_irq_event(ctx->drm_dev); + } +} + +int display_entity_notification(struct display_entity_notifier *notf, + struct display_entity *entity, int status) +{ + struct hdmi_cdf_context *hdata = container_of(notf, + struct hdmi_cdf_context, notf); + struct exynos_hdmi_control_ops *exynos_ops = + (struct exynos_hdmi_control_ops *)entity->private; + + if (status != DISPLAY_ENTITY_NOTIFIER_CONNECT && entity) + return -EINVAL; + + DRM_DEBUG_KMS("[%d][%s] NOTIFIER_CONNECT\n", __LINE__, __func__); + + hdata->entity = entity; + + hdata->subscriber.context = hdata; + hdata->subscriber.notify = event_notify; + display_entity_subscribe_event(entity, &hdata->subscriber); + + exynos_ops->get_hpdstate(entity, &hdata->hpd); + return 0; +} + +static struct exynos_hdmi_ops hdmi_ops = { + /* display */ + .is_connected = hdmi_cdf_is_connected, + .get_edid = hdmi_cdf_get_edid, + .check_timing = hdmi_cdf_check_timing, + + /* manager */ + .mode_fixup = hdmi_cdf_mode_fixup, + .mode_set = hdmi_cdf_mode_set, + .get_max_resol = hdmi_cdf_get_max_resol, + .commit = hdmi_cdf_commit, + .dpms = hdmi_cdf_dpms, +}; + +static int hdmi_cdf_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *dev_node; + struct platform_device *disp_pdev; + struct exynos_drm_hdmi_context *drm_hdmi_ctx; + struct hdmi_cdf_context *hdata; + int ret; + + DRM_DEBUG_KMS("[%d][%s]\n", __LINE__, __func__); + + dev_node = of_find_compatible_node(NULL, NULL, + "samsung,exynos5-hdmi"); + if (!dev_node) { + DRM_DEBUG_KMS("[ERROR][%d][%s] dt node not found.\n", + __LINE__, __func__); + return -EINVAL; + } + + disp_pdev = of_find_device_by_node(dev_node); + if (!disp_pdev) { + DRM_DEBUG_KMS("[ERROR][%d][%s] No pdev\n", + __LINE__, __func__); + return -EINVAL; + } + + drm_hdmi_ctx = devm_kzalloc(&pdev->dev, sizeof(*drm_hdmi_ctx), + GFP_KERNEL); + if (!drm_hdmi_ctx) { + DRM_ERROR("failed to allocate common hdmi context.\n"); + return -ENOMEM; + } + + hdata = devm_kzalloc(&pdev->dev, sizeof(struct hdmi_cdf_context), + GFP_KERNEL); + if (!hdata) { + DRM_ERROR("out of memory\n"); + return -ENOMEM; + } + + drm_hdmi_ctx->ctx = (void *)hdata; + hdata->parent_ctx = (void *)drm_hdmi_ctx; + + platform_set_drvdata(pdev, drm_hdmi_ctx); + + /* 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); + + hdata->dev = dev; + hdata->notf.dev = &disp_pdev->dev; + hdata->notf.notify = display_entity_notification; + + ret = display_entity_register_notifier(&hdata->notf); + if (ret) { + DRM_DEBUG_KMS("[ERROR][%d][%s] entity registe failed.\n", + __LINE__, __func__); + return -EINVAL; + } + + return 0; +} + +static int hdmi_cdf_remove(struct platform_device *pdev) +{ + return 0; +} + +struct platform_driver hdmi_cdf_driver = { + .probe = hdmi_cdf_probe, + .remove = hdmi_cdf_remove, + .driver = { + .name = "exynos-hdmi-cdf", + .owner = THIS_MODULE, + }, +}; + diff --git a/include/video/exynos_hdmi.h b/include/video/exynos_hdmi.h new file mode 100644 index 0000000..cc8d613 --- /dev/null +++ b/include/video/exynos_hdmi.h @@ -0,0 +1,25 @@ +/* + * Display Core + * + * Copyright (C) 2012 Renesas Solutions Corp. + * + * Contacts: Laurent Pinchart <laurent.pinchart@xxxxxxxxxxxxxxxx> + * + * 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. + */ + +#ifndef __EXYNOS_HDMI_H__ +#define __EXYNOS_HDMI_H__ + +#include <linux/kref.h> +#include <linux/list.h> +#include <linux/module.h> + +struct exynos_hdmi_control_ops { + int (*get_hpdstate)(struct display_entity *entity, + unsigned int *hpd_state); +}; + +#endif /* __EXYNOS_HDMI_H__ */ -- 1.8.0 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/dri-devel