diff options
Diffstat (limited to 'drivers/video/sh_mobile_hdmi.c')
-rw-r--r-- | drivers/video/sh_mobile_hdmi.c | 97 |
1 files changed, 59 insertions, 38 deletions
diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c index e7594e145c9..74d9f546a2e 100644 --- a/drivers/video/sh_mobile_hdmi.c +++ b/drivers/video/sh_mobile_hdmi.c | |||
@@ -222,6 +222,7 @@ struct sh_hdmi { | |||
222 | struct delayed_work edid_work; | 222 | struct delayed_work edid_work; |
223 | struct fb_var_screeninfo var; | 223 | struct fb_var_screeninfo var; |
224 | struct fb_monspecs monspec; | 224 | struct fb_monspecs monspec; |
225 | struct notifier_block notifier; | ||
225 | }; | 226 | }; |
226 | 227 | ||
227 | static void hdmi_write(struct sh_hdmi *hdmi, u8 data, u8 reg) | 228 | static void hdmi_write(struct sh_hdmi *hdmi, u8 data, u8 reg) |
@@ -738,7 +739,7 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, | |||
738 | struct fb_modelist *modelist = NULL; | 739 | struct fb_modelist *modelist = NULL; |
739 | unsigned int f_width = 0, f_height = 0, f_refresh = 0; | 740 | unsigned int f_width = 0, f_height = 0, f_refresh = 0; |
740 | unsigned long found_rate_error = ULONG_MAX; /* silly compiler... */ | 741 | unsigned long found_rate_error = ULONG_MAX; /* silly compiler... */ |
741 | bool exact_match = false; | 742 | bool scanning = false, preferred_bad = false; |
742 | u8 edid[128]; | 743 | u8 edid[128]; |
743 | char *forced; | 744 | char *forced; |
744 | int i; | 745 | int i; |
@@ -801,6 +802,9 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, | |||
801 | if (i < 2) { | 802 | if (i < 2) { |
802 | f_width = 0; | 803 | f_width = 0; |
803 | f_height = 0; | 804 | f_height = 0; |
805 | } else { | ||
806 | /* The user wants us to use the EDID data */ | ||
807 | scanning = true; | ||
804 | } | 808 | } |
805 | dev_dbg(hdmi->dev, "Forced mode %ux%u@%uHz\n", | 809 | dev_dbg(hdmi->dev, "Forced mode %ux%u@%uHz\n", |
806 | f_width, f_height, f_refresh); | 810 | f_width, f_height, f_refresh); |
@@ -808,37 +812,56 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, | |||
808 | 812 | ||
809 | /* Walk monitor modes to find the best or the exact match */ | 813 | /* Walk monitor modes to find the best or the exact match */ |
810 | for (i = 0, mode = hdmi->monspec.modedb; | 814 | for (i = 0, mode = hdmi->monspec.modedb; |
811 | f_width && f_height && i < hdmi->monspec.modedb_len && !exact_match; | 815 | i < hdmi->monspec.modedb_len && scanning; |
812 | i++, mode++) { | 816 | i++, mode++) { |
813 | unsigned long rate_error; | 817 | unsigned long rate_error; |
814 | 818 | ||
815 | /* No interest in unmatching modes */ | 819 | if (!f_width && !f_height) { |
816 | if (f_width != mode->xres || f_height != mode->yres) | 820 | /* |
821 | * A parameter string "video=sh_mobile_lcdc:0x0" means | ||
822 | * use the preferred EDID mode. If it is rejected by | ||
823 | * .fb_check_var(), keep looking, until an acceptable | ||
824 | * one is found. | ||
825 | */ | ||
826 | if ((mode->flag & FB_MODE_IS_FIRST) || preferred_bad) | ||
827 | scanning = false; | ||
828 | else | ||
829 | continue; | ||
830 | } else if (f_width != mode->xres || f_height != mode->yres) { | ||
831 | /* No interest in unmatching modes */ | ||
817 | continue; | 832 | continue; |
833 | } | ||
818 | 834 | ||
819 | rate_error = sh_hdmi_rate_error(hdmi, mode, hdmi_rate, parent_rate); | 835 | rate_error = sh_hdmi_rate_error(hdmi, mode, hdmi_rate, parent_rate); |
820 | 836 | ||
821 | if (f_refresh == mode->refresh || (!f_refresh && !rate_error)) | 837 | if (scanning) { |
822 | /* | 838 | if (f_refresh == mode->refresh || (!f_refresh && !rate_error)) |
823 | * Exact match if either the refresh rate matches or it | 839 | /* |
824 | * hasn't been specified and we've found a mode, for | 840 | * Exact match if either the refresh rate |
825 | * which we can configure the clock precisely | 841 | * matches or it hasn't been specified and we've |
826 | */ | 842 | * found a mode, for which we can configure the |
827 | exact_match = true; | 843 | * clock precisely |
828 | else if (found && found_rate_error <= rate_error) | 844 | */ |
829 | /* | 845 | scanning = false; |
830 | * We otherwise search for the closest matching clock | 846 | else if (found && found_rate_error <= rate_error) |
831 | * rate - either if no refresh rate has been specified | 847 | /* |
832 | * or we cannot find an exactly matching one | 848 | * We otherwise search for the closest matching |
833 | */ | 849 | * clock rate - either if no refresh rate has |
834 | continue; | 850 | * been specified or we cannot find an exactly |
851 | * matching one | ||
852 | */ | ||
853 | continue; | ||
854 | } | ||
835 | 855 | ||
836 | /* Check if supported: sufficient fb memory, supported clock-rate */ | 856 | /* Check if supported: sufficient fb memory, supported clock-rate */ |
837 | fb_videomode_to_var(var, mode); | 857 | fb_videomode_to_var(var, mode); |
838 | 858 | ||
859 | var->bits_per_pixel = info->var.bits_per_pixel; | ||
860 | |||
839 | if (info && info->fbops->fb_check_var && | 861 | if (info && info->fbops->fb_check_var && |
840 | info->fbops->fb_check_var(var, info)) { | 862 | info->fbops->fb_check_var(var, info)) { |
841 | exact_match = false; | 863 | scanning = true; |
864 | preferred_bad = true; | ||
842 | continue; | 865 | continue; |
843 | } | 866 | } |
844 | 867 | ||
@@ -856,9 +879,9 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, | |||
856 | * driver, and passing ->info with HDMI platform data. | 879 | * driver, and passing ->info with HDMI platform data. |
857 | */ | 880 | */ |
858 | if (info && !found) { | 881 | if (info && !found) { |
859 | modelist = hdmi->info->modelist.next && | 882 | modelist = info->modelist.next && |
860 | !list_empty(&hdmi->info->modelist) ? | 883 | !list_empty(&info->modelist) ? |
861 | list_entry(hdmi->info->modelist.next, | 884 | list_entry(info->modelist.next, |
862 | struct fb_modelist, list) : | 885 | struct fb_modelist, list) : |
863 | NULL; | 886 | NULL; |
864 | 887 | ||
@@ -1101,6 +1124,7 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work) | |||
1101 | mutex_lock(&hdmi->mutex); | 1124 | mutex_lock(&hdmi->mutex); |
1102 | 1125 | ||
1103 | if (hdmi->hp_state == HDMI_HOTPLUG_CONNECTED) { | 1126 | if (hdmi->hp_state == HDMI_HOTPLUG_CONNECTED) { |
1127 | struct fb_info *info = hdmi->info; | ||
1104 | unsigned long parent_rate = 0, hdmi_rate; | 1128 | unsigned long parent_rate = 0, hdmi_rate; |
1105 | 1129 | ||
1106 | /* A device has been plugged in */ | 1130 | /* A device has been plugged in */ |
@@ -1122,22 +1146,21 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work) | |||
1122 | /* Switched to another (d) power-save mode */ | 1146 | /* Switched to another (d) power-save mode */ |
1123 | msleep(10); | 1147 | msleep(10); |
1124 | 1148 | ||
1125 | if (!hdmi->info) | 1149 | if (!info) |
1126 | goto out; | 1150 | goto out; |
1127 | 1151 | ||
1128 | ch = hdmi->info->par; | 1152 | ch = info->par; |
1129 | 1153 | ||
1130 | acquire_console_sem(); | 1154 | acquire_console_sem(); |
1131 | 1155 | ||
1132 | /* HDMI plug in */ | 1156 | /* HDMI plug in */ |
1133 | if (!sh_hdmi_must_reconfigure(hdmi) && | 1157 | if (!sh_hdmi_must_reconfigure(hdmi) && |
1134 | hdmi->info->state == FBINFO_STATE_RUNNING) { | 1158 | info->state == FBINFO_STATE_RUNNING) { |
1135 | /* | 1159 | /* |
1136 | * First activation with the default monitor - just turn | 1160 | * First activation with the default monitor - just turn |
1137 | * on, if we run a resume here, the logo disappears | 1161 | * on, if we run a resume here, the logo disappears |
1138 | */ | 1162 | */ |
1139 | if (lock_fb_info(hdmi->info)) { | 1163 | if (lock_fb_info(info)) { |
1140 | struct fb_info *info = hdmi->info; | ||
1141 | info->var.width = hdmi->var.width; | 1164 | info->var.width = hdmi->var.width; |
1142 | info->var.height = hdmi->var.height; | 1165 | info->var.height = hdmi->var.height; |
1143 | sh_hdmi_display_on(hdmi, info); | 1166 | sh_hdmi_display_on(hdmi, info); |
@@ -1145,7 +1168,7 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work) | |||
1145 | } | 1168 | } |
1146 | } else { | 1169 | } else { |
1147 | /* New monitor or have to wake up */ | 1170 | /* New monitor or have to wake up */ |
1148 | fb_set_suspend(hdmi->info, 0); | 1171 | fb_set_suspend(info, 0); |
1149 | } | 1172 | } |
1150 | 1173 | ||
1151 | release_console_sem(); | 1174 | release_console_sem(); |
@@ -1176,13 +1199,6 @@ out: | |||
1176 | } | 1199 | } |
1177 | 1200 | ||
1178 | static int sh_hdmi_notify(struct notifier_block *nb, | 1201 | static int sh_hdmi_notify(struct notifier_block *nb, |
1179 | unsigned long action, void *data); | ||
1180 | |||
1181 | static struct notifier_block sh_hdmi_notifier = { | ||
1182 | .notifier_call = sh_hdmi_notify, | ||
1183 | }; | ||
1184 | |||
1185 | static int sh_hdmi_notify(struct notifier_block *nb, | ||
1186 | unsigned long action, void *data) | 1202 | unsigned long action, void *data) |
1187 | { | 1203 | { |
1188 | struct fb_event *event = data; | 1204 | struct fb_event *event = data; |
@@ -1191,7 +1207,7 @@ static int sh_hdmi_notify(struct notifier_block *nb, | |||
1191 | struct sh_mobile_lcdc_board_cfg *board_cfg = &ch->cfg.board_cfg; | 1207 | struct sh_mobile_lcdc_board_cfg *board_cfg = &ch->cfg.board_cfg; |
1192 | struct sh_hdmi *hdmi = board_cfg->board_data; | 1208 | struct sh_hdmi *hdmi = board_cfg->board_data; |
1193 | 1209 | ||
1194 | if (nb != &sh_hdmi_notifier || !hdmi || hdmi->info != info) | 1210 | if (!hdmi || nb != &hdmi->notifier || hdmi->info != info) |
1195 | return NOTIFY_DONE; | 1211 | return NOTIFY_DONE; |
1196 | 1212 | ||
1197 | switch(action) { | 1213 | switch(action) { |
@@ -1210,11 +1226,11 @@ static int sh_hdmi_notify(struct notifier_block *nb, | |||
1210 | * temporarily, synchronise with the work queue and re-acquire | 1226 | * temporarily, synchronise with the work queue and re-acquire |
1211 | * the info->lock. | 1227 | * the info->lock. |
1212 | */ | 1228 | */ |
1213 | unlock_fb_info(hdmi->info); | 1229 | unlock_fb_info(info); |
1214 | mutex_lock(&hdmi->mutex); | 1230 | mutex_lock(&hdmi->mutex); |
1215 | hdmi->info = NULL; | 1231 | hdmi->info = NULL; |
1216 | mutex_unlock(&hdmi->mutex); | 1232 | mutex_unlock(&hdmi->mutex); |
1217 | lock_fb_info(hdmi->info); | 1233 | lock_fb_info(info); |
1218 | return NOTIFY_OK; | 1234 | return NOTIFY_OK; |
1219 | } | 1235 | } |
1220 | return NOTIFY_DONE; | 1236 | return NOTIFY_DONE; |
@@ -1312,6 +1328,9 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) | |||
1312 | goto ecodec; | 1328 | goto ecodec; |
1313 | } | 1329 | } |
1314 | 1330 | ||
1331 | hdmi->notifier.notifier_call = sh_hdmi_notify; | ||
1332 | fb_register_client(&hdmi->notifier); | ||
1333 | |||
1315 | return 0; | 1334 | return 0; |
1316 | 1335 | ||
1317 | ecodec: | 1336 | ecodec: |
@@ -1342,6 +1361,8 @@ static int __exit sh_hdmi_remove(struct platform_device *pdev) | |||
1342 | 1361 | ||
1343 | snd_soc_unregister_codec(&pdev->dev); | 1362 | snd_soc_unregister_codec(&pdev->dev); |
1344 | 1363 | ||
1364 | fb_unregister_client(&hdmi->notifier); | ||
1365 | |||
1345 | board_cfg->display_on = NULL; | 1366 | board_cfg->display_on = NULL; |
1346 | board_cfg->display_off = NULL; | 1367 | board_cfg->display_off = NULL; |
1347 | board_cfg->board_data = NULL; | 1368 | board_cfg->board_data = NULL; |