summaryrefslogtreecommitdiffstats
path: root/drivers/video/sh_mobile_hdmi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/sh_mobile_hdmi.c')
-rw-r--r--drivers/video/sh_mobile_hdmi.c340
1 files changed, 228 insertions, 112 deletions
diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c
index 55b3077ff6f..2b9e56a6bde 100644
--- a/drivers/video/sh_mobile_hdmi.c
+++ b/drivers/video/sh_mobile_hdmi.c
@@ -22,6 +22,7 @@
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/workqueue.h>
+#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/initval.h>
@@ -209,7 +210,11 @@ enum hotplug_state {
struct sh_hdmi {
void __iomem *base;
enum hotplug_state hp_state; /* hot-plug status */
- bool preprogrammed_mode; /* use a pre-programmed VIC or the external mode */
+ u8 preprogrammed_vic; /* use a pre-programmed VIC or
+ the external mode */
+ u8 edid_block_addr;
+ u8 edid_segment_nr;
+ u8 edid_blocks;
struct clk *hdmi_clk;
struct device *dev;
struct fb_info *info;
@@ -217,6 +222,7 @@ struct sh_hdmi {
struct delayed_work edid_work;
struct fb_var_screeninfo var;
struct fb_monspecs monspec;
+ struct notifier_block notifier;
};
static void hdmi_write(struct sh_hdmi *hdmi, u8 data, u8 reg)
@@ -342,7 +348,7 @@ static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi)
hdmi_write(hdmi, var->vsync_len, HDMI_EXTERNAL_V_DURATION);
/* Set bit 0 of HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS here for external mode */
- if (!hdmi->preprogrammed_mode)
+ if (!hdmi->preprogrammed_vic)
hdmi_write(hdmi, sync | 1 | (voffset << 4),
HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS);
}
@@ -466,7 +472,18 @@ static void sh_hdmi_audio_config(struct sh_hdmi *hdmi)
*/
static void sh_hdmi_phy_config(struct sh_hdmi *hdmi)
{
- if (hdmi->var.yres > 480) {
+ if (hdmi->var.pixclock < 10000) {
+ /* for 1080p8bit 148MHz */
+ hdmi_write(hdmi, 0x1d, HDMI_SLIPHDMIT_PARAM_SETTINGS_1);
+ hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_2);
+ hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_3);
+ hdmi_write(hdmi, 0x4c, HDMI_SLIPHDMIT_PARAM_SETTINGS_5);
+ hdmi_write(hdmi, 0x1e, HDMI_SLIPHDMIT_PARAM_SETTINGS_6);
+ hdmi_write(hdmi, 0x48, HDMI_SLIPHDMIT_PARAM_SETTINGS_7);
+ hdmi_write(hdmi, 0x0e, HDMI_SLIPHDMIT_PARAM_SETTINGS_8);
+ hdmi_write(hdmi, 0x25, HDMI_SLIPHDMIT_PARAM_SETTINGS_9);
+ hdmi_write(hdmi, 0x04, HDMI_SLIPHDMIT_PARAM_SETTINGS_10);
+ } else if (hdmi->var.pixclock < 30000) {
/* 720p, 8bit, 74.25MHz. Might need to be adjusted for other formats */
/*
* [1:0] Speed_A
@@ -565,13 +582,11 @@ static void sh_hdmi_avi_infoframe_setup(struct sh_hdmi *hdmi)
hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB3);
/*
- * VIC = 1280 x 720p: ignored if external config is used
- * Send 2 for 720 x 480p, 16 for 1080p, ignored in external mode
+ * VIC should be ignored if external config is used, so, we could just use 0,
+ * but play safe and use a valid value in any case just in case
*/
- if (hdmi->var.yres == 1080 && hdmi->var.xres == 1920)
- vic = 16;
- else if (hdmi->var.yres == 480 && hdmi->var.xres == 720)
- vic = 2;
+ if (hdmi->preprogrammed_vic)
+ vic = hdmi->preprogrammed_vic;
else
vic = 4;
hdmi_write(hdmi, vic, HDMI_CTRL_PKT_BUF_ACCESS_PB4);
@@ -685,11 +700,21 @@ static void sh_hdmi_configure(struct sh_hdmi *hdmi)
}
static unsigned long sh_hdmi_rate_error(struct sh_hdmi *hdmi,
- const struct fb_videomode *mode)
+ const struct fb_videomode *mode,
+ unsigned long *hdmi_rate, unsigned long *parent_rate)
{
- long target = PICOS2KHZ(mode->pixclock) * 1000,
- rate = clk_round_rate(hdmi->hdmi_clk, target);
- unsigned long rate_error = rate > 0 ? abs(rate - target) : ULONG_MAX;
+ unsigned long target = PICOS2KHZ(mode->pixclock) * 1000, rate_error;
+ struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data;
+
+ *hdmi_rate = clk_round_rate(hdmi->hdmi_clk, target);
+ if ((long)*hdmi_rate < 0)
+ *hdmi_rate = clk_get_rate(hdmi->hdmi_clk);
+
+ rate_error = (long)*hdmi_rate > 0 ? abs(*hdmi_rate - target) : ULONG_MAX;
+ if (rate_error && pdata->clk_optimize_parent)
+ rate_error = pdata->clk_optimize_parent(target, hdmi_rate, parent_rate);
+ else if (clk_get_parent(hdmi->hdmi_clk))
+ *parent_rate = clk_get_rate(clk_get_parent(hdmi->hdmi_clk));
dev_dbg(hdmi->dev, "%u-%u-%u-%u x %u-%u-%u-%u\n",
mode->left_margin, mode->xres,
@@ -697,14 +722,15 @@ static unsigned long sh_hdmi_rate_error(struct sh_hdmi *hdmi,
mode->upper_margin, mode->yres,
mode->lower_margin, mode->vsync_len);
- dev_dbg(hdmi->dev, "\t@%lu(+/-%lu)Hz, e=%lu / 1000, r=%uHz\n", target,
- rate_error, rate_error ? 10000 / (10 * target / rate_error) : 0,
- mode->refresh);
+ dev_dbg(hdmi->dev, "\t@%lu(+/-%lu)Hz, e=%lu / 1000, r=%uHz, p=%luHz\n", target,
+ rate_error, rate_error ? 10000 / (10 * target / rate_error) : 0,
+ mode->refresh, *parent_rate);
return rate_error;
}
-static int sh_hdmi_read_edid(struct sh_hdmi *hdmi)
+static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate,
+ unsigned long *parent_rate)
{
struct fb_var_screeninfo tmpvar;
struct fb_var_screeninfo *var = &tmpvar;
@@ -713,7 +739,7 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi)
struct fb_modelist *modelist = NULL;
unsigned int f_width = 0, f_height = 0, f_refresh = 0;
unsigned long found_rate_error = ULONG_MAX; /* silly compiler... */
- bool exact_match = false;
+ bool scanning = false, preferred_bad = false;
u8 edid[128];
char *forced;
int i;
@@ -735,7 +761,38 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi)
printk(KERN_CONT "\n");
#endif
- fb_edid_to_monspecs(edid, &hdmi->monspec);
+ if (!hdmi->edid_blocks) {
+ fb_edid_to_monspecs(edid, &hdmi->monspec);
+ hdmi->edid_blocks = edid[126] + 1;
+
+ dev_dbg(hdmi->dev, "%d main modes, %d extension blocks\n",
+ hdmi->monspec.modedb_len, hdmi->edid_blocks - 1);
+ } else {
+ dev_dbg(hdmi->dev, "Extension %u detected, DTD start %u\n",
+ edid[0], edid[2]);
+ fb_edid_add_monspecs(edid, &hdmi->monspec);
+ }
+
+ if (hdmi->edid_blocks > hdmi->edid_segment_nr * 2 +
+ (hdmi->edid_block_addr >> 7) + 1) {
+ /* More blocks to read */
+ if (hdmi->edid_block_addr) {
+ hdmi->edid_block_addr = 0;
+ hdmi->edid_segment_nr++;
+ } else {
+ hdmi->edid_block_addr = 0x80;
+ }
+ /* Set EDID word address */
+ hdmi_write(hdmi, hdmi->edid_block_addr, HDMI_EDID_WORD_ADDRESS);
+ /* Enable EDID interrupt */
+ hdmi_write(hdmi, 0xC6, HDMI_INTERRUPT_MASK_1);
+ /* Set EDID segment pointer - starts reading EDID */
+ hdmi_write(hdmi, hdmi->edid_segment_nr, HDMI_EDID_SEGMENT_POINTER);
+ return -EAGAIN;
+ }
+
+ /* All E-EDID blocks ready */
+ dev_dbg(hdmi->dev, "%d main and extended modes\n", hdmi->monspec.modedb_len);
fb_get_options("sh_mobile_lcdc", &forced);
if (forced && *forced) {
@@ -745,6 +802,9 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi)
if (i < 2) {
f_width = 0;
f_height = 0;
+ } else {
+ /* The user wants us to use the EDID data */
+ scanning = true;
}
dev_dbg(hdmi->dev, "Forced mode %ux%u@%uHz\n",
f_width, f_height, f_refresh);
@@ -752,34 +812,56 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi)
/* Walk monitor modes to find the best or the exact match */
for (i = 0, mode = hdmi->monspec.modedb;
- f_width && f_height && i < hdmi->monspec.modedb_len && !exact_match;
+ i < hdmi->monspec.modedb_len && scanning;
i++, mode++) {
- unsigned long rate_error = sh_hdmi_rate_error(hdmi, mode);
+ unsigned long rate_error;
- /* No interest in unmatching modes */
- if (f_width != mode->xres || f_height != mode->yres)
- continue;
- if (f_refresh == mode->refresh || (!f_refresh && !rate_error))
+ if (!f_width && !f_height) {
/*
- * Exact match if either the refresh rate matches or it
- * hasn't been specified and we've found a mode, for
- * which we can configure the clock precisely
- */
- exact_match = true;
- else if (found && found_rate_error <= rate_error)
- /*
- * We otherwise search for the closest matching clock
- * rate - either if no refresh rate has been specified
- * or we cannot find an exactly matching one
+ * A parameter string "video=sh_mobile_lcdc:0x0" means
+ * use the preferred EDID mode. If it is rejected by
+ * .fb_check_var(), keep looking, until an acceptable
+ * one is found.
*/
+ if ((mode->flag & FB_MODE_IS_FIRST) || preferred_bad)
+ scanning = false;
+ else
+ continue;
+ } else if (f_width != mode->xres || f_height != mode->yres) {
+ /* No interest in unmatching modes */
continue;
+ }
+
+ rate_error = sh_hdmi_rate_error(hdmi, mode, hdmi_rate, parent_rate);
+
+ if (scanning) {
+ if (f_refresh == mode->refresh || (!f_refresh && !rate_error))
+ /*
+ * Exact match if either the refresh rate
+ * matches or it hasn't been specified and we've
+ * found a mode, for which we can configure the
+ * clock precisely
+ */
+ scanning = false;
+ else if (found && found_rate_error <= rate_error)
+ /*
+ * We otherwise search for the closest matching
+ * clock rate - either if no refresh rate has
+ * been specified or we cannot find an exactly
+ * matching one
+ */
+ continue;
+ }
/* Check if supported: sufficient fb memory, supported clock-rate */
fb_videomode_to_var(var, mode);
+ var->bits_per_pixel = info->var.bits_per_pixel;
+
if (info && info->fbops->fb_check_var &&
info->fbops->fb_check_var(var, info)) {
- exact_match = false;
+ scanning = true;
+ preferred_bad = true;
continue;
}
@@ -787,6 +869,9 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi)
found_rate_error = rate_error;
}
+ hdmi->var.width = hdmi->monspec.max_x * 10;
+ hdmi->var.height = hdmi->monspec.max_y * 10;
+
/*
* TODO 1: if no ->info is present, postpone running the config until
* after ->info first gets registered.
@@ -794,15 +879,15 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi)
* driver, and passing ->info with HDMI platform data.
*/
if (info && !found) {
- modelist = hdmi->info->modelist.next &&
- !list_empty(&hdmi->info->modelist) ?
- list_entry(hdmi->info->modelist.next,
+ modelist = info->modelist.next &&
+ !list_empty(&info->modelist) ?
+ list_entry(info->modelist.next,
struct fb_modelist, list) :
NULL;
if (modelist) {
found = &modelist->mode;
- found_rate_error = sh_hdmi_rate_error(hdmi, found);
+ found_rate_error = sh_hdmi_rate_error(hdmi, found, hdmi_rate, parent_rate);
}
}
@@ -810,16 +895,27 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi)
if (!found)
return -ENXIO;
- dev_info(hdmi->dev, "Using %s mode %ux%u@%uHz (%luHz), clock error %luHz\n",
- modelist ? "default" : "EDID", found->xres, found->yres,
- found->refresh, PICOS2KHZ(found->pixclock) * 1000, found_rate_error);
-
- if ((found->xres == 720 && found->yres == 480) ||
- (found->xres == 1280 && found->yres == 720) ||
- (found->xres == 1920 && found->yres == 1080))
- hdmi->preprogrammed_mode = true;
+ if (found->xres == 640 && found->yres == 480 && found->refresh == 60)
+ hdmi->preprogrammed_vic = 1;
+ else if (found->xres == 720 && found->yres == 480 && found->refresh == 60)
+ hdmi->preprogrammed_vic = 2;
+ else if (found->xres == 720 && found->yres == 576 && found->refresh == 50)
+ hdmi->preprogrammed_vic = 17;
+ else if (found->xres == 1280 && found->yres == 720 && found->refresh == 60)
+ hdmi->preprogrammed_vic = 4;
+ else if (found->xres == 1920 && found->yres == 1080 && found->refresh == 24)
+ hdmi->preprogrammed_vic = 32;
+ else if (found->xres == 1920 && found->yres == 1080 && found->refresh == 50)
+ hdmi->preprogrammed_vic = 31;
+ else if (found->xres == 1920 && found->yres == 1080 && found->refresh == 60)
+ hdmi->preprogrammed_vic = 16;
else
- hdmi->preprogrammed_mode = false;
+ hdmi->preprogrammed_vic = 0;
+
+ dev_dbg(hdmi->dev, "Using %s %s mode %ux%u@%uHz (%luHz), clock error %luHz\n",
+ modelist ? "default" : "EDID", hdmi->preprogrammed_vic ? "VIC" : "external",
+ found->xres, found->yres, found->refresh,
+ PICOS2KHZ(found->pixclock) * 1000, found_rate_error);
fb_videomode_to_var(&hdmi->var, found);
sh_hdmi_external_video_param(hdmi);
@@ -868,32 +964,34 @@ static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id)
/* Check, if hot plug & MSENS pin status are both high */
if ((msens & 0xC0) == 0xC0) {
/* Display plug in */
+ hdmi->edid_segment_nr = 0;
+ hdmi->edid_block_addr = 0;
+ hdmi->edid_blocks = 0;
hdmi->hp_state = HDMI_HOTPLUG_CONNECTED;
/* Set EDID word address */
hdmi_write(hdmi, 0x00, HDMI_EDID_WORD_ADDRESS);
- /* Set EDID segment pointer */
- hdmi_write(hdmi, 0x00, HDMI_EDID_SEGMENT_POINTER);
/* Enable EDID interrupt */
hdmi_write(hdmi, 0xC6, HDMI_INTERRUPT_MASK_1);
+ /* Set EDID segment pointer - starts reading EDID */
+ hdmi_write(hdmi, 0x00, HDMI_EDID_SEGMENT_POINTER);
} else if (!(status1 & 0x80)) {
/* Display unplug, beware multiple interrupts */
- if (hdmi->hp_state != HDMI_HOTPLUG_DISCONNECTED)
+ if (hdmi->hp_state != HDMI_HOTPLUG_DISCONNECTED) {
+ hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED;
schedule_delayed_work(&hdmi->edid_work, 0);
-
- hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED;
+ }
/* display_off will switch back to mode_a */
}
} else if (status1 & 2) {
/* EDID error interrupt: retry */
/* Set EDID word address */
- hdmi_write(hdmi, 0x00, HDMI_EDID_WORD_ADDRESS);
+ hdmi_write(hdmi, hdmi->edid_block_addr, HDMI_EDID_WORD_ADDRESS);
/* Set EDID segment pointer */
- hdmi_write(hdmi, 0x00, HDMI_EDID_SEGMENT_POINTER);
+ hdmi_write(hdmi, hdmi->edid_segment_nr, HDMI_EDID_SEGMENT_POINTER);
} else if (status1 & 4) {
/* Disable EDID interrupt */
hdmi_write(hdmi, 0xC0, HDMI_INTERRUPT_MASK_1);
- hdmi->hp_state = HDMI_HOTPLUG_EDID_DONE;
schedule_delayed_work(&hdmi->edid_work, msecs_to_jiffies(10));
}
@@ -960,8 +1058,12 @@ static bool sh_hdmi_must_reconfigure(struct sh_hdmi *hdmi)
dev_dbg(info->dev, "Old %ux%u, new %ux%u\n",
mode1.xres, mode1.yres, mode2.xres, mode2.yres);
- if (fb_mode_is_equal(&mode1, &mode2))
+ if (fb_mode_is_equal(&mode1, &mode2)) {
+ /* It can be a different monitor with an equal video-mode */
+ old_var->width = new_var->width;
+ old_var->height = new_var->height;
return false;
+ }
dev_dbg(info->dev, "Switching %u -> %u lines\n",
mode1.yres, mode2.yres);
@@ -972,39 +1074,37 @@ static bool sh_hdmi_must_reconfigure(struct sh_hdmi *hdmi)
/**
* sh_hdmi_clk_configure() - set HDMI clock frequency and enable the clock
- * @hdmi: driver context
- * @pixclock: pixel clock period in picoseconds
- * return: configured positive rate if successful
- * 0 if couldn't set the rate, but managed to enable the clock
- * negative error, if couldn't enable the clock
+ * @hdmi: driver context
+ * @hdmi_rate: HDMI clock frequency in Hz
+ * @parent_rate: if != 0 - set parent clock rate for optimal precision
+ * return: configured positive rate if successful
+ * 0 if couldn't set the rate, but managed to enable the
+ * clock, negative error, if couldn't enable the clock
*/
-static long sh_hdmi_clk_configure(struct sh_hdmi *hdmi, unsigned long pixclock)
+static long sh_hdmi_clk_configure(struct sh_hdmi *hdmi, unsigned long hdmi_rate,
+ unsigned long parent_rate)
{
- long rate;
int ret;
- rate = PICOS2KHZ(pixclock) * 1000;
- rate = clk_round_rate(hdmi->hdmi_clk, rate);
- if (rate > 0) {
- ret = clk_set_rate(hdmi->hdmi_clk, rate);
+ if (parent_rate && clk_get_parent(hdmi->hdmi_clk)) {
+ ret = clk_set_rate(clk_get_parent(hdmi->hdmi_clk), parent_rate);
if (ret < 0) {
- dev_warn(hdmi->dev, "Cannot set rate %ld: %d\n", rate, ret);
- rate = 0;
+ dev_warn(hdmi->dev, "Cannot set parent rate %ld: %d\n", parent_rate, ret);
+ hdmi_rate = clk_round_rate(hdmi->hdmi_clk, hdmi_rate);
} else {
- dev_dbg(hdmi->dev, "HDMI set frequency %lu\n", rate);
+ dev_dbg(hdmi->dev, "HDMI set parent frequency %lu\n", parent_rate);
}
- } else {
- rate = 0;
- dev_warn(hdmi->dev, "Cannot get suitable rate: %ld\n", rate);
}
- ret = clk_enable(hdmi->hdmi_clk);
+ ret = clk_set_rate(hdmi->hdmi_clk, hdmi_rate);
if (ret < 0) {
- dev_err(hdmi->dev, "Cannot enable clock: %d\n", ret);
- return ret;
+ dev_warn(hdmi->dev, "Cannot set rate %ld: %d\n", hdmi_rate, ret);
+ hdmi_rate = 0;
+ } else {
+ dev_dbg(hdmi->dev, "HDMI set frequency %lu\n", hdmi_rate);
}
- return rate;
+ return hdmi_rate;
}
/* Hotplug interrupt occurred, read EDID */
@@ -1023,17 +1123,21 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work)
mutex_lock(&hdmi->mutex);
- if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) {
+ if (hdmi->hp_state == HDMI_HOTPLUG_CONNECTED) {
+ struct fb_info *info = hdmi->info;
+ unsigned long parent_rate = 0, hdmi_rate;
+
/* A device has been plugged in */
pm_runtime_get_sync(hdmi->dev);
- ret = sh_hdmi_read_edid(hdmi);
+ ret = sh_hdmi_read_edid(hdmi, &hdmi_rate, &parent_rate);
if (ret < 0)
goto out;
+ hdmi->hp_state = HDMI_HOTPLUG_EDID_DONE;
+
/* Reconfigure the clock */
- clk_disable(hdmi->hdmi_clk);
- ret = sh_hdmi_clk_configure(hdmi, hdmi->var.pixclock);
+ ret = sh_hdmi_clk_configure(hdmi, hdmi_rate, parent_rate);
if (ret < 0)
goto out;
@@ -1042,47 +1146,52 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work)
/* Switched to another (d) power-save mode */
msleep(10);
- if (!hdmi->info)
+ if (!info)
goto out;
- ch = hdmi->info->par;
+ ch = info->par;
- acquire_console_sem();
+ console_lock();
/* HDMI plug in */
if (!sh_hdmi_must_reconfigure(hdmi) &&
- hdmi->info->state == FBINFO_STATE_RUNNING) {
+ info->state == FBINFO_STATE_RUNNING) {
/*
* First activation with the default monitor - just turn
* on, if we run a resume here, the logo disappears
*/
- if (lock_fb_info(hdmi->info)) {
- sh_hdmi_display_on(hdmi, hdmi->info);
- unlock_fb_info(hdmi->info);
+ if (lock_fb_info(info)) {
+ info->var.width = hdmi->var.width;
+ info->var.height = hdmi->var.height;
+ sh_hdmi_display_on(hdmi, info);
+ unlock_fb_info(info);
}
} else {
/* New monitor or have to wake up */
- fb_set_suspend(hdmi->info, 0);
+ fb_set_suspend(info, 0);
}
- release_console_sem();
+ console_unlock();
} else {
ret = 0;
if (!hdmi->info)
goto out;
- acquire_console_sem();
+ hdmi->monspec.modedb_len = 0;
+ fb_destroy_modedb(hdmi->monspec.modedb);
+ hdmi->monspec.modedb = NULL;
+
+ console_lock();
/* HDMI disconnect */
fb_set_suspend(hdmi->info, 1);
- release_console_sem();
+ console_unlock();
pm_runtime_put(hdmi->dev);
- fb_destroy_modedb(hdmi->monspec.modedb);
}
out:
- if (ret < 0)
+ if (ret < 0 && ret != -EAGAIN)
hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED;
mutex_unlock(&hdmi->mutex);
@@ -1090,13 +1199,6 @@ out:
}
static int sh_hdmi_notify(struct notifier_block *nb,
- unsigned long action, void *data);
-
-static struct notifier_block sh_hdmi_notifier = {
- .notifier_call = sh_hdmi_notify,
-};
-
-static int sh_hdmi_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
struct fb_event *event = data;
@@ -1105,7 +1207,7 @@ static int sh_hdmi_notify(struct notifier_block *nb,
struct sh_mobile_lcdc_board_cfg *board_cfg = &ch->cfg.board_cfg;
struct sh_hdmi *hdmi = board_cfg->board_data;
- if (nb != &sh_hdmi_notifier || !hdmi || hdmi->info != info)
+ if (!hdmi || nb != &hdmi->notifier || hdmi->info != info)
return NOTIFY_DONE;
switch(action) {
@@ -1124,11 +1226,11 @@ static int sh_hdmi_notify(struct notifier_block *nb,
* temporarily, synchronise with the work queue and re-acquire
* the info->lock.
*/
- unlock_fb_info(hdmi->info);
+ unlock_fb_info(info);
mutex_lock(&hdmi->mutex);
hdmi->info = NULL;
mutex_unlock(&hdmi->mutex);
- lock_fb_info(hdmi->info);
+ lock_fb_info(info);
return NOTIFY_OK;
}
return NOTIFY_DONE;
@@ -1163,13 +1265,22 @@ static int __init sh_hdmi_probe(struct platform_device *pdev)
goto egetclk;
}
- /* Some arbitrary relaxed pixclock just to get things started */
- rate = sh_hdmi_clk_configure(hdmi, 37037);
+ /* An arbitrary relaxed pixclock just to get things started: from standard 480p */
+ rate = clk_round_rate(hdmi->hdmi_clk, PICOS2KHZ(37037));
+ if (rate > 0)
+ rate = sh_hdmi_clk_configure(hdmi, rate, 0);
+
if (rate < 0) {
ret = rate;
goto erate;
}
+ ret = clk_enable(hdmi->hdmi_clk);
+ if (ret < 0) {
+ dev_err(hdmi->dev, "Cannot enable clock: %d\n", ret);
+ goto erate;
+ }
+
dev_dbg(&pdev->dev, "Enabled HDMI clock at %luHz\n", rate);
if (!request_mem_region(res->start, resource_size(res), dev_name(&pdev->dev))) {
@@ -1187,10 +1298,6 @@ static int __init sh_hdmi_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, hdmi);
- /* Product and revision IDs are 0 in sh-mobile version */
- dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n",
- hdmi_read(hdmi, HDMI_PRODUCT_ID), hdmi_read(hdmi, HDMI_REVISION_ID));
-
/* Set up LCDC callbacks */
board_cfg = &pdata->lcd_chan->board_cfg;
board_cfg->owner = THIS_MODULE;
@@ -1203,6 +1310,10 @@ static int __init sh_hdmi_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
pm_runtime_resume(&pdev->dev);
+ /* Product and revision IDs are 0 in sh-mobile version */
+ dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n",
+ hdmi_read(hdmi, HDMI_PRODUCT_ID), hdmi_read(hdmi, HDMI_REVISION_ID));
+
ret = request_irq(irq, sh_hdmi_hotplug, 0,
dev_name(&pdev->dev), hdmi);
if (ret < 0) {
@@ -1217,6 +1328,9 @@ static int __init sh_hdmi_probe(struct platform_device *pdev)
goto ecodec;
}
+ hdmi->notifier.notifier_call = sh_hdmi_notify;
+ fb_register_client(&hdmi->notifier);
+
return 0;
ecodec:
@@ -1247,6 +1361,8 @@ static int __exit sh_hdmi_remove(struct platform_device *pdev)
snd_soc_unregister_codec(&pdev->dev);
+ fb_unregister_client(&hdmi->notifier);
+
board_cfg->display_on = NULL;
board_cfg->display_off = NULL;
board_cfg->board_data = NULL;