diff options
Diffstat (limited to 'drivers/video/sh_mobile_hdmi.c')
-rw-r--r-- | drivers/video/sh_mobile_hdmi.c | 297 |
1 files changed, 74 insertions, 223 deletions
diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c index 647ba984f00f..eafb19da2c07 100644 --- a/drivers/video/sh_mobile_hdmi.c +++ b/drivers/video/sh_mobile_hdmi.c | |||
@@ -208,6 +208,8 @@ enum hotplug_state { | |||
208 | }; | 208 | }; |
209 | 209 | ||
210 | struct sh_hdmi { | 210 | struct sh_hdmi { |
211 | struct sh_mobile_lcdc_entity entity; | ||
212 | |||
211 | void __iomem *base; | 213 | void __iomem *base; |
212 | enum hotplug_state hp_state; /* hot-plug status */ | 214 | enum hotplug_state hp_state; /* hot-plug status */ |
213 | u8 preprogrammed_vic; /* use a pre-programmed VIC or | 215 | u8 preprogrammed_vic; /* use a pre-programmed VIC or |
@@ -217,14 +219,13 @@ struct sh_hdmi { | |||
217 | u8 edid_blocks; | 219 | u8 edid_blocks; |
218 | struct clk *hdmi_clk; | 220 | struct clk *hdmi_clk; |
219 | struct device *dev; | 221 | struct device *dev; |
220 | struct fb_info *info; | ||
221 | struct mutex mutex; /* Protect the info pointer */ | ||
222 | struct delayed_work edid_work; | 222 | struct delayed_work edid_work; |
223 | struct fb_var_screeninfo var; | 223 | struct fb_videomode mode; |
224 | struct fb_monspecs monspec; | 224 | struct fb_monspecs monspec; |
225 | struct notifier_block notifier; | ||
226 | }; | 225 | }; |
227 | 226 | ||
227 | #define entity_to_sh_hdmi(e) container_of(e, struct sh_hdmi, entity) | ||
228 | |||
228 | static void hdmi_write(struct sh_hdmi *hdmi, u8 data, u8 reg) | 229 | static void hdmi_write(struct sh_hdmi *hdmi, u8 data, u8 reg) |
229 | { | 230 | { |
230 | iowrite8(data, hdmi->base + reg); | 231 | iowrite8(data, hdmi->base + reg); |
@@ -290,24 +291,24 @@ static struct snd_soc_codec_driver soc_codec_dev_sh_hdmi = { | |||
290 | /* External video parameter settings */ | 291 | /* External video parameter settings */ |
291 | static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi) | 292 | static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi) |
292 | { | 293 | { |
293 | struct fb_var_screeninfo *var = &hdmi->var; | 294 | struct fb_videomode *mode = &hdmi->mode; |
294 | u16 htotal, hblank, hdelay, vtotal, vblank, vdelay, voffset; | 295 | u16 htotal, hblank, hdelay, vtotal, vblank, vdelay, voffset; |
295 | u8 sync = 0; | 296 | u8 sync = 0; |
296 | 297 | ||
297 | htotal = var->xres + var->right_margin + var->left_margin + var->hsync_len; | 298 | htotal = mode->xres + mode->right_margin + mode->left_margin |
298 | 299 | + mode->hsync_len; | |
299 | hdelay = var->hsync_len + var->left_margin; | 300 | hdelay = mode->hsync_len + mode->left_margin; |
300 | hblank = var->right_margin + hdelay; | 301 | hblank = mode->right_margin + hdelay; |
301 | 302 | ||
302 | /* | 303 | /* |
303 | * Vertical timing looks a bit different in Figure 18, | 304 | * Vertical timing looks a bit different in Figure 18, |
304 | * but let's try the same first by setting offset = 0 | 305 | * but let's try the same first by setting offset = 0 |
305 | */ | 306 | */ |
306 | vtotal = var->yres + var->upper_margin + var->lower_margin + var->vsync_len; | 307 | vtotal = mode->yres + mode->upper_margin + mode->lower_margin |
307 | 308 | + mode->vsync_len; | |
308 | vdelay = var->vsync_len + var->upper_margin; | 309 | vdelay = mode->vsync_len + mode->upper_margin; |
309 | vblank = var->lower_margin + vdelay; | 310 | vblank = mode->lower_margin + vdelay; |
310 | voffset = min(var->upper_margin / 2, 6U); | 311 | voffset = min(mode->upper_margin / 2, 6U); |
311 | 312 | ||
312 | /* | 313 | /* |
313 | * [3]: VSYNC polarity: Positive | 314 | * [3]: VSYNC polarity: Positive |
@@ -315,14 +316,14 @@ static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi) | |||
315 | * [1]: Interlace/Progressive: Progressive | 316 | * [1]: Interlace/Progressive: Progressive |
316 | * [0]: External video settings enable: used. | 317 | * [0]: External video settings enable: used. |
317 | */ | 318 | */ |
318 | if (var->sync & FB_SYNC_HOR_HIGH_ACT) | 319 | if (mode->sync & FB_SYNC_HOR_HIGH_ACT) |
319 | sync |= 4; | 320 | sync |= 4; |
320 | if (var->sync & FB_SYNC_VERT_HIGH_ACT) | 321 | if (mode->sync & FB_SYNC_VERT_HIGH_ACT) |
321 | sync |= 8; | 322 | sync |= 8; |
322 | 323 | ||
323 | dev_dbg(hdmi->dev, "H: %u, %u, %u, %u; V: %u, %u, %u, %u; sync 0x%x\n", | 324 | dev_dbg(hdmi->dev, "H: %u, %u, %u, %u; V: %u, %u, %u, %u; sync 0x%x\n", |
324 | htotal, hblank, hdelay, var->hsync_len, | 325 | htotal, hblank, hdelay, mode->hsync_len, |
325 | vtotal, vblank, vdelay, var->vsync_len, sync); | 326 | vtotal, vblank, vdelay, mode->vsync_len, sync); |
326 | 327 | ||
327 | hdmi_write(hdmi, sync | (voffset << 4), HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS); | 328 | hdmi_write(hdmi, sync | (voffset << 4), HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS); |
328 | 329 | ||
@@ -335,8 +336,8 @@ static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi) | |||
335 | hdmi_write(hdmi, hdelay, HDMI_EXTERNAL_H_DELAY_7_0); | 336 | hdmi_write(hdmi, hdelay, HDMI_EXTERNAL_H_DELAY_7_0); |
336 | hdmi_write(hdmi, hdelay >> 8, HDMI_EXTERNAL_H_DELAY_9_8); | 337 | hdmi_write(hdmi, hdelay >> 8, HDMI_EXTERNAL_H_DELAY_9_8); |
337 | 338 | ||
338 | hdmi_write(hdmi, var->hsync_len, HDMI_EXTERNAL_H_DURATION_7_0); | 339 | hdmi_write(hdmi, mode->hsync_len, HDMI_EXTERNAL_H_DURATION_7_0); |
339 | hdmi_write(hdmi, var->hsync_len >> 8, HDMI_EXTERNAL_H_DURATION_9_8); | 340 | hdmi_write(hdmi, mode->hsync_len >> 8, HDMI_EXTERNAL_H_DURATION_9_8); |
340 | 341 | ||
341 | hdmi_write(hdmi, vtotal, HDMI_EXTERNAL_V_TOTAL_7_0); | 342 | hdmi_write(hdmi, vtotal, HDMI_EXTERNAL_V_TOTAL_7_0); |
342 | hdmi_write(hdmi, vtotal >> 8, HDMI_EXTERNAL_V_TOTAL_9_8); | 343 | hdmi_write(hdmi, vtotal >> 8, HDMI_EXTERNAL_V_TOTAL_9_8); |
@@ -345,7 +346,7 @@ static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi) | |||
345 | 346 | ||
346 | hdmi_write(hdmi, vdelay, HDMI_EXTERNAL_V_DELAY); | 347 | hdmi_write(hdmi, vdelay, HDMI_EXTERNAL_V_DELAY); |
347 | 348 | ||
348 | hdmi_write(hdmi, var->vsync_len, HDMI_EXTERNAL_V_DURATION); | 349 | hdmi_write(hdmi, mode->vsync_len, HDMI_EXTERNAL_V_DURATION); |
349 | 350 | ||
350 | /* Set bit 0 of HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS here for external mode */ | 351 | /* Set bit 0 of HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS here for external mode */ |
351 | if (!hdmi->preprogrammed_vic) | 352 | if (!hdmi->preprogrammed_vic) |
@@ -472,7 +473,7 @@ static void sh_hdmi_audio_config(struct sh_hdmi *hdmi) | |||
472 | */ | 473 | */ |
473 | static void sh_hdmi_phy_config(struct sh_hdmi *hdmi) | 474 | static void sh_hdmi_phy_config(struct sh_hdmi *hdmi) |
474 | { | 475 | { |
475 | if (hdmi->var.pixclock < 10000) { | 476 | if (hdmi->mode.pixclock < 10000) { |
476 | /* for 1080p8bit 148MHz */ | 477 | /* for 1080p8bit 148MHz */ |
477 | hdmi_write(hdmi, 0x1d, HDMI_SLIPHDMIT_PARAM_SETTINGS_1); | 478 | hdmi_write(hdmi, 0x1d, HDMI_SLIPHDMIT_PARAM_SETTINGS_1); |
478 | hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_2); | 479 | hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_2); |
@@ -483,7 +484,7 @@ static void sh_hdmi_phy_config(struct sh_hdmi *hdmi) | |||
483 | hdmi_write(hdmi, 0x0e, HDMI_SLIPHDMIT_PARAM_SETTINGS_8); | 484 | hdmi_write(hdmi, 0x0e, HDMI_SLIPHDMIT_PARAM_SETTINGS_8); |
484 | hdmi_write(hdmi, 0x25, HDMI_SLIPHDMIT_PARAM_SETTINGS_9); | 485 | hdmi_write(hdmi, 0x25, HDMI_SLIPHDMIT_PARAM_SETTINGS_9); |
485 | hdmi_write(hdmi, 0x04, HDMI_SLIPHDMIT_PARAM_SETTINGS_10); | 486 | hdmi_write(hdmi, 0x04, HDMI_SLIPHDMIT_PARAM_SETTINGS_10); |
486 | } else if (hdmi->var.pixclock < 30000) { | 487 | } else if (hdmi->mode.pixclock < 30000) { |
487 | /* 720p, 8bit, 74.25MHz. Might need to be adjusted for other formats */ | 488 | /* 720p, 8bit, 74.25MHz. Might need to be adjusted for other formats */ |
488 | /* | 489 | /* |
489 | * [1:0] Speed_A | 490 | * [1:0] Speed_A |
@@ -732,14 +733,12 @@ static unsigned long sh_hdmi_rate_error(struct sh_hdmi *hdmi, | |||
732 | static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, | 733 | static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, |
733 | unsigned long *parent_rate) | 734 | unsigned long *parent_rate) |
734 | { | 735 | { |
735 | struct fb_var_screeninfo tmpvar; | 736 | struct sh_mobile_lcdc_chan *ch = hdmi->entity.lcdc; |
736 | struct fb_var_screeninfo *var = &tmpvar; | ||
737 | const struct fb_videomode *mode, *found = NULL; | 737 | const struct fb_videomode *mode, *found = NULL; |
738 | struct fb_info *info = hdmi->info; | ||
739 | struct fb_modelist *modelist = NULL; | ||
740 | unsigned int f_width = 0, f_height = 0, f_refresh = 0; | 738 | unsigned int f_width = 0, f_height = 0, f_refresh = 0; |
741 | unsigned long found_rate_error = ULONG_MAX; /* silly compiler... */ | 739 | unsigned long found_rate_error = ULONG_MAX; /* silly compiler... */ |
742 | bool scanning = false, preferred_bad = false; | 740 | bool scanning = false, preferred_bad = false; |
741 | bool use_edid_mode = false; | ||
743 | u8 edid[128]; | 742 | u8 edid[128]; |
744 | char *forced; | 743 | char *forced; |
745 | int i; | 744 | int i; |
@@ -854,12 +853,9 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, | |||
854 | } | 853 | } |
855 | 854 | ||
856 | /* Check if supported: sufficient fb memory, supported clock-rate */ | 855 | /* Check if supported: sufficient fb memory, supported clock-rate */ |
857 | fb_videomode_to_var(var, mode); | 856 | if (ch && ch->notify && |
858 | 857 | ch->notify(ch, SH_MOBILE_LCDC_EVENT_DISPLAY_MODE, mode, | |
859 | var->bits_per_pixel = info->var.bits_per_pixel; | 858 | NULL)) { |
860 | |||
861 | if (info && info->fbops->fb_check_var && | ||
862 | info->fbops->fb_check_var(var, info)) { | ||
863 | scanning = true; | 859 | scanning = true; |
864 | preferred_bad = true; | 860 | preferred_bad = true; |
865 | continue; | 861 | continue; |
@@ -867,28 +863,19 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, | |||
867 | 863 | ||
868 | found = mode; | 864 | found = mode; |
869 | found_rate_error = rate_error; | 865 | found_rate_error = rate_error; |
866 | use_edid_mode = true; | ||
870 | } | 867 | } |
871 | 868 | ||
872 | hdmi->var.width = hdmi->monspec.max_x * 10; | ||
873 | hdmi->var.height = hdmi->monspec.max_y * 10; | ||
874 | |||
875 | /* | 869 | /* |
876 | * TODO 1: if no ->info is present, postpone running the config until | 870 | * TODO 1: if no default mode is present, postpone running the config |
877 | * after ->info first gets registered. | 871 | * until after the LCDC channel is initialized. |
878 | * TODO 2: consider registering the HDMI platform device from the LCDC | 872 | * TODO 2: consider registering the HDMI platform device from the LCDC |
879 | * driver, and passing ->info with HDMI platform data. | 873 | * driver. |
880 | */ | 874 | */ |
881 | if (info && !found) { | 875 | if (!found && hdmi->entity.def_mode.xres != 0) { |
882 | modelist = info->modelist.next && | 876 | found = &hdmi->entity.def_mode; |
883 | !list_empty(&info->modelist) ? | 877 | found_rate_error = sh_hdmi_rate_error(hdmi, found, hdmi_rate, |
884 | list_entry(info->modelist.next, | 878 | parent_rate); |
885 | struct fb_modelist, list) : | ||
886 | NULL; | ||
887 | |||
888 | if (modelist) { | ||
889 | found = &modelist->mode; | ||
890 | found_rate_error = sh_hdmi_rate_error(hdmi, found, hdmi_rate, parent_rate); | ||
891 | } | ||
892 | } | 879 | } |
893 | 880 | ||
894 | /* No cookie today */ | 881 | /* No cookie today */ |
@@ -912,12 +899,13 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, | |||
912 | else | 899 | else |
913 | hdmi->preprogrammed_vic = 0; | 900 | hdmi->preprogrammed_vic = 0; |
914 | 901 | ||
915 | dev_dbg(hdmi->dev, "Using %s %s mode %ux%u@%uHz (%luHz), clock error %luHz\n", | 902 | dev_dbg(hdmi->dev, "Using %s %s mode %ux%u@%uHz (%luHz), " |
916 | modelist ? "default" : "EDID", hdmi->preprogrammed_vic ? "VIC" : "external", | 903 | "clock error %luHz\n", use_edid_mode ? "EDID" : "default", |
917 | found->xres, found->yres, found->refresh, | 904 | hdmi->preprogrammed_vic ? "VIC" : "external", found->xres, |
918 | PICOS2KHZ(found->pixclock) * 1000, found_rate_error); | 905 | found->yres, found->refresh, PICOS2KHZ(found->pixclock) * 1000, |
906 | found_rate_error); | ||
919 | 907 | ||
920 | fb_videomode_to_var(&hdmi->var, found); | 908 | hdmi->mode = *found; |
921 | sh_hdmi_external_video_param(hdmi); | 909 | sh_hdmi_external_video_param(hdmi); |
922 | 910 | ||
923 | return 0; | 911 | return 0; |
@@ -998,22 +986,12 @@ static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id) | |||
998 | return IRQ_HANDLED; | 986 | return IRQ_HANDLED; |
999 | } | 987 | } |
1000 | 988 | ||
1001 | /* locking: called with info->lock held, or before register_framebuffer() */ | 989 | static int sh_hdmi_display_on(struct sh_mobile_lcdc_entity *entity) |
1002 | static void sh_hdmi_display_on(void *arg, struct fb_info *info) | ||
1003 | { | 990 | { |
1004 | /* | 991 | struct sh_hdmi *hdmi = entity_to_sh_hdmi(entity); |
1005 | * info is guaranteed to be valid, when we are called, because our | ||
1006 | * FB_EVENT_FB_UNBIND notify is also called with info->lock held | ||
1007 | */ | ||
1008 | struct sh_hdmi *hdmi = arg; | ||
1009 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; | ||
1010 | struct sh_mobile_lcdc_chan *ch = info->par; | ||
1011 | 992 | ||
1012 | dev_dbg(hdmi->dev, "%s(%p): state %x\n", __func__, | 993 | dev_dbg(hdmi->dev, "%s(%p): state %x\n", __func__, hdmi, |
1013 | pdata->lcd_dev, info->state); | 994 | hdmi->hp_state); |
1014 | |||
1015 | /* No need to lock */ | ||
1016 | hdmi->info = info; | ||
1017 | 995 | ||
1018 | /* | 996 | /* |
1019 | * hp_state can be set to | 997 | * hp_state can be set to |
@@ -1021,56 +999,30 @@ static void sh_hdmi_display_on(void *arg, struct fb_info *info) | |||
1021 | * HDMI_HOTPLUG_CONNECTED: on monitor plug-in | 999 | * HDMI_HOTPLUG_CONNECTED: on monitor plug-in |
1022 | * HDMI_HOTPLUG_EDID_DONE: on EDID read completion | 1000 | * HDMI_HOTPLUG_EDID_DONE: on EDID read completion |
1023 | */ | 1001 | */ |
1024 | switch (hdmi->hp_state) { | 1002 | if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) { |
1025 | case HDMI_HOTPLUG_EDID_DONE: | ||
1026 | /* PS mode d->e. All functions are active */ | 1003 | /* PS mode d->e. All functions are active */ |
1027 | hdmi_write(hdmi, 0x80, HDMI_SYSTEM_CTRL); | 1004 | hdmi_write(hdmi, 0x80, HDMI_SYSTEM_CTRL); |
1028 | dev_dbg(hdmi->dev, "HDMI running\n"); | 1005 | dev_dbg(hdmi->dev, "HDMI running\n"); |
1029 | break; | ||
1030 | case HDMI_HOTPLUG_DISCONNECTED: | ||
1031 | info->state = FBINFO_STATE_SUSPENDED; | ||
1032 | default: | ||
1033 | hdmi->var = ch->display_var; | ||
1034 | } | 1006 | } |
1007 | |||
1008 | return hdmi->hp_state == HDMI_HOTPLUG_DISCONNECTED | ||
1009 | ? SH_MOBILE_LCDC_DISPLAY_DISCONNECTED | ||
1010 | : SH_MOBILE_LCDC_DISPLAY_CONNECTED; | ||
1035 | } | 1011 | } |
1036 | 1012 | ||
1037 | /* locking: called with info->lock held */ | 1013 | static void sh_hdmi_display_off(struct sh_mobile_lcdc_entity *entity) |
1038 | static void sh_hdmi_display_off(void *arg) | ||
1039 | { | 1014 | { |
1040 | struct sh_hdmi *hdmi = arg; | 1015 | struct sh_hdmi *hdmi = entity_to_sh_hdmi(entity); |
1041 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; | ||
1042 | 1016 | ||
1043 | dev_dbg(hdmi->dev, "%s(%p)\n", __func__, pdata->lcd_dev); | 1017 | dev_dbg(hdmi->dev, "%s(%p)\n", __func__, hdmi); |
1044 | /* PS mode e->a */ | 1018 | /* PS mode e->a */ |
1045 | hdmi_write(hdmi, 0x10, HDMI_SYSTEM_CTRL); | 1019 | hdmi_write(hdmi, 0x10, HDMI_SYSTEM_CTRL); |
1046 | } | 1020 | } |
1047 | 1021 | ||
1048 | static bool sh_hdmi_must_reconfigure(struct sh_hdmi *hdmi) | 1022 | static const struct sh_mobile_lcdc_entity_ops sh_hdmi_ops = { |
1049 | { | 1023 | .display_on = sh_hdmi_display_on, |
1050 | struct fb_info *info = hdmi->info; | 1024 | .display_off = sh_hdmi_display_off, |
1051 | struct sh_mobile_lcdc_chan *ch = info->par; | 1025 | }; |
1052 | struct fb_var_screeninfo *new_var = &hdmi->var, *old_var = &ch->display_var; | ||
1053 | struct fb_videomode mode1, mode2; | ||
1054 | |||
1055 | fb_var_to_videomode(&mode1, old_var); | ||
1056 | fb_var_to_videomode(&mode2, new_var); | ||
1057 | |||
1058 | dev_dbg(info->dev, "Old %ux%u, new %ux%u\n", | ||
1059 | mode1.xres, mode1.yres, mode2.xres, mode2.yres); | ||
1060 | |||
1061 | if (fb_mode_is_equal(&mode1, &mode2)) { | ||
1062 | /* It can be a different monitor with an equal video-mode */ | ||
1063 | old_var->width = new_var->width; | ||
1064 | old_var->height = new_var->height; | ||
1065 | return false; | ||
1066 | } | ||
1067 | |||
1068 | dev_dbg(info->dev, "Switching %u -> %u lines\n", | ||
1069 | mode1.yres, mode2.yres); | ||
1070 | *old_var = *new_var; | ||
1071 | |||
1072 | return true; | ||
1073 | } | ||
1074 | 1026 | ||
1075 | /** | 1027 | /** |
1076 | * sh_hdmi_clk_configure() - set HDMI clock frequency and enable the clock | 1028 | * sh_hdmi_clk_configure() - set HDMI clock frequency and enable the clock |
@@ -1111,20 +1063,11 @@ static long sh_hdmi_clk_configure(struct sh_hdmi *hdmi, unsigned long hdmi_rate, | |||
1111 | static void sh_hdmi_edid_work_fn(struct work_struct *work) | 1063 | static void sh_hdmi_edid_work_fn(struct work_struct *work) |
1112 | { | 1064 | { |
1113 | struct sh_hdmi *hdmi = container_of(work, struct sh_hdmi, edid_work.work); | 1065 | struct sh_hdmi *hdmi = container_of(work, struct sh_hdmi, edid_work.work); |
1114 | struct fb_info *info; | 1066 | struct sh_mobile_lcdc_chan *ch = hdmi->entity.lcdc; |
1115 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; | ||
1116 | struct sh_mobile_lcdc_chan *ch; | ||
1117 | int ret; | 1067 | int ret; |
1118 | 1068 | ||
1119 | dev_dbg(hdmi->dev, "%s(%p): begin, hotplug status %d\n", __func__, | 1069 | dev_dbg(hdmi->dev, "%s(%p): begin, hotplug status %d\n", __func__, hdmi, |
1120 | pdata->lcd_dev, hdmi->hp_state); | 1070 | hdmi->hp_state); |
1121 | |||
1122 | if (!pdata->lcd_dev) | ||
1123 | return; | ||
1124 | |||
1125 | mutex_lock(&hdmi->mutex); | ||
1126 | |||
1127 | info = hdmi->info; | ||
1128 | 1071 | ||
1129 | if (hdmi->hp_state == HDMI_HOTPLUG_CONNECTED) { | 1072 | if (hdmi->hp_state == HDMI_HOTPLUG_CONNECTED) { |
1130 | unsigned long parent_rate = 0, hdmi_rate; | 1073 | unsigned long parent_rate = 0, hdmi_rate; |
@@ -1145,103 +1088,32 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work) | |||
1145 | /* Switched to another (d) power-save mode */ | 1088 | /* Switched to another (d) power-save mode */ |
1146 | msleep(10); | 1089 | msleep(10); |
1147 | 1090 | ||
1148 | if (!info) | 1091 | if (ch && ch->notify) |
1149 | goto out; | 1092 | ch->notify(ch, SH_MOBILE_LCDC_EVENT_DISPLAY_CONNECT, |
1150 | 1093 | &hdmi->mode, &hdmi->monspec); | |
1151 | ch = info->par; | ||
1152 | |||
1153 | if (lock_fb_info(info)) { | ||
1154 | console_lock(); | ||
1155 | |||
1156 | /* HDMI plug in */ | ||
1157 | if (!sh_hdmi_must_reconfigure(hdmi) && | ||
1158 | info->state == FBINFO_STATE_RUNNING) { | ||
1159 | /* | ||
1160 | * First activation with the default monitor - just turn | ||
1161 | * on, if we run a resume here, the logo disappears | ||
1162 | */ | ||
1163 | info->var.width = hdmi->var.width; | ||
1164 | info->var.height = hdmi->var.height; | ||
1165 | sh_hdmi_display_on(hdmi, info); | ||
1166 | } else { | ||
1167 | /* New monitor or have to wake up */ | ||
1168 | fb_set_suspend(info, 0); | ||
1169 | } | ||
1170 | |||
1171 | console_unlock(); | ||
1172 | unlock_fb_info(info); | ||
1173 | } | ||
1174 | } else { | 1094 | } else { |
1175 | ret = 0; | ||
1176 | if (!info) | ||
1177 | goto out; | ||
1178 | |||
1179 | hdmi->monspec.modedb_len = 0; | 1095 | hdmi->monspec.modedb_len = 0; |
1180 | fb_destroy_modedb(hdmi->monspec.modedb); | 1096 | fb_destroy_modedb(hdmi->monspec.modedb); |
1181 | hdmi->monspec.modedb = NULL; | 1097 | hdmi->monspec.modedb = NULL; |
1182 | 1098 | ||
1183 | if (lock_fb_info(info)) { | 1099 | if (ch && ch->notify) |
1184 | console_lock(); | 1100 | ch->notify(ch, SH_MOBILE_LCDC_EVENT_DISPLAY_DISCONNECT, |
1101 | NULL, NULL); | ||
1185 | 1102 | ||
1186 | /* HDMI disconnect */ | 1103 | ret = 0; |
1187 | fb_set_suspend(info, 1); | ||
1188 | |||
1189 | console_unlock(); | ||
1190 | unlock_fb_info(info); | ||
1191 | } | ||
1192 | } | 1104 | } |
1193 | 1105 | ||
1194 | out: | 1106 | out: |
1195 | if (ret < 0 && ret != -EAGAIN) | 1107 | if (ret < 0 && ret != -EAGAIN) |
1196 | hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; | 1108 | hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; |
1197 | mutex_unlock(&hdmi->mutex); | ||
1198 | 1109 | ||
1199 | dev_dbg(hdmi->dev, "%s(%p): end\n", __func__, pdata->lcd_dev); | 1110 | dev_dbg(hdmi->dev, "%s(%p): end\n", __func__, hdmi); |
1200 | } | ||
1201 | |||
1202 | static int sh_hdmi_notify(struct notifier_block *nb, | ||
1203 | unsigned long action, void *data) | ||
1204 | { | ||
1205 | struct fb_event *event = data; | ||
1206 | struct fb_info *info = event->info; | ||
1207 | struct sh_mobile_lcdc_chan *ch = info->par; | ||
1208 | struct sh_mobile_lcdc_board_cfg *board_cfg = &ch->cfg.board_cfg; | ||
1209 | struct sh_hdmi *hdmi = board_cfg->board_data; | ||
1210 | |||
1211 | if (!hdmi || nb != &hdmi->notifier || hdmi->info != info) | ||
1212 | return NOTIFY_DONE; | ||
1213 | |||
1214 | switch(action) { | ||
1215 | case FB_EVENT_FB_REGISTERED: | ||
1216 | /* Unneeded, activation taken care by sh_hdmi_display_on() */ | ||
1217 | break; | ||
1218 | case FB_EVENT_FB_UNREGISTERED: | ||
1219 | /* | ||
1220 | * We are called from unregister_framebuffer() with the | ||
1221 | * info->lock held. This is bad for us, because we can race with | ||
1222 | * the scheduled work, which has to call fb_set_suspend(), which | ||
1223 | * takes info->lock internally, so, sh_hdmi_edid_work_fn() | ||
1224 | * cannot take and hold info->lock for the whole function | ||
1225 | * duration. Using an additional lock creates a classical AB-BA | ||
1226 | * lock up. Therefore, we have to release the info->lock | ||
1227 | * temporarily, synchronise with the work queue and re-acquire | ||
1228 | * the info->lock. | ||
1229 | */ | ||
1230 | unlock_fb_info(info); | ||
1231 | mutex_lock(&hdmi->mutex); | ||
1232 | hdmi->info = NULL; | ||
1233 | mutex_unlock(&hdmi->mutex); | ||
1234 | lock_fb_info(info); | ||
1235 | return NOTIFY_OK; | ||
1236 | } | ||
1237 | return NOTIFY_DONE; | ||
1238 | } | 1111 | } |
1239 | 1112 | ||
1240 | static int __init sh_hdmi_probe(struct platform_device *pdev) | 1113 | static int __init sh_hdmi_probe(struct platform_device *pdev) |
1241 | { | 1114 | { |
1242 | struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; | 1115 | struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; |
1243 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 1116 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
1244 | struct sh_mobile_lcdc_board_cfg *board_cfg; | ||
1245 | int irq = platform_get_irq(pdev, 0), ret; | 1117 | int irq = platform_get_irq(pdev, 0), ret; |
1246 | struct sh_hdmi *hdmi; | 1118 | struct sh_hdmi *hdmi; |
1247 | long rate; | 1119 | long rate; |
@@ -1255,9 +1127,9 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) | |||
1255 | return -ENOMEM; | 1127 | return -ENOMEM; |
1256 | } | 1128 | } |
1257 | 1129 | ||
1258 | mutex_init(&hdmi->mutex); | ||
1259 | |||
1260 | hdmi->dev = &pdev->dev; | 1130 | hdmi->dev = &pdev->dev; |
1131 | hdmi->entity.owner = THIS_MODULE; | ||
1132 | hdmi->entity.ops = &sh_hdmi_ops; | ||
1261 | 1133 | ||
1262 | hdmi->hdmi_clk = clk_get(&pdev->dev, "ick"); | 1134 | hdmi->hdmi_clk = clk_get(&pdev->dev, "ick"); |
1263 | if (IS_ERR(hdmi->hdmi_clk)) { | 1135 | if (IS_ERR(hdmi->hdmi_clk)) { |
@@ -1297,14 +1169,7 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) | |||
1297 | goto emap; | 1169 | goto emap; |
1298 | } | 1170 | } |
1299 | 1171 | ||
1300 | platform_set_drvdata(pdev, hdmi); | 1172 | platform_set_drvdata(pdev, &hdmi->entity); |
1301 | |||
1302 | /* Set up LCDC callbacks */ | ||
1303 | board_cfg = &pdata->lcd_chan->board_cfg; | ||
1304 | board_cfg->owner = THIS_MODULE; | ||
1305 | board_cfg->board_data = hdmi; | ||
1306 | board_cfg->display_on = sh_hdmi_display_on; | ||
1307 | board_cfg->display_off = sh_hdmi_display_off; | ||
1308 | 1173 | ||
1309 | INIT_DELAYED_WORK(&hdmi->edid_work, sh_hdmi_edid_work_fn); | 1174 | INIT_DELAYED_WORK(&hdmi->edid_work, sh_hdmi_edid_work_fn); |
1310 | 1175 | ||
@@ -1329,9 +1194,6 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) | |||
1329 | goto ecodec; | 1194 | goto ecodec; |
1330 | } | 1195 | } |
1331 | 1196 | ||
1332 | hdmi->notifier.notifier_call = sh_hdmi_notify; | ||
1333 | fb_register_client(&hdmi->notifier); | ||
1334 | |||
1335 | return 0; | 1197 | return 0; |
1336 | 1198 | ||
1337 | ecodec: | 1199 | ecodec: |
@@ -1347,7 +1209,6 @@ ereqreg: | |||
1347 | erate: | 1209 | erate: |
1348 | clk_put(hdmi->hdmi_clk); | 1210 | clk_put(hdmi->hdmi_clk); |
1349 | egetclk: | 1211 | egetclk: |
1350 | mutex_destroy(&hdmi->mutex); | ||
1351 | kfree(hdmi); | 1212 | kfree(hdmi); |
1352 | 1213 | ||
1353 | return ret; | 1214 | return ret; |
@@ -1355,21 +1216,12 @@ egetclk: | |||
1355 | 1216 | ||
1356 | static int __exit sh_hdmi_remove(struct platform_device *pdev) | 1217 | static int __exit sh_hdmi_remove(struct platform_device *pdev) |
1357 | { | 1218 | { |
1358 | struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; | 1219 | struct sh_hdmi *hdmi = entity_to_sh_hdmi(platform_get_drvdata(pdev)); |
1359 | struct sh_hdmi *hdmi = platform_get_drvdata(pdev); | ||
1360 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 1220 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
1361 | struct sh_mobile_lcdc_board_cfg *board_cfg = &pdata->lcd_chan->board_cfg; | ||
1362 | int irq = platform_get_irq(pdev, 0); | 1221 | int irq = platform_get_irq(pdev, 0); |
1363 | 1222 | ||
1364 | snd_soc_unregister_codec(&pdev->dev); | 1223 | snd_soc_unregister_codec(&pdev->dev); |
1365 | 1224 | ||
1366 | fb_unregister_client(&hdmi->notifier); | ||
1367 | |||
1368 | board_cfg->display_on = NULL; | ||
1369 | board_cfg->display_off = NULL; | ||
1370 | board_cfg->board_data = NULL; | ||
1371 | board_cfg->owner = NULL; | ||
1372 | |||
1373 | /* No new work will be scheduled, wait for running ISR */ | 1225 | /* No new work will be scheduled, wait for running ISR */ |
1374 | free_irq(irq, hdmi); | 1226 | free_irq(irq, hdmi); |
1375 | /* Wait for already scheduled work */ | 1227 | /* Wait for already scheduled work */ |
@@ -1380,7 +1232,6 @@ static int __exit sh_hdmi_remove(struct platform_device *pdev) | |||
1380 | clk_put(hdmi->hdmi_clk); | 1232 | clk_put(hdmi->hdmi_clk); |
1381 | iounmap(hdmi->base); | 1233 | iounmap(hdmi->base); |
1382 | release_mem_region(res->start, resource_size(res)); | 1234 | release_mem_region(res->start, resource_size(res)); |
1383 | mutex_destroy(&hdmi->mutex); | ||
1384 | kfree(hdmi); | 1235 | kfree(hdmi); |
1385 | 1236 | ||
1386 | return 0; | 1237 | return 0; |