diff options
Diffstat (limited to 'drivers/video/sh_mobile_hdmi.c')
-rw-r--r-- | drivers/video/sh_mobile_hdmi.c | 849 |
1 files changed, 613 insertions, 236 deletions
diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c index 2fde08cc66bf..7d54e2c612f7 100644 --- a/drivers/video/sh_mobile_hdmi.c +++ b/drivers/video/sh_mobile_hdmi.c | |||
@@ -22,10 +22,15 @@ | |||
22 | #include <linux/slab.h> | 22 | #include <linux/slab.h> |
23 | #include <linux/types.h> | 23 | #include <linux/types.h> |
24 | #include <linux/workqueue.h> | 24 | #include <linux/workqueue.h> |
25 | #include <sound/soc.h> | ||
26 | #include <sound/soc-dapm.h> | ||
27 | #include <sound/initval.h> | ||
25 | 28 | ||
26 | #include <video/sh_mobile_hdmi.h> | 29 | #include <video/sh_mobile_hdmi.h> |
27 | #include <video/sh_mobile_lcdc.h> | 30 | #include <video/sh_mobile_lcdc.h> |
28 | 31 | ||
32 | #include "sh_mobile_lcdcfb.h" | ||
33 | |||
29 | #define HDMI_SYSTEM_CTRL 0x00 /* System control */ | 34 | #define HDMI_SYSTEM_CTRL 0x00 /* System control */ |
30 | #define HDMI_L_R_DATA_SWAP_CTRL_RPKT 0x01 /* L/R data swap control, | 35 | #define HDMI_L_R_DATA_SWAP_CTRL_RPKT 0x01 /* L/R data swap control, |
31 | bits 19..16 of 20-bit N for Audio Clock Regeneration packet */ | 36 | bits 19..16 of 20-bit N for Audio Clock Regeneration packet */ |
@@ -204,12 +209,20 @@ enum hotplug_state { | |||
204 | 209 | ||
205 | struct sh_hdmi { | 210 | struct sh_hdmi { |
206 | void __iomem *base; | 211 | void __iomem *base; |
207 | enum hotplug_state hp_state; | 212 | enum hotplug_state hp_state; /* hot-plug status */ |
213 | u8 preprogrammed_vic; /* use a pre-programmed VIC or | ||
214 | the external mode */ | ||
215 | u8 edid_block_addr; | ||
216 | u8 edid_segment_nr; | ||
217 | u8 edid_blocks; | ||
208 | struct clk *hdmi_clk; | 218 | struct clk *hdmi_clk; |
209 | struct device *dev; | 219 | struct device *dev; |
210 | struct fb_info *info; | 220 | struct fb_info *info; |
221 | struct mutex mutex; /* Protect the info pointer */ | ||
211 | struct delayed_work edid_work; | 222 | struct delayed_work edid_work; |
212 | struct fb_var_screeninfo var; | 223 | struct fb_var_screeninfo var; |
224 | struct fb_monspecs monspec; | ||
225 | struct notifier_block notifier; | ||
213 | }; | 226 | }; |
214 | 227 | ||
215 | 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) |
@@ -222,8 +235,60 @@ static u8 hdmi_read(struct sh_hdmi *hdmi, u8 reg) | |||
222 | return ioread8(hdmi->base + reg); | 235 | return ioread8(hdmi->base + reg); |
223 | } | 236 | } |
224 | 237 | ||
238 | /* | ||
239 | * HDMI sound | ||
240 | */ | ||
241 | static unsigned int sh_hdmi_snd_read(struct snd_soc_codec *codec, | ||
242 | unsigned int reg) | ||
243 | { | ||
244 | struct sh_hdmi *hdmi = snd_soc_codec_get_drvdata(codec); | ||
245 | |||
246 | return hdmi_read(hdmi, reg); | ||
247 | } | ||
248 | |||
249 | static int sh_hdmi_snd_write(struct snd_soc_codec *codec, | ||
250 | unsigned int reg, | ||
251 | unsigned int value) | ||
252 | { | ||
253 | struct sh_hdmi *hdmi = snd_soc_codec_get_drvdata(codec); | ||
254 | |||
255 | hdmi_write(hdmi, value, reg); | ||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | static struct snd_soc_dai_driver sh_hdmi_dai = { | ||
260 | .name = "sh_mobile_hdmi-hifi", | ||
261 | .playback = { | ||
262 | .stream_name = "Playback", | ||
263 | .channels_min = 2, | ||
264 | .channels_max = 8, | ||
265 | .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | | ||
266 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | | ||
267 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | | ||
268 | SNDRV_PCM_RATE_192000, | ||
269 | .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, | ||
270 | }, | ||
271 | }; | ||
272 | |||
273 | static int sh_hdmi_snd_probe(struct snd_soc_codec *codec) | ||
274 | { | ||
275 | dev_info(codec->dev, "SH Mobile HDMI Audio Codec"); | ||
276 | |||
277 | return 0; | ||
278 | } | ||
279 | |||
280 | static struct snd_soc_codec_driver soc_codec_dev_sh_hdmi = { | ||
281 | .probe = sh_hdmi_snd_probe, | ||
282 | .read = sh_hdmi_snd_read, | ||
283 | .write = sh_hdmi_snd_write, | ||
284 | }; | ||
285 | |||
286 | /* | ||
287 | * HDMI video | ||
288 | */ | ||
289 | |||
225 | /* External video parameter settings */ | 290 | /* External video parameter settings */ |
226 | static void hdmi_external_video_param(struct sh_hdmi *hdmi) | 291 | static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi) |
227 | { | 292 | { |
228 | struct fb_var_screeninfo *var = &hdmi->var; | 293 | struct fb_var_screeninfo *var = &hdmi->var; |
229 | u16 htotal, hblank, hdelay, vtotal, vblank, vdelay, voffset; | 294 | u16 htotal, hblank, hdelay, vtotal, vblank, vdelay, voffset; |
@@ -255,9 +320,9 @@ static void hdmi_external_video_param(struct sh_hdmi *hdmi) | |||
255 | if (var->sync & FB_SYNC_VERT_HIGH_ACT) | 320 | if (var->sync & FB_SYNC_VERT_HIGH_ACT) |
256 | sync |= 8; | 321 | sync |= 8; |
257 | 322 | ||
258 | pr_debug("H: %u, %u, %u, %u; V: %u, %u, %u, %u; sync 0x%x\n", | 323 | dev_dbg(hdmi->dev, "H: %u, %u, %u, %u; V: %u, %u, %u, %u; sync 0x%x\n", |
259 | htotal, hblank, hdelay, var->hsync_len, | 324 | htotal, hblank, hdelay, var->hsync_len, |
260 | vtotal, vblank, vdelay, var->vsync_len, sync); | 325 | vtotal, vblank, vdelay, var->vsync_len, sync); |
261 | 326 | ||
262 | hdmi_write(hdmi, sync | (voffset << 4), HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS); | 327 | hdmi_write(hdmi, sync | (voffset << 4), HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS); |
263 | 328 | ||
@@ -282,7 +347,10 @@ static void hdmi_external_video_param(struct sh_hdmi *hdmi) | |||
282 | 347 | ||
283 | hdmi_write(hdmi, var->vsync_len, HDMI_EXTERNAL_V_DURATION); | 348 | hdmi_write(hdmi, var->vsync_len, HDMI_EXTERNAL_V_DURATION); |
284 | 349 | ||
285 | /* Set bit 0 of HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS here for manual mode */ | 350 | /* Set bit 0 of HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS here for external mode */ |
351 | if (!hdmi->preprogrammed_vic) | ||
352 | hdmi_write(hdmi, sync | 1 | (voffset << 4), | ||
353 | HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS); | ||
286 | } | 354 | } |
287 | 355 | ||
288 | /** | 356 | /** |
@@ -318,6 +386,9 @@ static void sh_hdmi_video_config(struct sh_hdmi *hdmi) | |||
318 | */ | 386 | */ |
319 | static void sh_hdmi_audio_config(struct sh_hdmi *hdmi) | 387 | static void sh_hdmi_audio_config(struct sh_hdmi *hdmi) |
320 | { | 388 | { |
389 | u8 data; | ||
390 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; | ||
391 | |||
321 | /* | 392 | /* |
322 | * [7:4] L/R data swap control | 393 | * [7:4] L/R data swap control |
323 | * [3:0] appropriate N[19:16] | 394 | * [3:0] appropriate N[19:16] |
@@ -335,7 +406,23 @@ static void sh_hdmi_audio_config(struct sh_hdmi *hdmi) | |||
335 | * [6:5] set required down sampling rate if required | 406 | * [6:5] set required down sampling rate if required |
336 | * [4:3] set required audio source | 407 | * [4:3] set required audio source |
337 | */ | 408 | */ |
338 | hdmi_write(hdmi, 0x00, HDMI_AUDIO_SETTING_1); | 409 | switch (pdata->flags & HDMI_SND_SRC_MASK) { |
410 | default: | ||
411 | /* fall through */ | ||
412 | case HDMI_SND_SRC_I2S: | ||
413 | data = 0x0 << 3; | ||
414 | break; | ||
415 | case HDMI_SND_SRC_SPDIF: | ||
416 | data = 0x1 << 3; | ||
417 | break; | ||
418 | case HDMI_SND_SRC_DSD: | ||
419 | data = 0x2 << 3; | ||
420 | break; | ||
421 | case HDMI_SND_SRC_HBR: | ||
422 | data = 0x3 << 3; | ||
423 | break; | ||
424 | } | ||
425 | hdmi_write(hdmi, data, HDMI_AUDIO_SETTING_1); | ||
339 | 426 | ||
340 | /* [3:0] set sending channel number for channel status */ | 427 | /* [3:0] set sending channel number for channel status */ |
341 | hdmi_write(hdmi, 0x40, HDMI_AUDIO_SETTING_2); | 428 | hdmi_write(hdmi, 0x40, HDMI_AUDIO_SETTING_2); |
@@ -381,21 +468,72 @@ static void sh_hdmi_audio_config(struct sh_hdmi *hdmi) | |||
381 | } | 468 | } |
382 | 469 | ||
383 | /** | 470 | /** |
384 | * sh_hdmi_phy_config() | 471 | * sh_hdmi_phy_config() - configure the HDMI PHY for the used video mode |
385 | */ | 472 | */ |
386 | static void sh_hdmi_phy_config(struct sh_hdmi *hdmi) | 473 | static void sh_hdmi_phy_config(struct sh_hdmi *hdmi) |
387 | { | 474 | { |
388 | /* 720p, 8bit, 74.25MHz. Might need to be adjusted for other formats */ | 475 | if (hdmi->var.pixclock < 10000) { |
389 | hdmi_write(hdmi, 0x19, HDMI_SLIPHDMIT_PARAM_SETTINGS_1); | 476 | /* for 1080p8bit 148MHz */ |
390 | hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_2); | 477 | hdmi_write(hdmi, 0x1d, HDMI_SLIPHDMIT_PARAM_SETTINGS_1); |
391 | hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_3); | 478 | hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_2); |
392 | /* PLLA_CONFIG[7:0]: VCO gain, VCO offset, LPF resistance[0] */ | 479 | hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_3); |
393 | hdmi_write(hdmi, 0x44, HDMI_SLIPHDMIT_PARAM_SETTINGS_5); | 480 | hdmi_write(hdmi, 0x4c, HDMI_SLIPHDMIT_PARAM_SETTINGS_5); |
394 | hdmi_write(hdmi, 0x32, HDMI_SLIPHDMIT_PARAM_SETTINGS_6); | 481 | hdmi_write(hdmi, 0x1e, HDMI_SLIPHDMIT_PARAM_SETTINGS_6); |
395 | hdmi_write(hdmi, 0x4A, HDMI_SLIPHDMIT_PARAM_SETTINGS_7); | 482 | hdmi_write(hdmi, 0x48, HDMI_SLIPHDMIT_PARAM_SETTINGS_7); |
396 | hdmi_write(hdmi, 0x0E, HDMI_SLIPHDMIT_PARAM_SETTINGS_8); | 483 | hdmi_write(hdmi, 0x0e, HDMI_SLIPHDMIT_PARAM_SETTINGS_8); |
397 | hdmi_write(hdmi, 0x25, HDMI_SLIPHDMIT_PARAM_SETTINGS_9); | 484 | hdmi_write(hdmi, 0x25, HDMI_SLIPHDMIT_PARAM_SETTINGS_9); |
398 | hdmi_write(hdmi, 0x04, HDMI_SLIPHDMIT_PARAM_SETTINGS_10); | 485 | hdmi_write(hdmi, 0x04, HDMI_SLIPHDMIT_PARAM_SETTINGS_10); |
486 | } else if (hdmi->var.pixclock < 30000) { | ||
487 | /* 720p, 8bit, 74.25MHz. Might need to be adjusted for other formats */ | ||
488 | /* | ||
489 | * [1:0] Speed_A | ||
490 | * [3:2] Speed_B | ||
491 | * [4] PLLA_Bypass | ||
492 | * [6] DRV_TEST_EN | ||
493 | * [7] DRV_TEST_IN | ||
494 | */ | ||
495 | hdmi_write(hdmi, 0x0f, HDMI_SLIPHDMIT_PARAM_SETTINGS_1); | ||
496 | /* PLLB_CONFIG[17], PLLA_CONFIG[17] - not in PHY datasheet */ | ||
497 | hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_2); | ||
498 | /* | ||
499 | * [2:0] BGR_I_OFFSET | ||
500 | * [6:4] BGR_V_OFFSET | ||
501 | */ | ||
502 | hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_3); | ||
503 | /* PLLA_CONFIG[7:0]: VCO gain, VCO offset, LPF resistance[0] */ | ||
504 | hdmi_write(hdmi, 0x44, HDMI_SLIPHDMIT_PARAM_SETTINGS_5); | ||
505 | /* | ||
506 | * PLLA_CONFIG[15:8]: regulator voltage[0], CP current, | ||
507 | * LPF capacitance, LPF resistance[1] | ||
508 | */ | ||
509 | hdmi_write(hdmi, 0x32, HDMI_SLIPHDMIT_PARAM_SETTINGS_6); | ||
510 | /* PLLB_CONFIG[7:0]: LPF resistance[0], VCO offset, VCO gain */ | ||
511 | hdmi_write(hdmi, 0x4A, HDMI_SLIPHDMIT_PARAM_SETTINGS_7); | ||
512 | /* | ||
513 | * PLLB_CONFIG[15:8]: regulator voltage[0], CP current, | ||
514 | * LPF capacitance, LPF resistance[1] | ||
515 | */ | ||
516 | hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_8); | ||
517 | /* DRV_CONFIG, PE_CONFIG */ | ||
518 | hdmi_write(hdmi, 0x25, HDMI_SLIPHDMIT_PARAM_SETTINGS_9); | ||
519 | /* | ||
520 | * [2:0] AMON_SEL (4 == LPF voltage) | ||
521 | * [4] PLLA_CONFIG[16] | ||
522 | * [5] PLLB_CONFIG[16] | ||
523 | */ | ||
524 | hdmi_write(hdmi, 0x04, HDMI_SLIPHDMIT_PARAM_SETTINGS_10); | ||
525 | } else { | ||
526 | /* for 480p8bit 27MHz */ | ||
527 | hdmi_write(hdmi, 0x19, HDMI_SLIPHDMIT_PARAM_SETTINGS_1); | ||
528 | hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_2); | ||
529 | hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_3); | ||
530 | hdmi_write(hdmi, 0x44, HDMI_SLIPHDMIT_PARAM_SETTINGS_5); | ||
531 | hdmi_write(hdmi, 0x32, HDMI_SLIPHDMIT_PARAM_SETTINGS_6); | ||
532 | hdmi_write(hdmi, 0x48, HDMI_SLIPHDMIT_PARAM_SETTINGS_7); | ||
533 | hdmi_write(hdmi, 0x0F, HDMI_SLIPHDMIT_PARAM_SETTINGS_8); | ||
534 | hdmi_write(hdmi, 0x20, HDMI_SLIPHDMIT_PARAM_SETTINGS_9); | ||
535 | hdmi_write(hdmi, 0x04, HDMI_SLIPHDMIT_PARAM_SETTINGS_10); | ||
536 | } | ||
399 | } | 537 | } |
400 | 538 | ||
401 | /** | 539 | /** |
@@ -403,6 +541,8 @@ static void sh_hdmi_phy_config(struct sh_hdmi *hdmi) | |||
403 | */ | 541 | */ |
404 | static void sh_hdmi_avi_infoframe_setup(struct sh_hdmi *hdmi) | 542 | static void sh_hdmi_avi_infoframe_setup(struct sh_hdmi *hdmi) |
405 | { | 543 | { |
544 | u8 vic; | ||
545 | |||
406 | /* AVI InfoFrame */ | 546 | /* AVI InfoFrame */ |
407 | hdmi_write(hdmi, 0x06, HDMI_CTRL_PKT_BUF_INDEX); | 547 | hdmi_write(hdmi, 0x06, HDMI_CTRL_PKT_BUF_INDEX); |
408 | 548 | ||
@@ -427,9 +567,9 @@ static void sh_hdmi_avi_infoframe_setup(struct sh_hdmi *hdmi) | |||
427 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB1); | 567 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB1); |
428 | 568 | ||
429 | /* | 569 | /* |
430 | * C = No Data | 570 | * [7:6] C = Colorimetry: no data |
431 | * M = 16:9 Picture Aspect Ratio | 571 | * [5:4] M = 2: 16:9, 1: 4:3 Picture Aspect Ratio |
432 | * R = Same as picture aspect ratio | 572 | * [3:0] R = 8: Active Frame Aspect Ratio: same as picture aspect ratio |
433 | */ | 573 | */ |
434 | hdmi_write(hdmi, 0x28, HDMI_CTRL_PKT_BUF_ACCESS_PB2); | 574 | hdmi_write(hdmi, 0x28, HDMI_CTRL_PKT_BUF_ACCESS_PB2); |
435 | 575 | ||
@@ -442,10 +582,14 @@ static void sh_hdmi_avi_infoframe_setup(struct sh_hdmi *hdmi) | |||
442 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB3); | 582 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB3); |
443 | 583 | ||
444 | /* | 584 | /* |
445 | * VIC = 1280 x 720p: ignored if external config is used | 585 | * VIC should be ignored if external config is used, so, we could just use 0, |
446 | * Send 2 for 720 x 480p, 16 for 1080p | 586 | * but play safe and use a valid value in any case just in case |
447 | */ | 587 | */ |
448 | hdmi_write(hdmi, 4, HDMI_CTRL_PKT_BUF_ACCESS_PB4); | 588 | if (hdmi->preprogrammed_vic) |
589 | vic = hdmi->preprogrammed_vic; | ||
590 | else | ||
591 | vic = 4; | ||
592 | hdmi_write(hdmi, vic, HDMI_CTRL_PKT_BUF_ACCESS_PB4); | ||
449 | 593 | ||
450 | /* PR = No Repetition */ | 594 | /* PR = No Repetition */ |
451 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB5); | 595 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB5); |
@@ -519,100 +663,6 @@ static void sh_hdmi_audio_infoframe_setup(struct sh_hdmi *hdmi) | |||
519 | } | 663 | } |
520 | 664 | ||
521 | /** | 665 | /** |
522 | * sh_hdmi_gamut_metadata_setup() - Gamut Metadata Packet of CONTROL PACKET | ||
523 | */ | ||
524 | static void sh_hdmi_gamut_metadata_setup(struct sh_hdmi *hdmi) | ||
525 | { | ||
526 | int i; | ||
527 | |||
528 | /* Gamut Metadata Packet */ | ||
529 | hdmi_write(hdmi, 0x04, HDMI_CTRL_PKT_BUF_INDEX); | ||
530 | |||
531 | /* Packet Type = 0x0A */ | ||
532 | hdmi_write(hdmi, 0x0A, HDMI_CTRL_PKT_BUF_ACCESS_HB0); | ||
533 | /* Gamut Packet is not used, so default value */ | ||
534 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1); | ||
535 | /* Gamut Packet is not used, so default value */ | ||
536 | hdmi_write(hdmi, 0x10, HDMI_CTRL_PKT_BUF_ACCESS_HB2); | ||
537 | |||
538 | /* GBD bytes 0 through 27 */ | ||
539 | for (i = 0; i <= 27; i++) | ||
540 | /* HDMI_CTRL_PKT_BUF_ACCESS_PB0_63H - PB27_7EH */ | ||
541 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i); | ||
542 | } | ||
543 | |||
544 | /** | ||
545 | * sh_hdmi_acp_setup() - Audio Content Protection Packet (ACP) | ||
546 | */ | ||
547 | static void sh_hdmi_acp_setup(struct sh_hdmi *hdmi) | ||
548 | { | ||
549 | int i; | ||
550 | |||
551 | /* Audio Content Protection Packet (ACP) */ | ||
552 | hdmi_write(hdmi, 0x01, HDMI_CTRL_PKT_BUF_INDEX); | ||
553 | |||
554 | /* Packet Type = 0x04 */ | ||
555 | hdmi_write(hdmi, 0x04, HDMI_CTRL_PKT_BUF_ACCESS_HB0); | ||
556 | /* ACP_Type */ | ||
557 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1); | ||
558 | /* Reserved (0) */ | ||
559 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB2); | ||
560 | |||
561 | /* GBD bytes 0 through 27 */ | ||
562 | for (i = 0; i <= 27; i++) | ||
563 | /* HDMI_CTRL_PKT_BUF_ACCESS_PB0 - PB27 */ | ||
564 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i); | ||
565 | } | ||
566 | |||
567 | /** | ||
568 | * sh_hdmi_isrc1_setup() - ISRC1 Packet | ||
569 | */ | ||
570 | static void sh_hdmi_isrc1_setup(struct sh_hdmi *hdmi) | ||
571 | { | ||
572 | int i; | ||
573 | |||
574 | /* ISRC1 Packet */ | ||
575 | hdmi_write(hdmi, 0x02, HDMI_CTRL_PKT_BUF_INDEX); | ||
576 | |||
577 | /* Packet Type = 0x05 */ | ||
578 | hdmi_write(hdmi, 0x05, HDMI_CTRL_PKT_BUF_ACCESS_HB0); | ||
579 | /* ISRC_Cont, ISRC_Valid, Reserved (0), ISRC_Status */ | ||
580 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1); | ||
581 | /* Reserved (0) */ | ||
582 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB2); | ||
583 | |||
584 | /* PB0 UPC_EAN_ISRC_0-15 */ | ||
585 | /* Bytes PB16-PB27 shall be set to a value of 0. */ | ||
586 | for (i = 0; i <= 27; i++) | ||
587 | /* HDMI_CTRL_PKT_BUF_ACCESS_PB0 - PB27 */ | ||
588 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i); | ||
589 | } | ||
590 | |||
591 | /** | ||
592 | * sh_hdmi_isrc2_setup() - ISRC2 Packet | ||
593 | */ | ||
594 | static void sh_hdmi_isrc2_setup(struct sh_hdmi *hdmi) | ||
595 | { | ||
596 | int i; | ||
597 | |||
598 | /* ISRC2 Packet */ | ||
599 | hdmi_write(hdmi, 0x03, HDMI_CTRL_PKT_BUF_INDEX); | ||
600 | |||
601 | /* HB0 Packet Type = 0x06 */ | ||
602 | hdmi_write(hdmi, 0x06, HDMI_CTRL_PKT_BUF_ACCESS_HB0); | ||
603 | /* Reserved (0) */ | ||
604 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1); | ||
605 | /* Reserved (0) */ | ||
606 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB2); | ||
607 | |||
608 | /* PB0 UPC_EAN_ISRC_16-31 */ | ||
609 | /* Bytes PB16-PB27 shall be set to a value of 0. */ | ||
610 | for (i = 0; i <= 27; i++) | ||
611 | /* HDMI_CTRL_PKT_BUF_ACCESS_PB0 - PB27 */ | ||
612 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i); | ||
613 | } | ||
614 | |||
615 | /** | ||
616 | * sh_hdmi_configure() - Initialise HDMI for output | 666 | * sh_hdmi_configure() - Initialise HDMI for output |
617 | */ | 667 | */ |
618 | static void sh_hdmi_configure(struct sh_hdmi *hdmi) | 668 | static void sh_hdmi_configure(struct sh_hdmi *hdmi) |
@@ -632,18 +682,6 @@ static void sh_hdmi_configure(struct sh_hdmi *hdmi) | |||
632 | /* Audio InfoFrame */ | 682 | /* Audio InfoFrame */ |
633 | sh_hdmi_audio_infoframe_setup(hdmi); | 683 | sh_hdmi_audio_infoframe_setup(hdmi); |
634 | 684 | ||
635 | /* Gamut Metadata packet */ | ||
636 | sh_hdmi_gamut_metadata_setup(hdmi); | ||
637 | |||
638 | /* Audio Content Protection (ACP) Packet */ | ||
639 | sh_hdmi_acp_setup(hdmi); | ||
640 | |||
641 | /* ISRC1 Packet */ | ||
642 | sh_hdmi_isrc1_setup(hdmi); | ||
643 | |||
644 | /* ISRC2 Packet */ | ||
645 | sh_hdmi_isrc2_setup(hdmi); | ||
646 | |||
647 | /* | 685 | /* |
648 | * Control packet auto send with VSYNC control: auto send | 686 | * Control packet auto send with VSYNC control: auto send |
649 | * General control, Gamut metadata, ISRC, and ACP packets | 687 | * General control, Gamut metadata, ISRC, and ACP packets |
@@ -661,17 +699,53 @@ static void sh_hdmi_configure(struct sh_hdmi *hdmi) | |||
661 | hdmi_write(hdmi, 0x40, HDMI_SYSTEM_CTRL); | 699 | hdmi_write(hdmi, 0x40, HDMI_SYSTEM_CTRL); |
662 | } | 700 | } |
663 | 701 | ||
664 | static void sh_hdmi_read_edid(struct sh_hdmi *hdmi) | 702 | static unsigned long sh_hdmi_rate_error(struct sh_hdmi *hdmi, |
703 | const struct fb_videomode *mode, | ||
704 | unsigned long *hdmi_rate, unsigned long *parent_rate) | ||
665 | { | 705 | { |
666 | struct fb_var_screeninfo *var = &hdmi->var; | 706 | unsigned long target = PICOS2KHZ(mode->pixclock) * 1000, rate_error; |
667 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; | 707 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; |
668 | struct fb_videomode *lcd_cfg = &pdata->lcd_chan->lcd_cfg; | 708 | |
669 | unsigned long height = var->height, width = var->width; | 709 | *hdmi_rate = clk_round_rate(hdmi->hdmi_clk, target); |
670 | int i; | 710 | if ((long)*hdmi_rate < 0) |
711 | *hdmi_rate = clk_get_rate(hdmi->hdmi_clk); | ||
712 | |||
713 | rate_error = (long)*hdmi_rate > 0 ? abs(*hdmi_rate - target) : ULONG_MAX; | ||
714 | if (rate_error && pdata->clk_optimize_parent) | ||
715 | rate_error = pdata->clk_optimize_parent(target, hdmi_rate, parent_rate); | ||
716 | else if (clk_get_parent(hdmi->hdmi_clk)) | ||
717 | *parent_rate = clk_get_rate(clk_get_parent(hdmi->hdmi_clk)); | ||
718 | |||
719 | dev_dbg(hdmi->dev, "%u-%u-%u-%u x %u-%u-%u-%u\n", | ||
720 | mode->left_margin, mode->xres, | ||
721 | mode->right_margin, mode->hsync_len, | ||
722 | mode->upper_margin, mode->yres, | ||
723 | mode->lower_margin, mode->vsync_len); | ||
724 | |||
725 | dev_dbg(hdmi->dev, "\t@%lu(+/-%lu)Hz, e=%lu / 1000, r=%uHz, p=%luHz\n", target, | ||
726 | rate_error, rate_error ? 10000 / (10 * target / rate_error) : 0, | ||
727 | mode->refresh, *parent_rate); | ||
728 | |||
729 | return rate_error; | ||
730 | } | ||
731 | |||
732 | static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, | ||
733 | unsigned long *parent_rate) | ||
734 | { | ||
735 | struct fb_var_screeninfo tmpvar; | ||
736 | struct fb_var_screeninfo *var = &tmpvar; | ||
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; | ||
741 | unsigned long found_rate_error = ULONG_MAX; /* silly compiler... */ | ||
742 | bool scanning = false, preferred_bad = false; | ||
671 | u8 edid[128]; | 743 | u8 edid[128]; |
744 | char *forced; | ||
745 | int i; | ||
672 | 746 | ||
673 | /* Read EDID */ | 747 | /* Read EDID */ |
674 | pr_debug("Read back EDID code:"); | 748 | dev_dbg(hdmi->dev, "Read back EDID code:"); |
675 | for (i = 0; i < 128; i++) { | 749 | for (i = 0; i < 128; i++) { |
676 | edid[i] = hdmi_read(hdmi, HDMI_EDID_KSV_FIFO_ACCESS_WINDOW); | 750 | edid[i] = hdmi_read(hdmi, HDMI_EDID_KSV_FIFO_ACCESS_WINDOW); |
677 | #ifdef DEBUG | 751 | #ifdef DEBUG |
@@ -686,29 +760,167 @@ static void sh_hdmi_read_edid(struct sh_hdmi *hdmi) | |||
686 | #ifdef DEBUG | 760 | #ifdef DEBUG |
687 | printk(KERN_CONT "\n"); | 761 | printk(KERN_CONT "\n"); |
688 | #endif | 762 | #endif |
689 | fb_parse_edid(edid, var); | 763 | |
690 | pr_debug("%u-%u-%u-%u x %u-%u-%u-%u @ %lu kHz monitor detected\n", | 764 | if (!hdmi->edid_blocks) { |
691 | var->left_margin, var->xres, var->right_margin, var->hsync_len, | 765 | fb_edid_to_monspecs(edid, &hdmi->monspec); |
692 | var->upper_margin, var->yres, var->lower_margin, var->vsync_len, | 766 | hdmi->edid_blocks = edid[126] + 1; |
693 | PICOS2KHZ(var->pixclock)); | 767 | |
694 | 768 | dev_dbg(hdmi->dev, "%d main modes, %d extension blocks\n", | |
695 | /* FIXME: Use user-provided configuration instead of EDID */ | 769 | hdmi->monspec.modedb_len, hdmi->edid_blocks - 1); |
696 | var->width = width; | 770 | } else { |
697 | var->xres = lcd_cfg->xres; | 771 | dev_dbg(hdmi->dev, "Extension %u detected, DTD start %u\n", |
698 | var->xres_virtual = lcd_cfg->xres; | 772 | edid[0], edid[2]); |
699 | var->left_margin = lcd_cfg->left_margin; | 773 | fb_edid_add_monspecs(edid, &hdmi->monspec); |
700 | var->right_margin = lcd_cfg->right_margin; | 774 | } |
701 | var->hsync_len = lcd_cfg->hsync_len; | 775 | |
702 | var->height = height; | 776 | if (hdmi->edid_blocks > hdmi->edid_segment_nr * 2 + |
703 | var->yres = lcd_cfg->yres; | 777 | (hdmi->edid_block_addr >> 7) + 1) { |
704 | var->yres_virtual = lcd_cfg->yres * 2; | 778 | /* More blocks to read */ |
705 | var->upper_margin = lcd_cfg->upper_margin; | 779 | if (hdmi->edid_block_addr) { |
706 | var->lower_margin = lcd_cfg->lower_margin; | 780 | hdmi->edid_block_addr = 0; |
707 | var->vsync_len = lcd_cfg->vsync_len; | 781 | hdmi->edid_segment_nr++; |
708 | var->sync = lcd_cfg->sync; | 782 | } else { |
709 | var->pixclock = lcd_cfg->pixclock; | 783 | hdmi->edid_block_addr = 0x80; |
710 | 784 | } | |
711 | hdmi_external_video_param(hdmi); | 785 | /* Set EDID word address */ |
786 | hdmi_write(hdmi, hdmi->edid_block_addr, HDMI_EDID_WORD_ADDRESS); | ||
787 | /* Enable EDID interrupt */ | ||
788 | hdmi_write(hdmi, 0xC6, HDMI_INTERRUPT_MASK_1); | ||
789 | /* Set EDID segment pointer - starts reading EDID */ | ||
790 | hdmi_write(hdmi, hdmi->edid_segment_nr, HDMI_EDID_SEGMENT_POINTER); | ||
791 | return -EAGAIN; | ||
792 | } | ||
793 | |||
794 | /* All E-EDID blocks ready */ | ||
795 | dev_dbg(hdmi->dev, "%d main and extended modes\n", hdmi->monspec.modedb_len); | ||
796 | |||
797 | fb_get_options("sh_mobile_lcdc", &forced); | ||
798 | if (forced && *forced) { | ||
799 | /* Only primitive parsing so far */ | ||
800 | i = sscanf(forced, "%ux%u@%u", | ||
801 | &f_width, &f_height, &f_refresh); | ||
802 | if (i < 2) { | ||
803 | f_width = 0; | ||
804 | f_height = 0; | ||
805 | } else { | ||
806 | /* The user wants us to use the EDID data */ | ||
807 | scanning = true; | ||
808 | } | ||
809 | dev_dbg(hdmi->dev, "Forced mode %ux%u@%uHz\n", | ||
810 | f_width, f_height, f_refresh); | ||
811 | } | ||
812 | |||
813 | /* Walk monitor modes to find the best or the exact match */ | ||
814 | for (i = 0, mode = hdmi->monspec.modedb; | ||
815 | i < hdmi->monspec.modedb_len && scanning; | ||
816 | i++, mode++) { | ||
817 | unsigned long rate_error; | ||
818 | |||
819 | if (!f_width && !f_height) { | ||
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 */ | ||
832 | continue; | ||
833 | } | ||
834 | |||
835 | rate_error = sh_hdmi_rate_error(hdmi, mode, hdmi_rate, parent_rate); | ||
836 | |||
837 | if (scanning) { | ||
838 | if (f_refresh == mode->refresh || (!f_refresh && !rate_error)) | ||
839 | /* | ||
840 | * Exact match if either the refresh rate | ||
841 | * matches or it hasn't been specified and we've | ||
842 | * found a mode, for which we can configure the | ||
843 | * clock precisely | ||
844 | */ | ||
845 | scanning = false; | ||
846 | else if (found && found_rate_error <= rate_error) | ||
847 | /* | ||
848 | * We otherwise search for the closest matching | ||
849 | * clock rate - either if no refresh rate has | ||
850 | * been specified or we cannot find an exactly | ||
851 | * matching one | ||
852 | */ | ||
853 | continue; | ||
854 | } | ||
855 | |||
856 | /* Check if supported: sufficient fb memory, supported clock-rate */ | ||
857 | fb_videomode_to_var(var, mode); | ||
858 | |||
859 | var->bits_per_pixel = info->var.bits_per_pixel; | ||
860 | |||
861 | if (info && info->fbops->fb_check_var && | ||
862 | info->fbops->fb_check_var(var, info)) { | ||
863 | scanning = true; | ||
864 | preferred_bad = true; | ||
865 | continue; | ||
866 | } | ||
867 | |||
868 | found = mode; | ||
869 | found_rate_error = rate_error; | ||
870 | } | ||
871 | |||
872 | hdmi->var.width = hdmi->monspec.max_x * 10; | ||
873 | hdmi->var.height = hdmi->monspec.max_y * 10; | ||
874 | |||
875 | /* | ||
876 | * TODO 1: if no ->info is present, postpone running the config until | ||
877 | * after ->info first gets registered. | ||
878 | * TODO 2: consider registering the HDMI platform device from the LCDC | ||
879 | * driver, and passing ->info with HDMI platform data. | ||
880 | */ | ||
881 | if (info && !found) { | ||
882 | modelist = info->modelist.next && | ||
883 | !list_empty(&info->modelist) ? | ||
884 | list_entry(info->modelist.next, | ||
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 | } | ||
893 | |||
894 | /* No cookie today */ | ||
895 | if (!found) | ||
896 | return -ENXIO; | ||
897 | |||
898 | if (found->xres == 640 && found->yres == 480 && found->refresh == 60) | ||
899 | hdmi->preprogrammed_vic = 1; | ||
900 | else if (found->xres == 720 && found->yres == 480 && found->refresh == 60) | ||
901 | hdmi->preprogrammed_vic = 2; | ||
902 | else if (found->xres == 720 && found->yres == 576 && found->refresh == 50) | ||
903 | hdmi->preprogrammed_vic = 17; | ||
904 | else if (found->xres == 1280 && found->yres == 720 && found->refresh == 60) | ||
905 | hdmi->preprogrammed_vic = 4; | ||
906 | else if (found->xres == 1920 && found->yres == 1080 && found->refresh == 24) | ||
907 | hdmi->preprogrammed_vic = 32; | ||
908 | else if (found->xres == 1920 && found->yres == 1080 && found->refresh == 50) | ||
909 | hdmi->preprogrammed_vic = 31; | ||
910 | else if (found->xres == 1920 && found->yres == 1080 && found->refresh == 60) | ||
911 | hdmi->preprogrammed_vic = 16; | ||
912 | else | ||
913 | hdmi->preprogrammed_vic = 0; | ||
914 | |||
915 | dev_dbg(hdmi->dev, "Using %s %s mode %ux%u@%uHz (%luHz), clock error %luHz\n", | ||
916 | modelist ? "default" : "EDID", hdmi->preprogrammed_vic ? "VIC" : "external", | ||
917 | found->xres, found->yres, found->refresh, | ||
918 | PICOS2KHZ(found->pixclock) * 1000, found_rate_error); | ||
919 | |||
920 | fb_videomode_to_var(&hdmi->var, found); | ||
921 | sh_hdmi_external_video_param(hdmi); | ||
922 | |||
923 | return 0; | ||
712 | } | 924 | } |
713 | 925 | ||
714 | static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id) | 926 | static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id) |
@@ -736,8 +948,8 @@ static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id) | |||
736 | hdmi_write(hdmi, 0xFF, HDMI_INTERRUPT_STATUS_2); | 948 | hdmi_write(hdmi, 0xFF, HDMI_INTERRUPT_STATUS_2); |
737 | 949 | ||
738 | if (printk_ratelimit()) | 950 | if (printk_ratelimit()) |
739 | pr_debug("IRQ #%d: Status #1: 0x%x & 0x%x, #2: 0x%x & 0x%x\n", | 951 | dev_dbg(hdmi->dev, "IRQ #%d: Status #1: 0x%x & 0x%x, #2: 0x%x & 0x%x\n", |
740 | irq, status1, mask1, status2, mask2); | 952 | irq, status1, mask1, status2, mask2); |
741 | 953 | ||
742 | if (!((status1 & mask1) | (status2 & mask2))) { | 954 | if (!((status1 & mask1) | (status2 & mask2))) { |
743 | return IRQ_NONE; | 955 | return IRQ_NONE; |
@@ -748,136 +960,283 @@ static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id) | |||
748 | udelay(500); | 960 | udelay(500); |
749 | 961 | ||
750 | msens = hdmi_read(hdmi, HDMI_HOT_PLUG_MSENS_STATUS); | 962 | msens = hdmi_read(hdmi, HDMI_HOT_PLUG_MSENS_STATUS); |
751 | pr_debug("MSENS 0x%x\n", msens); | 963 | dev_dbg(hdmi->dev, "MSENS 0x%x\n", msens); |
752 | /* Check, if hot plug & MSENS pin status are both high */ | 964 | /* Check, if hot plug & MSENS pin status are both high */ |
753 | if ((msens & 0xC0) == 0xC0) { | 965 | if ((msens & 0xC0) == 0xC0) { |
754 | /* Display plug in */ | 966 | /* Display plug in */ |
967 | hdmi->edid_segment_nr = 0; | ||
968 | hdmi->edid_block_addr = 0; | ||
969 | hdmi->edid_blocks = 0; | ||
755 | hdmi->hp_state = HDMI_HOTPLUG_CONNECTED; | 970 | hdmi->hp_state = HDMI_HOTPLUG_CONNECTED; |
756 | 971 | ||
757 | /* Set EDID word address */ | 972 | /* Set EDID word address */ |
758 | hdmi_write(hdmi, 0x00, HDMI_EDID_WORD_ADDRESS); | 973 | hdmi_write(hdmi, 0x00, HDMI_EDID_WORD_ADDRESS); |
759 | /* Set EDID segment pointer */ | ||
760 | hdmi_write(hdmi, 0x00, HDMI_EDID_SEGMENT_POINTER); | ||
761 | /* Enable EDID interrupt */ | 974 | /* Enable EDID interrupt */ |
762 | hdmi_write(hdmi, 0xC6, HDMI_INTERRUPT_MASK_1); | 975 | hdmi_write(hdmi, 0xC6, HDMI_INTERRUPT_MASK_1); |
976 | /* Set EDID segment pointer - starts reading EDID */ | ||
977 | hdmi_write(hdmi, 0x00, HDMI_EDID_SEGMENT_POINTER); | ||
763 | } else if (!(status1 & 0x80)) { | 978 | } else if (!(status1 & 0x80)) { |
764 | /* Display unplug, beware multiple interrupts */ | 979 | /* Display unplug, beware multiple interrupts */ |
765 | if (hdmi->hp_state != HDMI_HOTPLUG_DISCONNECTED) | 980 | if (hdmi->hp_state != HDMI_HOTPLUG_DISCONNECTED) { |
981 | hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; | ||
766 | schedule_delayed_work(&hdmi->edid_work, 0); | 982 | schedule_delayed_work(&hdmi->edid_work, 0); |
767 | 983 | } | |
768 | hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; | ||
769 | /* display_off will switch back to mode_a */ | 984 | /* display_off will switch back to mode_a */ |
770 | } | 985 | } |
771 | } else if (status1 & 2) { | 986 | } else if (status1 & 2) { |
772 | /* EDID error interrupt: retry */ | 987 | /* EDID error interrupt: retry */ |
773 | /* Set EDID word address */ | 988 | /* Set EDID word address */ |
774 | hdmi_write(hdmi, 0x00, HDMI_EDID_WORD_ADDRESS); | 989 | hdmi_write(hdmi, hdmi->edid_block_addr, HDMI_EDID_WORD_ADDRESS); |
775 | /* Set EDID segment pointer */ | 990 | /* Set EDID segment pointer */ |
776 | hdmi_write(hdmi, 0x00, HDMI_EDID_SEGMENT_POINTER); | 991 | hdmi_write(hdmi, hdmi->edid_segment_nr, HDMI_EDID_SEGMENT_POINTER); |
777 | } else if (status1 & 4) { | 992 | } else if (status1 & 4) { |
778 | /* Disable EDID interrupt */ | 993 | /* Disable EDID interrupt */ |
779 | hdmi_write(hdmi, 0xC0, HDMI_INTERRUPT_MASK_1); | 994 | hdmi_write(hdmi, 0xC0, HDMI_INTERRUPT_MASK_1); |
780 | hdmi->hp_state = HDMI_HOTPLUG_EDID_DONE; | ||
781 | schedule_delayed_work(&hdmi->edid_work, msecs_to_jiffies(10)); | 995 | schedule_delayed_work(&hdmi->edid_work, msecs_to_jiffies(10)); |
782 | } | 996 | } |
783 | 997 | ||
784 | return IRQ_HANDLED; | 998 | return IRQ_HANDLED; |
785 | } | 999 | } |
786 | 1000 | ||
787 | static void hdmi_display_on(void *arg, struct fb_info *info) | 1001 | /* locking: called with info->lock held, or before register_framebuffer() */ |
1002 | static void sh_hdmi_display_on(void *arg, struct fb_info *info) | ||
788 | { | 1003 | { |
1004 | /* | ||
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 | */ | ||
789 | struct sh_hdmi *hdmi = arg; | 1008 | struct sh_hdmi *hdmi = arg; |
790 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; | 1009 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; |
1010 | struct sh_mobile_lcdc_chan *ch = info->par; | ||
791 | 1011 | ||
792 | if (info->var.xres != 1280 || info->var.yres != 720) { | 1012 | dev_dbg(hdmi->dev, "%s(%p): state %x\n", __func__, |
793 | dev_warn(info->device, "Unsupported framebuffer geometry %ux%u\n", | 1013 | pdata->lcd_dev, info->state); |
794 | info->var.xres, info->var.yres); | 1014 | |
795 | return; | 1015 | /* No need to lock */ |
796 | } | 1016 | hdmi->info = info; |
797 | 1017 | ||
798 | pr_debug("%s(%p): state %x\n", __func__, pdata->lcd_dev, info->state); | ||
799 | /* | 1018 | /* |
800 | * FIXME: not a good place to store fb_info. And we cannot nullify it | 1019 | * hp_state can be set to |
801 | * even on monitor disconnect. What should the lifecycle be? | 1020 | * HDMI_HOTPLUG_DISCONNECTED: on monitor unplug |
1021 | * HDMI_HOTPLUG_CONNECTED: on monitor plug-in | ||
1022 | * HDMI_HOTPLUG_EDID_DONE: on EDID read completion | ||
802 | */ | 1023 | */ |
803 | hdmi->info = info; | ||
804 | switch (hdmi->hp_state) { | 1024 | switch (hdmi->hp_state) { |
805 | case HDMI_HOTPLUG_EDID_DONE: | 1025 | case HDMI_HOTPLUG_EDID_DONE: |
806 | /* PS mode d->e. All functions are active */ | 1026 | /* PS mode d->e. All functions are active */ |
807 | hdmi_write(hdmi, 0x80, HDMI_SYSTEM_CTRL); | 1027 | hdmi_write(hdmi, 0x80, HDMI_SYSTEM_CTRL); |
808 | pr_debug("HDMI running\n"); | 1028 | dev_dbg(hdmi->dev, "HDMI running\n"); |
809 | break; | 1029 | break; |
810 | case HDMI_HOTPLUG_DISCONNECTED: | 1030 | case HDMI_HOTPLUG_DISCONNECTED: |
811 | info->state = FBINFO_STATE_SUSPENDED; | 1031 | info->state = FBINFO_STATE_SUSPENDED; |
812 | default: | 1032 | default: |
813 | hdmi->var = info->var; | 1033 | hdmi->var = ch->display_var; |
814 | } | 1034 | } |
815 | } | 1035 | } |
816 | 1036 | ||
817 | static void hdmi_display_off(void *arg) | 1037 | /* locking: called with info->lock held */ |
1038 | static void sh_hdmi_display_off(void *arg) | ||
818 | { | 1039 | { |
819 | struct sh_hdmi *hdmi = arg; | 1040 | struct sh_hdmi *hdmi = arg; |
820 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; | 1041 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; |
821 | 1042 | ||
822 | pr_debug("%s(%p)\n", __func__, pdata->lcd_dev); | 1043 | dev_dbg(hdmi->dev, "%s(%p)\n", __func__, pdata->lcd_dev); |
823 | /* PS mode e->a */ | 1044 | /* PS mode e->a */ |
824 | hdmi_write(hdmi, 0x10, HDMI_SYSTEM_CTRL); | 1045 | hdmi_write(hdmi, 0x10, HDMI_SYSTEM_CTRL); |
825 | } | 1046 | } |
826 | 1047 | ||
1048 | static bool sh_hdmi_must_reconfigure(struct sh_hdmi *hdmi) | ||
1049 | { | ||
1050 | struct fb_info *info = hdmi->info; | ||
1051 | struct sh_mobile_lcdc_chan *ch = info->par; | ||
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 | |||
1075 | /** | ||
1076 | * sh_hdmi_clk_configure() - set HDMI clock frequency and enable the clock | ||
1077 | * @hdmi: driver context | ||
1078 | * @hdmi_rate: HDMI clock frequency in Hz | ||
1079 | * @parent_rate: if != 0 - set parent clock rate for optimal precision | ||
1080 | * return: configured positive rate if successful | ||
1081 | * 0 if couldn't set the rate, but managed to enable the | ||
1082 | * clock, negative error, if couldn't enable the clock | ||
1083 | */ | ||
1084 | static long sh_hdmi_clk_configure(struct sh_hdmi *hdmi, unsigned long hdmi_rate, | ||
1085 | unsigned long parent_rate) | ||
1086 | { | ||
1087 | int ret; | ||
1088 | |||
1089 | if (parent_rate && clk_get_parent(hdmi->hdmi_clk)) { | ||
1090 | ret = clk_set_rate(clk_get_parent(hdmi->hdmi_clk), parent_rate); | ||
1091 | if (ret < 0) { | ||
1092 | dev_warn(hdmi->dev, "Cannot set parent rate %ld: %d\n", parent_rate, ret); | ||
1093 | hdmi_rate = clk_round_rate(hdmi->hdmi_clk, hdmi_rate); | ||
1094 | } else { | ||
1095 | dev_dbg(hdmi->dev, "HDMI set parent frequency %lu\n", parent_rate); | ||
1096 | } | ||
1097 | } | ||
1098 | |||
1099 | ret = clk_set_rate(hdmi->hdmi_clk, hdmi_rate); | ||
1100 | if (ret < 0) { | ||
1101 | dev_warn(hdmi->dev, "Cannot set rate %ld: %d\n", hdmi_rate, ret); | ||
1102 | hdmi_rate = 0; | ||
1103 | } else { | ||
1104 | dev_dbg(hdmi->dev, "HDMI set frequency %lu\n", hdmi_rate); | ||
1105 | } | ||
1106 | |||
1107 | return hdmi_rate; | ||
1108 | } | ||
1109 | |||
827 | /* Hotplug interrupt occurred, read EDID */ | 1110 | /* Hotplug interrupt occurred, read EDID */ |
828 | static void edid_work_fn(struct work_struct *work) | 1111 | static void sh_hdmi_edid_work_fn(struct work_struct *work) |
829 | { | 1112 | { |
830 | struct sh_hdmi *hdmi = container_of(work, struct sh_hdmi, edid_work.work); | 1113 | struct sh_hdmi *hdmi = container_of(work, struct sh_hdmi, edid_work.work); |
831 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; | 1114 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; |
1115 | struct sh_mobile_lcdc_chan *ch; | ||
1116 | int ret; | ||
832 | 1117 | ||
833 | pr_debug("%s(%p): begin, hotplug status %d\n", __func__, | 1118 | dev_dbg(hdmi->dev, "%s(%p): begin, hotplug status %d\n", __func__, |
834 | pdata->lcd_dev, hdmi->hp_state); | 1119 | pdata->lcd_dev, hdmi->hp_state); |
835 | 1120 | ||
836 | if (!pdata->lcd_dev) | 1121 | if (!pdata->lcd_dev) |
837 | return; | 1122 | return; |
838 | 1123 | ||
839 | if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) { | 1124 | mutex_lock(&hdmi->mutex); |
840 | pm_runtime_get_sync(hdmi->dev); | 1125 | |
841 | /* A device has been plugged in */ | 1126 | if (hdmi->hp_state == HDMI_HOTPLUG_CONNECTED) { |
842 | sh_hdmi_read_edid(hdmi); | 1127 | struct fb_info *info = hdmi->info; |
1128 | unsigned long parent_rate = 0, hdmi_rate; | ||
1129 | |||
1130 | ret = sh_hdmi_read_edid(hdmi, &hdmi_rate, &parent_rate); | ||
1131 | if (ret < 0) | ||
1132 | goto out; | ||
1133 | |||
1134 | hdmi->hp_state = HDMI_HOTPLUG_EDID_DONE; | ||
1135 | |||
1136 | /* Reconfigure the clock */ | ||
1137 | ret = sh_hdmi_clk_configure(hdmi, hdmi_rate, parent_rate); | ||
1138 | if (ret < 0) | ||
1139 | goto out; | ||
1140 | |||
843 | msleep(10); | 1141 | msleep(10); |
844 | sh_hdmi_configure(hdmi); | 1142 | sh_hdmi_configure(hdmi); |
845 | /* Switched to another (d) power-save mode */ | 1143 | /* Switched to another (d) power-save mode */ |
846 | msleep(10); | 1144 | msleep(10); |
847 | 1145 | ||
848 | if (!hdmi->info) | 1146 | if (!info) |
849 | return; | 1147 | goto out; |
1148 | |||
1149 | ch = info->par; | ||
850 | 1150 | ||
851 | acquire_console_sem(); | 1151 | console_lock(); |
852 | 1152 | ||
853 | /* HDMI plug in */ | 1153 | /* HDMI plug in */ |
854 | hdmi->info->var = hdmi->var; | 1154 | if (!sh_hdmi_must_reconfigure(hdmi) && |
855 | if (hdmi->info->state != FBINFO_STATE_RUNNING) | 1155 | info->state == FBINFO_STATE_RUNNING) { |
856 | fb_set_suspend(hdmi->info, 0); | 1156 | /* |
857 | else | 1157 | * First activation with the default monitor - just turn |
858 | hdmi_display_on(hdmi, hdmi->info); | 1158 | * on, if we run a resume here, the logo disappears |
1159 | */ | ||
1160 | if (lock_fb_info(info)) { | ||
1161 | info->var.width = hdmi->var.width; | ||
1162 | info->var.height = hdmi->var.height; | ||
1163 | sh_hdmi_display_on(hdmi, info); | ||
1164 | unlock_fb_info(info); | ||
1165 | } | ||
1166 | } else { | ||
1167 | /* New monitor or have to wake up */ | ||
1168 | fb_set_suspend(info, 0); | ||
1169 | } | ||
859 | 1170 | ||
860 | release_console_sem(); | 1171 | console_unlock(); |
861 | } else { | 1172 | } else { |
1173 | ret = 0; | ||
862 | if (!hdmi->info) | 1174 | if (!hdmi->info) |
863 | return; | 1175 | goto out; |
1176 | |||
1177 | hdmi->monspec.modedb_len = 0; | ||
1178 | fb_destroy_modedb(hdmi->monspec.modedb); | ||
1179 | hdmi->monspec.modedb = NULL; | ||
864 | 1180 | ||
865 | acquire_console_sem(); | 1181 | console_lock(); |
866 | 1182 | ||
867 | /* HDMI disconnect */ | 1183 | /* HDMI disconnect */ |
868 | fb_set_suspend(hdmi->info, 1); | 1184 | fb_set_suspend(hdmi->info, 1); |
869 | 1185 | ||
870 | release_console_sem(); | 1186 | console_unlock(); |
871 | pm_runtime_put(hdmi->dev); | ||
872 | } | 1187 | } |
873 | 1188 | ||
874 | pr_debug("%s(%p): end\n", __func__, pdata->lcd_dev); | 1189 | out: |
1190 | if (ret < 0 && ret != -EAGAIN) | ||
1191 | hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; | ||
1192 | mutex_unlock(&hdmi->mutex); | ||
1193 | |||
1194 | dev_dbg(hdmi->dev, "%s(%p): end\n", __func__, pdata->lcd_dev); | ||
1195 | } | ||
1196 | |||
1197 | static int sh_hdmi_notify(struct notifier_block *nb, | ||
1198 | unsigned long action, void *data) | ||
1199 | { | ||
1200 | struct fb_event *event = data; | ||
1201 | struct fb_info *info = event->info; | ||
1202 | struct sh_mobile_lcdc_chan *ch = info->par; | ||
1203 | struct sh_mobile_lcdc_board_cfg *board_cfg = &ch->cfg.board_cfg; | ||
1204 | struct sh_hdmi *hdmi = board_cfg->board_data; | ||
1205 | |||
1206 | if (!hdmi || nb != &hdmi->notifier || hdmi->info != info) | ||
1207 | return NOTIFY_DONE; | ||
1208 | |||
1209 | switch(action) { | ||
1210 | case FB_EVENT_FB_REGISTERED: | ||
1211 | /* Unneeded, activation taken care by sh_hdmi_display_on() */ | ||
1212 | break; | ||
1213 | case FB_EVENT_FB_UNREGISTERED: | ||
1214 | /* | ||
1215 | * We are called from unregister_framebuffer() with the | ||
1216 | * info->lock held. This is bad for us, because we can race with | ||
1217 | * the scheduled work, which has to call fb_set_suspend(), which | ||
1218 | * takes info->lock internally, so, sh_hdmi_edid_work_fn() | ||
1219 | * cannot take and hold info->lock for the whole function | ||
1220 | * duration. Using an additional lock creates a classical AB-BA | ||
1221 | * lock up. Therefore, we have to release the info->lock | ||
1222 | * temporarily, synchronise with the work queue and re-acquire | ||
1223 | * the info->lock. | ||
1224 | */ | ||
1225 | unlock_fb_info(info); | ||
1226 | mutex_lock(&hdmi->mutex); | ||
1227 | hdmi->info = NULL; | ||
1228 | mutex_unlock(&hdmi->mutex); | ||
1229 | lock_fb_info(info); | ||
1230 | return NOTIFY_OK; | ||
1231 | } | ||
1232 | return NOTIFY_DONE; | ||
875 | } | 1233 | } |
876 | 1234 | ||
877 | static int __init sh_hdmi_probe(struct platform_device *pdev) | 1235 | static int __init sh_hdmi_probe(struct platform_device *pdev) |
878 | { | 1236 | { |
879 | struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; | 1237 | struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; |
880 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 1238 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
1239 | struct sh_mobile_lcdc_board_cfg *board_cfg; | ||
881 | int irq = platform_get_irq(pdev, 0), ret; | 1240 | int irq = platform_get_irq(pdev, 0), ret; |
882 | struct sh_hdmi *hdmi; | 1241 | struct sh_hdmi *hdmi; |
883 | long rate; | 1242 | long rate; |
@@ -891,6 +1250,8 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) | |||
891 | return -ENOMEM; | 1250 | return -ENOMEM; |
892 | } | 1251 | } |
893 | 1252 | ||
1253 | mutex_init(&hdmi->mutex); | ||
1254 | |||
894 | hdmi->dev = &pdev->dev; | 1255 | hdmi->dev = &pdev->dev; |
895 | 1256 | ||
896 | hdmi->hdmi_clk = clk_get(&pdev->dev, "ick"); | 1257 | hdmi->hdmi_clk = clk_get(&pdev->dev, "ick"); |
@@ -900,30 +1261,23 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) | |||
900 | goto egetclk; | 1261 | goto egetclk; |
901 | } | 1262 | } |
902 | 1263 | ||
903 | rate = PICOS2KHZ(pdata->lcd_chan->lcd_cfg.pixclock) * 1000; | 1264 | /* An arbitrary relaxed pixclock just to get things started: from standard 480p */ |
1265 | rate = clk_round_rate(hdmi->hdmi_clk, PICOS2KHZ(37037)); | ||
1266 | if (rate > 0) | ||
1267 | rate = sh_hdmi_clk_configure(hdmi, rate, 0); | ||
904 | 1268 | ||
905 | rate = clk_round_rate(hdmi->hdmi_clk, rate); | ||
906 | if (rate < 0) { | 1269 | if (rate < 0) { |
907 | ret = rate; | 1270 | ret = rate; |
908 | dev_err(&pdev->dev, "Cannot get suitable rate: %ld\n", rate); | ||
909 | goto erate; | 1271 | goto erate; |
910 | } | 1272 | } |
911 | 1273 | ||
912 | ret = clk_set_rate(hdmi->hdmi_clk, rate); | ||
913 | if (ret < 0) { | ||
914 | dev_err(&pdev->dev, "Cannot set rate %ld: %d\n", rate, ret); | ||
915 | goto erate; | ||
916 | } | ||
917 | |||
918 | pr_debug("HDMI set frequency %lu\n", rate); | ||
919 | |||
920 | ret = clk_enable(hdmi->hdmi_clk); | 1274 | ret = clk_enable(hdmi->hdmi_clk); |
921 | if (ret < 0) { | 1275 | if (ret < 0) { |
922 | dev_err(&pdev->dev, "Cannot enable clock: %d\n", ret); | 1276 | dev_err(hdmi->dev, "Cannot enable clock: %d\n", ret); |
923 | goto eclkenable; | 1277 | goto erate; |
924 | } | 1278 | } |
925 | 1279 | ||
926 | dev_info(&pdev->dev, "Enabled HDMI clock at %luHz\n", rate); | 1280 | dev_dbg(&pdev->dev, "Enabled HDMI clock at %luHz\n", rate); |
927 | 1281 | ||
928 | if (!request_mem_region(res->start, resource_size(res), dev_name(&pdev->dev))) { | 1282 | if (!request_mem_region(res->start, resource_size(res), dev_name(&pdev->dev))) { |
929 | dev_err(&pdev->dev, "HDMI register region already claimed\n"); | 1283 | dev_err(&pdev->dev, "HDMI register region already claimed\n"); |
@@ -940,21 +1294,21 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) | |||
940 | 1294 | ||
941 | platform_set_drvdata(pdev, hdmi); | 1295 | platform_set_drvdata(pdev, hdmi); |
942 | 1296 | ||
943 | #if 1 | ||
944 | /* Product and revision IDs are 0 in sh-mobile version */ | ||
945 | dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n", | ||
946 | hdmi_read(hdmi, HDMI_PRODUCT_ID), hdmi_read(hdmi, HDMI_REVISION_ID)); | ||
947 | #endif | ||
948 | |||
949 | /* Set up LCDC callbacks */ | 1297 | /* Set up LCDC callbacks */ |
950 | pdata->lcd_chan->board_cfg.board_data = hdmi; | 1298 | board_cfg = &pdata->lcd_chan->board_cfg; |
951 | pdata->lcd_chan->board_cfg.display_on = hdmi_display_on; | 1299 | board_cfg->owner = THIS_MODULE; |
952 | pdata->lcd_chan->board_cfg.display_off = hdmi_display_off; | 1300 | board_cfg->board_data = hdmi; |
1301 | board_cfg->display_on = sh_hdmi_display_on; | ||
1302 | board_cfg->display_off = sh_hdmi_display_off; | ||
953 | 1303 | ||
954 | INIT_DELAYED_WORK(&hdmi->edid_work, edid_work_fn); | 1304 | INIT_DELAYED_WORK(&hdmi->edid_work, sh_hdmi_edid_work_fn); |
955 | 1305 | ||
956 | pm_runtime_enable(&pdev->dev); | 1306 | pm_runtime_enable(&pdev->dev); |
957 | pm_runtime_resume(&pdev->dev); | 1307 | pm_runtime_get_sync(&pdev->dev); |
1308 | |||
1309 | /* Product and revision IDs are 0 in sh-mobile version */ | ||
1310 | dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n", | ||
1311 | hdmi_read(hdmi, HDMI_PRODUCT_ID), hdmi_read(hdmi, HDMI_REVISION_ID)); | ||
958 | 1312 | ||
959 | ret = request_irq(irq, sh_hdmi_hotplug, 0, | 1313 | ret = request_irq(irq, sh_hdmi_hotplug, 0, |
960 | dev_name(&pdev->dev), hdmi); | 1314 | dev_name(&pdev->dev), hdmi); |
@@ -963,19 +1317,32 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) | |||
963 | goto ereqirq; | 1317 | goto ereqirq; |
964 | } | 1318 | } |
965 | 1319 | ||
1320 | ret = snd_soc_register_codec(&pdev->dev, | ||
1321 | &soc_codec_dev_sh_hdmi, &sh_hdmi_dai, 1); | ||
1322 | if (ret < 0) { | ||
1323 | dev_err(&pdev->dev, "codec registration failed\n"); | ||
1324 | goto ecodec; | ||
1325 | } | ||
1326 | |||
1327 | hdmi->notifier.notifier_call = sh_hdmi_notify; | ||
1328 | fb_register_client(&hdmi->notifier); | ||
1329 | |||
966 | return 0; | 1330 | return 0; |
967 | 1331 | ||
1332 | ecodec: | ||
1333 | free_irq(irq, hdmi); | ||
968 | ereqirq: | 1334 | ereqirq: |
1335 | pm_runtime_put(&pdev->dev); | ||
969 | pm_runtime_disable(&pdev->dev); | 1336 | pm_runtime_disable(&pdev->dev); |
970 | iounmap(hdmi->base); | 1337 | iounmap(hdmi->base); |
971 | emap: | 1338 | emap: |
972 | release_mem_region(res->start, resource_size(res)); | 1339 | release_mem_region(res->start, resource_size(res)); |
973 | ereqreg: | 1340 | ereqreg: |
974 | clk_disable(hdmi->hdmi_clk); | 1341 | clk_disable(hdmi->hdmi_clk); |
975 | eclkenable: | ||
976 | erate: | 1342 | erate: |
977 | clk_put(hdmi->hdmi_clk); | 1343 | clk_put(hdmi->hdmi_clk); |
978 | egetclk: | 1344 | egetclk: |
1345 | mutex_destroy(&hdmi->mutex); | ||
979 | kfree(hdmi); | 1346 | kfree(hdmi); |
980 | 1347 | ||
981 | return ret; | 1348 | return ret; |
@@ -986,19 +1353,29 @@ static int __exit sh_hdmi_remove(struct platform_device *pdev) | |||
986 | struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; | 1353 | struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; |
987 | struct sh_hdmi *hdmi = platform_get_drvdata(pdev); | 1354 | struct sh_hdmi *hdmi = platform_get_drvdata(pdev); |
988 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 1355 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
1356 | struct sh_mobile_lcdc_board_cfg *board_cfg = &pdata->lcd_chan->board_cfg; | ||
989 | int irq = platform_get_irq(pdev, 0); | 1357 | int irq = platform_get_irq(pdev, 0); |
990 | 1358 | ||
991 | pdata->lcd_chan->board_cfg.display_on = NULL; | 1359 | snd_soc_unregister_codec(&pdev->dev); |
992 | pdata->lcd_chan->board_cfg.display_off = NULL; | 1360 | |
993 | pdata->lcd_chan->board_cfg.board_data = NULL; | 1361 | fb_unregister_client(&hdmi->notifier); |
994 | 1362 | ||
1363 | board_cfg->display_on = NULL; | ||
1364 | board_cfg->display_off = NULL; | ||
1365 | board_cfg->board_data = NULL; | ||
1366 | board_cfg->owner = NULL; | ||
1367 | |||
1368 | /* No new work will be scheduled, wait for running ISR */ | ||
995 | free_irq(irq, hdmi); | 1369 | free_irq(irq, hdmi); |
996 | pm_runtime_disable(&pdev->dev); | 1370 | /* Wait for already scheduled work */ |
997 | cancel_delayed_work_sync(&hdmi->edid_work); | 1371 | cancel_delayed_work_sync(&hdmi->edid_work); |
1372 | pm_runtime_put(&pdev->dev); | ||
1373 | pm_runtime_disable(&pdev->dev); | ||
998 | clk_disable(hdmi->hdmi_clk); | 1374 | clk_disable(hdmi->hdmi_clk); |
999 | clk_put(hdmi->hdmi_clk); | 1375 | clk_put(hdmi->hdmi_clk); |
1000 | iounmap(hdmi->base); | 1376 | iounmap(hdmi->base); |
1001 | release_mem_region(res->start, resource_size(res)); | 1377 | release_mem_region(res->start, resource_size(res)); |
1378 | mutex_destroy(&hdmi->mutex); | ||
1002 | kfree(hdmi); | 1379 | kfree(hdmi); |
1003 | 1380 | ||
1004 | return 0; | 1381 | return 0; |