#define get_hdmi_context(dev) platform_get_drvdata(to_platform_device(dev))
+#define HOTPLUG_DEBOUNCE_MS 1100
+
struct hdmi_resources {
struct clk *hdmi;
struct clk *sclk_hdmi;
unsigned int default_win;
unsigned int default_bpp;
bool hpd_handle;
- bool enabled;
bool has_hdmi_sink;
bool has_hdmi_audio;
bool is_soc_exynos5;
unsigned int external_irq;
unsigned int internal_irq;
unsigned int curr_irq;
- struct workqueue_struct *wq;
- struct work_struct hotplug_work;
+ struct timer_list hotplug_timer;
struct i2c_client *ddc_port;
struct i2c_client *hdmiphy_port;
int i;
for (i = 0; i < ARRAY_SIZE(hdmi_v13_confs); ++i)
- if (hdmi_v13_confs[i].width == mode->hdisplay &&
- hdmi_v13_confs[i].height == mode->vdisplay &&
- hdmi_v13_confs[i].vrefresh == mode->vrefresh &&
- hdmi_v13_confs[i].interlace ==
+ if (hdmi_v13_confs[i].width == mode->crtc_hdisplay &&
+ hdmi_v13_confs[i].height == mode->crtc_vdisplay &&
+ hdmi_v13_confs[i].vrefresh == mode->vrefresh &&
+ hdmi_v13_confs[i].interlace ==
((mode->flags & DRM_MODE_FLAG_INTERLACE) ?
true : false))
return i;
struct hdmi_context *hdata = ctx;
struct edid *edid;
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+ DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", DRM_BASE_ID(connector),
+ drm_get_connector_name(connector));
if (!hdata->ddc_port)
return ERR_PTR(-ENODEV);
u32 aspect_ratio;
u32 mod;
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+ /* TODO: stringify HDMI_PACKET_TYPE */
+ DRM_DEBUG_KMS("type: %d ver: %d len: %d\n", infoframe->type,
+ infoframe->ver, infoframe->len);
mod = hdmi_reg_read(hdata, HDMI_MODE_SEL);
if (!hdata->has_hdmi_sink) {
hdmi_reg_writeb(hdata, HDMI_VSI_CON,
struct hdmi_context *hdata = ctx;
struct fb_videomode *check_timing = timing;
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
DRM_DEBUG_KMS("[%d]x[%d] [%d]Hz [%x]\n", check_timing->xres,
check_timing->yres, check_timing->refresh,
check_timing->vmode);
int ret;
int i;
+ DRM_DEBUG_KMS("\n");
+
if (!hdata->hdmiphy_port) {
DRM_ERROR("hdmiphy is not attached\n");
return;
static void hdmi_conf_apply(struct hdmi_context *hdata)
{
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+ DRM_DEBUG_KMS("\n");
hdmiphy_conf_reset(hdata);
hdmiphy_conf_apply(hdata);
{
struct drm_mode_object base;
+ DRM_DEBUG_KMS("[MODE:%d:%s]\n", DRM_BASE_ID(src), src->name);
+
/* following information should be preserved,
* required for releasing the drm_display_mode node,
* duplicated to recieve adjustment info. */
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
- struct drm_display_mode *m;
+ struct drm_display_mode *t, *con_mode;
struct hdmi_context *hdata = ctx;
int index;
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
- drm_mode_set_crtcinfo(adjusted_mode, 0);
-
- if (hdata->is_v13)
- index = hdmi_v13_conf_index(adjusted_mode);
- else
- index = find_hdmiphy_conf(adjusted_mode->clock * 1000);
+ DRM_DEBUG_KMS("[CONNECTOR:%d:%s] [MODE:%d:%s]\n",
+ DRM_BASE_ID(connector),
+ drm_get_connector_name(connector),
+ DRM_BASE_ID(mode), mode->name);
- /* just return if user desired mode exists. */
- if (index >= 0)
- return;
+ /*
+ * Match the incoming mode to a mode in the connector list and copy it
+ * over. This is important since this might be an adjusted mode from the
+ * mixer and those have differing crtc_* values.
+ */
+ list_for_each_entry_safe(con_mode, t, &connector->modes, head) {
+ if (mode->hdisplay == con_mode->hdisplay &&
+ mode->vdisplay == con_mode->vdisplay &&
+ mode->clock == con_mode->clock) {
+ hdmi_mode_copy(adjusted_mode, con_mode);
+ return;
+ }
+ }
/*
- * otherwise, find the most suitable mode among modes and change it
- * to adjusted_mode.
+ * We didn't find a mode which matched the desired resolution, so just
+ * find something with the same clock.
*/
- list_for_each_entry(m, &connector->modes, head) {
+ list_for_each_entry_safe(con_mode, t, &connector->modes, head) {
if (hdata->is_v13)
- index = hdmi_v13_conf_index(m);
+ index = hdmi_v13_conf_index(con_mode);
else
- index = find_hdmiphy_conf(m->clock * 1000);
+ index = find_hdmiphy_conf(con_mode->clock * 1000);
if (index >= 0) {
DRM_INFO("desired mode doesn't exist so\n");
DRM_INFO("use the most suitable mode among modes.\n");
- hdmi_mode_copy(adjusted_mode, m);
+ hdmi_mode_copy(adjusted_mode, con_mode);
break;
}
}
struct hdmi_core_regs *core = &hdata->mode_conf.core;
struct hdmi_tg_regs *tg = &hdata->mode_conf.tg;
+ DRM_DEBUG_KMS("[MODE:%d:%s]\n", DRM_BASE_ID(m), m->name);
+
hdata->mode_conf.vic = drm_match_cea_mode(m);
hdata->mode_conf.pixel_clock = m->clock * 1000;
- hdmi_set_reg(core->h_blank, 2, m->htotal - m->hdisplay);
- hdmi_set_reg(core->v_line, 2, m->vtotal);
- hdmi_set_reg(core->h_line, 2, m->htotal);
+ hdmi_set_reg(core->h_blank, 2, m->crtc_htotal - m->crtc_hdisplay);
+ hdmi_set_reg(core->v_line, 2, m->crtc_vtotal);
+ hdmi_set_reg(core->h_line, 2, m->crtc_htotal);
hdmi_set_reg(core->hsync_pol, 1,
(m->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0);
hdmi_set_reg(core->vsync_pol, 1,
if (m->flags & DRM_MODE_FLAG_INTERLACE) {
/* Interlaced Mode */
hdmi_set_reg(core->v_sync_line_bef_2, 2,
- (m->vsync_end - m->vdisplay) / 2);
+ (m->crtc_vsync_end - m->crtc_vdisplay) / 2);
hdmi_set_reg(core->v_sync_line_bef_1, 2,
- (m->vsync_start - m->vdisplay) / 2);
- hdmi_set_reg(core->v2_blank, 2, m->vtotal / 2);
- hdmi_set_reg(core->v1_blank, 2, (m->vtotal - m->vdisplay) / 2);
+ (m->crtc_vsync_start - m->crtc_vdisplay) / 2);
+ hdmi_set_reg(core->v2_blank, 2, m->crtc_vtotal / 2);
+ hdmi_set_reg(core->v1_blank, 2,
+ (m->crtc_vtotal - m->crtc_vdisplay) / 2);
hdmi_set_reg(core->v_blank_f0, 2,
- (m->vtotal + ((m->vsync_end - m->vsync_start) * 4) + 5) / 2);
- hdmi_set_reg(core->v_blank_f1, 2, m->vtotal);
- hdmi_set_reg(core->v_sync_line_aft_2, 2, (m->vtotal / 2) + 7);
- hdmi_set_reg(core->v_sync_line_aft_1, 2, (m->vtotal / 2) + 2);
+ (m->crtc_vtotal +
+ ((m->crtc_vsync_end - m->crtc_vsync_start) * 4) + 5)
+ / 2);
+ hdmi_set_reg(core->v_blank_f1, 2, m->crtc_vtotal);
+ hdmi_set_reg(core->v_sync_line_aft_2, 2,
+ (m->crtc_vtotal / 2) + 7);
+ hdmi_set_reg(core->v_sync_line_aft_1, 2,
+ (m->crtc_vtotal / 2) + 2);
hdmi_set_reg(core->v_sync_line_aft_pxl_2, 2,
- (m->htotal / 2) + (m->hsync_start - m->hdisplay));
+ (m->crtc_htotal / 2) +
+ (m->crtc_hsync_start - m->crtc_hdisplay));
hdmi_set_reg(core->v_sync_line_aft_pxl_1, 2,
- (m->htotal / 2) + (m->hsync_start - m->hdisplay));
- hdmi_set_reg(tg->vact_st, 2, (m->vtotal - m->vdisplay) / 2);
- hdmi_set_reg(tg->vact_sz, 2, m->vdisplay / 2);
+ (m->crtc_htotal / 2) +
+ (m->crtc_hsync_start - m->crtc_hdisplay));
+ hdmi_set_reg(tg->vact_st, 2,
+ (m->crtc_vtotal - m->crtc_vdisplay) / 2);
+ hdmi_set_reg(tg->vact_sz, 2, m->crtc_vdisplay / 2);
hdmi_set_reg(tg->vact_st2, 2, 0x249);/* Reset value + 1*/
hdmi_set_reg(tg->vact_st3, 2, 0x0);
hdmi_set_reg(tg->vact_st4, 2, 0x0);
} else {
/* Progressive Mode */
hdmi_set_reg(core->v_sync_line_bef_2, 2,
- m->vsync_end - m->vdisplay);
+ m->crtc_vsync_end - m->crtc_vdisplay);
hdmi_set_reg(core->v_sync_line_bef_1, 2,
- m->vsync_start - m->vdisplay);
- hdmi_set_reg(core->v2_blank, 2, m->vtotal);
- hdmi_set_reg(core->v1_blank, 2, m->vtotal - m->vdisplay);
+ m->crtc_vsync_start - m->crtc_vdisplay);
+ hdmi_set_reg(core->v2_blank, 2, m->crtc_vtotal);
+ hdmi_set_reg(core->v1_blank, 2,
+ m->crtc_vtotal - m->crtc_vdisplay);
hdmi_set_reg(core->v_blank_f0, 2, 0xffff);
hdmi_set_reg(core->v_blank_f1, 2, 0xffff);
hdmi_set_reg(core->v_sync_line_aft_2, 2, 0xffff);
hdmi_set_reg(core->v_sync_line_aft_1, 2, 0xffff);
hdmi_set_reg(core->v_sync_line_aft_pxl_2, 2, 0xffff);
hdmi_set_reg(core->v_sync_line_aft_pxl_1, 2, 0xffff);
- hdmi_set_reg(tg->vact_st, 2, m->vtotal - m->vdisplay);
- hdmi_set_reg(tg->vact_sz, 2, m->vdisplay);
+ hdmi_set_reg(tg->vact_st, 2, m->crtc_vtotal - m->crtc_vdisplay);
+ hdmi_set_reg(tg->vact_sz, 2, m->crtc_vdisplay);
hdmi_set_reg(tg->vact_st2, 2, 0x248); /* Reset value */
hdmi_set_reg(tg->vact_st3, 2, 0x47b); /* Reset value */
hdmi_set_reg(tg->vact_st4, 2, 0x6ae); /* Reset value */
}
/* Following values & calculations are same irrespective of mode type */
- hdmi_set_reg(core->h_sync_start, 2, m->hsync_start - m->hdisplay - 2);
- hdmi_set_reg(core->h_sync_end, 2, m->hsync_end - m->hdisplay - 2);
+ hdmi_set_reg(core->h_sync_start, 2,
+ m->crtc_hsync_start - m->crtc_hdisplay - 2);
+ hdmi_set_reg(core->h_sync_end, 2,
+ m->crtc_hsync_end - m->crtc_hdisplay - 2);
hdmi_set_reg(core->vact_space_1, 2, 0xffff);
hdmi_set_reg(core->vact_space_2, 2, 0xffff);
hdmi_set_reg(core->vact_space_3, 2, 0xffff);
/* Timing generator registers */
hdmi_set_reg(tg->cmd, 1, 0x0);
- hdmi_set_reg(tg->h_fsz, 2, m->htotal);
- hdmi_set_reg(tg->hact_st, 2, m->htotal - m->hdisplay);
- hdmi_set_reg(tg->hact_sz, 2, m->hdisplay);
- hdmi_set_reg(tg->v_fsz, 2, m->vtotal);
+ hdmi_set_reg(tg->h_fsz, 2, m->crtc_htotal);
+ hdmi_set_reg(tg->hact_st, 2, m->crtc_htotal - m->crtc_hdisplay);
+ hdmi_set_reg(tg->hact_sz, 2, m->crtc_hdisplay);
+ hdmi_set_reg(tg->v_fsz, 2, m->crtc_vtotal);
hdmi_set_reg(tg->vsync, 2, 0x1);
hdmi_set_reg(tg->vsync2, 2, 0x233); /* Reset value */
hdmi_set_reg(tg->field_chg, 2, 0x233); /* Reset value */
hdmi_set_reg(tg->field_bot_hdmi, 2, 0x233); /* Reset value */
hdmi_set_reg(tg->tg_3d, 1, 0x0);
+ /* Workaround 4 implementation for 1440x900 resolution support */
if (hdata->is_soc_exynos5) {
- /* Workaround 4 implementation for 1440x900 resolution support */
- if (m->hdisplay == 1440 && m->vdisplay == 900 && m->clock == 106500) {
- hdmi_set_reg(tg->hact_st, 2, m->htotal - m->hdisplay - 0xe0);
- hdmi_set_reg(tg->hact_sz, 2, m->hdisplay + 0xe0);
+ if (m->crtc_hdisplay == 1440 && m->crtc_vdisplay == 900 &&
+ m->clock == 106500) {
+ hdmi_set_reg(tg->hact_st, 2,
+ m->crtc_htotal - m->crtc_hdisplay - 0xe0);
+ hdmi_set_reg(tg->hact_sz, 2, m->crtc_hdisplay + 0xe0);
}
/* Workaround 3 implementation for 800x600 resolution support */
- if (m->hdisplay == 800 && m->vdisplay == 600 && m->clock == 40000) {
- hdmi_set_reg(tg->hact_st, 2, m->htotal - m->hdisplay - 0x20);
- hdmi_set_reg(tg->hact_sz, 2, m->hdisplay + 0x20);
+ if (m->hdisplay == 800 && m->vdisplay == 600
+ && m->clock == 40000) {
+ hdmi_set_reg(tg->hact_st, 2,
+ m->crtc_htotal - m->crtc_hdisplay - 0x20);
+ hdmi_set_reg(tg->hact_sz, 2, m->crtc_hdisplay + 0x20);
}
}
}
{
struct hdmi_context *hdata = ctx;
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+ DRM_DEBUG_KMS("[MODE:%d:%s]\n", DRM_BASE_ID(mode), mode->name);
if (hdata->is_v13)
hdata->cur_conf = hdmi_v13_conf_index(mode);
hdmi_v14_mode_set(hdata, mode);
}
-static void hdmi_commit(void *ctx)
-{
- struct hdmi_context *hdata = ctx;
-
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
- if (!hdata->is_hdmi_powered_on)
- return;
-
- hdmi_conf_apply(hdata);
- hdata->enabled = true;
-}
-
static int hdmiphy_update_bits(struct i2c_client *client, u8 *reg_cache,
u8 reg, u8 mask, u8 val)
{
u8 buffer[2];
int ret;
- DRM_DEBUG_KMS("%s: hdmiphy is %s\n", __func__, on ? "on" : "off");
+ DRM_DEBUG_KMS("%s\n", on ? "on" : "off");
/* Cache all 32 registers to make the code below faster */
buffer[0] = 0x0;
hdmi_conf_init(hdata);
if (!hdata->is_soc_exynos5)
hdmi_audio_init(hdata);
- hdmi_commit(hdata);
+ hdmi_conf_apply(hdata);
}
static void hdmi_resource_poweroff(struct hdmi_context *hdata)
{
struct hdmi_context *hdata = ctx;
+ DRM_DEBUG_KMS("[DPMS:%s]\n", drm_get_dpms_name(mode));
+
switch (mode) {
case DRM_MODE_DPMS_ON:
if (!hdata->is_hdmi_powered_on)
{
struct hdmi_context *hdata = ctx;
+ DRM_DEBUG_KMS("[DEV:%s]\n", drm_dev->devname);
+
hdata->drm_dev = drm_dev;
return 0;
/* manager */
.mode_fixup = hdmi_mode_fixup,
.mode_set = hdmi_mode_set,
- .commit = hdmi_commit,
};
/*
* Handle hotplug events outside the interrupt handler proper.
*/
-static void hdmi_hotplug_func(struct work_struct *work)
+static void hdmi_hotplug_timer_func(unsigned long data)
{
- struct hdmi_context *hdata =
- container_of(work, struct hdmi_context, hotplug_work);
+ struct hdmi_context *hdata = (struct hdmi_context *)data;
drm_helper_hpd_irq_event(hdata->drm_dev);
}
static irqreturn_t hdmi_irq_handler(int irq, void *arg)
{
struct hdmi_context *hdata = arg;
- u32 intc_flag;
+ u32 intc_flag, time_ms = HOTPLUG_DEBOUNCE_MS;
+
if (hdata->is_hdmi_powered_on) {
intc_flag = hdmi_reg_read(hdata, HDMI_INTC_FLAG);
/* clearing flags for HPD plug/unplug */
hdmi_reg_writemask(hdata, HDMI_INTC_FLAG, ~0,
HDMI_INTC_FLAG_HPD_PLUG);
}
+
+ /*
+ * No need to debounce if we're powered on, the hardware
+ * has already done it for us.
+ */
+ time_ms = 0;
}
if (hdata->drm_dev && hdata->hpd_handle)
- queue_work(hdata->wq, &hdata->hotplug_work);
+ mod_timer(&hdata->hotplug_timer,
+ jiffies + msecs_to_jiffies(time_ms));
return IRQ_HANDLED;
}
struct platform_device *audio_dev;
int ret;
+ DRM_DEBUG_KMS("[PDEV:%s]\n", pdev->name);
+
audio_dev = platform_device_alloc("exynos-hdmi-audio", -1);
if (!audio_dev) {
DRM_ERROR("hdmi audio device allocation failed.\n");
void hdmi_unregister_audio_device(void)
{
+ DRM_DEBUG_KMS("\n");
platform_device_unregister(hdmi_audio_device);
}
int ret;
enum of_gpio_flags flags;
- DRM_DEBUG_KMS("[%d]\n", __LINE__);
+ DRM_DEBUG_KMS("[PDEV:%s]\n", pdev->name);
pdata = pdev->dev.platform_data;
if (!pdata) {
hdata->external_irq = gpio_to_irq(hdata->hpd_gpio);
- /* create workqueue and hotplug work */
- hdata->wq = alloc_workqueue("exynos-drm-hdmi",
- WQ_UNBOUND | WQ_NON_REENTRANT, 1);
- if (hdata->wq == NULL) {
- DRM_ERROR("Failed to create workqueue.\n");
- ret = -ENOMEM;
- goto err_hdmiphy;
- }
- INIT_WORK(&hdata->hotplug_work, hdmi_hotplug_func);
-
ret = request_irq(hdata->internal_irq, hdmi_irq_handler,
IRQF_SHARED, "int_hdmi", hdata);
if (ret) {
DRM_ERROR("request int interrupt failed.\n");
- goto err_workqueue;
+ goto err_hdmiphy;
}
disable_irq(hdata->internal_irq);
}
disable_irq(hdata->external_irq);
+ setup_timer(&hdata->hotplug_timer, hdmi_hotplug_timer_func,
+ (unsigned long)hdata);
+
if (of_device_is_compatible(dev->of_node,
"samsung,exynos5-hdmi")) {
ret = hdmi_register_audio_device(pdev);
free_irq(hdata->external_irq, hdata);
err_int_irq:
free_irq(hdata->internal_irq, hdata);
- err_workqueue:
- destroy_workqueue(hdata->wq);
err_hdmiphy:
put_device(&hdata->hdmiphy_port->dev);
err_ddc:
struct hdmi_context *hdata = platform_get_drvdata(pdev);
struct hdmi_resources *res = &hdata->res;
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+ DRM_DEBUG_KMS("[PDEV:%s]\n", pdev->name);
hdmi_resource_poweroff(hdata);
free_irq(hdata->internal_irq, hdata);
free_irq(hdata->external_irq, hdata);
- cancel_work_sync(&hdata->hotplug_work);
- destroy_workqueue(hdata->wq);
+ del_timer(&hdata->hotplug_timer);
clk_disable(res->hdmi);
clk_disable(res->sclk_hdmi);