diff options
| -rw-r--r-- | drivers/gpu/drm/exynos/Kconfig | 1 | ||||
| -rw-r--r-- | drivers/gpu/drm/exynos/exynos_hdmi.c | 250 | ||||
| -rw-r--r-- | drivers/gpu/drm/exynos/regs-hdmi.h | 8 |
3 files changed, 194 insertions, 65 deletions
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 305dc3d4ff77..5a7c9d8abd6b 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig | |||
| @@ -3,6 +3,7 @@ config DRM_EXYNOS | |||
| 3 | depends on OF && DRM && (ARCH_S3C64XX || ARCH_EXYNOS || ARCH_MULTIPLATFORM) | 3 | depends on OF && DRM && (ARCH_S3C64XX || ARCH_EXYNOS || ARCH_MULTIPLATFORM) |
| 4 | select DRM_KMS_HELPER | 4 | select DRM_KMS_HELPER |
| 5 | select VIDEOMODE_HELPERS | 5 | select VIDEOMODE_HELPERS |
| 6 | select SND_SOC_HDMI_CODEC if SND_SOC | ||
| 6 | help | 7 | help |
| 7 | Choose this option if you have a Samsung SoC EXYNOS chipset. | 8 | Choose this option if you have a Samsung SoC EXYNOS chipset. |
| 8 | If M is selected the module will be called exynosdrm. | 9 | If M is selected the module will be called exynosdrm. |
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 1309b1c9e074..82d1b7e2febe 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c | |||
| @@ -40,7 +40,7 @@ | |||
| 40 | #include <linux/component.h> | 40 | #include <linux/component.h> |
| 41 | #include <linux/mfd/syscon.h> | 41 | #include <linux/mfd/syscon.h> |
| 42 | #include <linux/regmap.h> | 42 | #include <linux/regmap.h> |
| 43 | 43 | #include <sound/hdmi-codec.h> | |
| 44 | #include <drm/exynos_drm.h> | 44 | #include <drm/exynos_drm.h> |
| 45 | 45 | ||
| 46 | #include <media/cec-notifier.h> | 46 | #include <media/cec-notifier.h> |
| @@ -111,12 +111,18 @@ struct hdmi_driver_data { | |||
| 111 | struct string_array_spec clk_muxes; | 111 | struct string_array_spec clk_muxes; |
| 112 | }; | 112 | }; |
| 113 | 113 | ||
| 114 | struct hdmi_audio { | ||
| 115 | struct platform_device *pdev; | ||
| 116 | struct hdmi_audio_infoframe infoframe; | ||
| 117 | struct hdmi_codec_params params; | ||
| 118 | bool mute; | ||
| 119 | }; | ||
| 120 | |||
| 114 | struct hdmi_context { | 121 | struct hdmi_context { |
| 115 | struct drm_encoder encoder; | 122 | struct drm_encoder encoder; |
| 116 | struct device *dev; | 123 | struct device *dev; |
| 117 | struct drm_device *drm_dev; | 124 | struct drm_device *drm_dev; |
| 118 | struct drm_connector connector; | 125 | struct drm_connector connector; |
| 119 | bool powered; | ||
| 120 | bool dvi_mode; | 126 | bool dvi_mode; |
| 121 | struct delayed_work hotplug_work; | 127 | struct delayed_work hotplug_work; |
| 122 | struct cec_notifier *notifier; | 128 | struct cec_notifier *notifier; |
| @@ -136,6 +142,11 @@ struct hdmi_context { | |||
| 136 | struct regulator *reg_hdmi_en; | 142 | struct regulator *reg_hdmi_en; |
| 137 | struct exynos_drm_clk phy_clk; | 143 | struct exynos_drm_clk phy_clk; |
| 138 | struct drm_bridge *bridge; | 144 | struct drm_bridge *bridge; |
| 145 | |||
| 146 | /* mutex protecting subsequent fields below */ | ||
| 147 | struct mutex mutex; | ||
| 148 | struct hdmi_audio audio; | ||
| 149 | bool powered; | ||
| 139 | }; | 150 | }; |
| 140 | 151 | ||
| 141 | static inline struct hdmi_context *encoder_to_hdmi(struct drm_encoder *e) | 152 | static inline struct hdmi_context *encoder_to_hdmi(struct drm_encoder *e) |
| @@ -776,6 +787,22 @@ static int hdmi_clk_set_parents(struct hdmi_context *hdata, bool to_phy) | |||
| 776 | return ret; | 787 | return ret; |
| 777 | } | 788 | } |
| 778 | 789 | ||
| 790 | static int hdmi_audio_infoframe_apply(struct hdmi_context *hdata) | ||
| 791 | { | ||
| 792 | struct hdmi_audio_infoframe *infoframe = &hdata->audio.infoframe; | ||
| 793 | u8 buf[HDMI_INFOFRAME_SIZE(AUDIO)]; | ||
| 794 | int len; | ||
| 795 | |||
| 796 | len = hdmi_audio_infoframe_pack(infoframe, buf, sizeof(buf)); | ||
| 797 | if (len < 0) | ||
| 798 | return len; | ||
| 799 | |||
| 800 | hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_EVERY_VSYNC); | ||
| 801 | hdmi_reg_write_buf(hdata, HDMI_AUI_HEADER0, buf, len); | ||
| 802 | |||
| 803 | return 0; | ||
| 804 | } | ||
| 805 | |||
| 779 | static void hdmi_reg_infoframes(struct hdmi_context *hdata) | 806 | static void hdmi_reg_infoframes(struct hdmi_context *hdata) |
| 780 | { | 807 | { |
| 781 | struct drm_display_mode *m = &hdata->encoder.crtc->state->mode; | 808 | struct drm_display_mode *m = &hdata->encoder.crtc->state->mode; |
| @@ -812,15 +839,7 @@ static void hdmi_reg_infoframes(struct hdmi_context *hdata) | |||
| 812 | hdmi_reg_write_buf(hdata, HDMI_VSI_DATA(0), buf + 3, ret - 3); | 839 | hdmi_reg_write_buf(hdata, HDMI_VSI_DATA(0), buf + 3, ret - 3); |
| 813 | } | 840 | } |
| 814 | 841 | ||
| 815 | ret = hdmi_audio_infoframe_init(&frm.audio); | 842 | hdmi_audio_infoframe_apply(hdata); |
| 816 | if (!ret) { | ||
| 817 | frm.audio.channels = 2; | ||
| 818 | ret = hdmi_audio_infoframe_pack(&frm.audio, buf, sizeof(buf)); | ||
| 819 | } | ||
| 820 | if (ret > 0) { | ||
| 821 | hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_EVERY_VSYNC); | ||
| 822 | hdmi_reg_write_buf(hdata, HDMI_AUI_HEADER0, buf, ret); | ||
| 823 | } | ||
| 824 | } | 843 | } |
| 825 | 844 | ||
| 826 | static enum drm_connector_status hdmi_detect(struct drm_connector *connector, | 845 | static enum drm_connector_status hdmi_detect(struct drm_connector *connector, |
| @@ -1010,23 +1029,18 @@ static void hdmi_reg_acr(struct hdmi_context *hdata, u32 freq) | |||
| 1010 | hdmi_reg_writeb(hdata, HDMI_ACR_CON, 4); | 1029 | hdmi_reg_writeb(hdata, HDMI_ACR_CON, 4); |
| 1011 | } | 1030 | } |
| 1012 | 1031 | ||
| 1013 | static void hdmi_audio_init(struct hdmi_context *hdata) | 1032 | static void hdmi_audio_config(struct hdmi_context *hdata) |
| 1014 | { | 1033 | { |
| 1015 | u32 sample_rate, bits_per_sample; | 1034 | u32 bit_ch = 1; |
| 1016 | u32 data_num, bit_ch, sample_frq; | 1035 | u32 data_num, val; |
| 1017 | u32 val; | 1036 | int i; |
| 1018 | |||
| 1019 | sample_rate = 44100; | ||
| 1020 | bits_per_sample = 16; | ||
| 1021 | 1037 | ||
| 1022 | switch (bits_per_sample) { | 1038 | switch (hdata->audio.params.sample_width) { |
| 1023 | case 20: | 1039 | case 20: |
| 1024 | data_num = 2; | 1040 | data_num = 2; |
| 1025 | bit_ch = 1; | ||
| 1026 | break; | 1041 | break; |
| 1027 | case 24: | 1042 | case 24: |
| 1028 | data_num = 3; | 1043 | data_num = 3; |
| 1029 | bit_ch = 1; | ||
| 1030 | break; | 1044 | break; |
| 1031 | default: | 1045 | default: |
| 1032 | data_num = 1; | 1046 | data_num = 1; |
| @@ -1034,7 +1048,7 @@ static void hdmi_audio_init(struct hdmi_context *hdata) | |||
| 1034 | break; | 1048 | break; |
| 1035 | } | 1049 | } |
| 1036 | 1050 | ||
| 1037 | hdmi_reg_acr(hdata, sample_rate); | 1051 | hdmi_reg_acr(hdata, hdata->audio.params.sample_rate); |
| 1038 | 1052 | ||
| 1039 | hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CON, HDMI_I2S_IN_DISABLE | 1053 | hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CON, HDMI_I2S_IN_DISABLE |
| 1040 | | HDMI_I2S_AUD_I2S | HDMI_I2S_CUV_I2S_ENABLE | 1054 | | HDMI_I2S_AUD_I2S | HDMI_I2S_CUV_I2S_ENABLE |
| @@ -1044,12 +1058,6 @@ static void hdmi_audio_init(struct hdmi_context *hdata) | |||
| 1044 | | HDMI_I2S_CH1_EN | HDMI_I2S_CH2_EN); | 1058 | | HDMI_I2S_CH1_EN | HDMI_I2S_CH2_EN); |
| 1045 | 1059 | ||
| 1046 | hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CUV, HDMI_I2S_CUV_RL_EN); | 1060 | hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CUV, HDMI_I2S_CUV_RL_EN); |
| 1047 | |||
| 1048 | sample_frq = (sample_rate == 44100) ? 0 : | ||
| 1049 | (sample_rate == 48000) ? 2 : | ||
| 1050 | (sample_rate == 32000) ? 3 : | ||
| 1051 | (sample_rate == 96000) ? 0xa : 0x0; | ||
| 1052 | |||
| 1053 | hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_DIS); | 1061 | hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_DIS); |
| 1054 | hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_EN); | 1062 | hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_EN); |
| 1055 | 1063 | ||
| @@ -1073,31 +1081,24 @@ static void hdmi_audio_init(struct hdmi_context *hdata) | |||
| 1073 | | HDMI_I2S_SET_SDATA_BIT(data_num) | 1081 | | HDMI_I2S_SET_SDATA_BIT(data_num) |
| 1074 | | HDMI_I2S_BASIC_FORMAT); | 1082 | | HDMI_I2S_BASIC_FORMAT); |
| 1075 | 1083 | ||
| 1076 | /* Configure register related to CUV information */ | 1084 | /* Configuration of the audio channel status registers */ |
| 1077 | hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_0, HDMI_I2S_CH_STATUS_MODE_0 | 1085 | for (i = 0; i < HDMI_I2S_CH_ST_MAXNUM; i++) |
| 1078 | | HDMI_I2S_2AUD_CH_WITHOUT_PREEMPH | 1086 | hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST(i), |
| 1079 | | HDMI_I2S_COPYRIGHT | 1087 | hdata->audio.params.iec.status[i]); |
| 1080 | | HDMI_I2S_LINEAR_PCM | ||
| 1081 | | HDMI_I2S_CONSUMER_FORMAT); | ||
| 1082 | hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_1, HDMI_I2S_CD_PLAYER); | ||
| 1083 | hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_2, HDMI_I2S_SET_SOURCE_NUM(0)); | ||
| 1084 | hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_3, HDMI_I2S_CLK_ACCUR_LEVEL_2 | ||
| 1085 | | HDMI_I2S_SET_SMP_FREQ(sample_frq)); | ||
| 1086 | hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_4, | ||
| 1087 | HDMI_I2S_ORG_SMP_FREQ_44_1 | ||
| 1088 | | HDMI_I2S_WORD_LEN_MAX24_24BITS | ||
| 1089 | | HDMI_I2S_WORD_LEN_MAX_24BITS); | ||
| 1090 | 1088 | ||
| 1091 | hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_CON, HDMI_I2S_CH_STATUS_RELOAD); | 1089 | hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_CON, HDMI_I2S_CH_STATUS_RELOAD); |
| 1092 | } | 1090 | } |
| 1093 | 1091 | ||
| 1094 | static void hdmi_audio_control(struct hdmi_context *hdata, bool onoff) | 1092 | static void hdmi_audio_control(struct hdmi_context *hdata) |
| 1095 | { | 1093 | { |
| 1094 | bool enable = !hdata->audio.mute; | ||
| 1095 | |||
| 1096 | if (hdata->dvi_mode) | 1096 | if (hdata->dvi_mode) |
| 1097 | return; | 1097 | return; |
| 1098 | 1098 | ||
| 1099 | hdmi_reg_writeb(hdata, HDMI_AUI_CON, onoff ? 2 : 0); | 1099 | hdmi_reg_writeb(hdata, HDMI_AUI_CON, enable ? |
| 1100 | hdmi_reg_writemask(hdata, HDMI_CON_0, onoff ? | 1100 | HDMI_AVI_CON_EVERY_VSYNC : HDMI_AUI_CON_NO_TRAN); |
| 1101 | hdmi_reg_writemask(hdata, HDMI_CON_0, enable ? | ||
| 1101 | HDMI_ASP_EN : HDMI_ASP_DIS, HDMI_ASP_MASK); | 1102 | HDMI_ASP_EN : HDMI_ASP_DIS, HDMI_ASP_MASK); |
| 1102 | } | 1103 | } |
| 1103 | 1104 | ||
| @@ -1428,13 +1429,14 @@ static void hdmiphy_conf_apply(struct hdmi_context *hdata) | |||
| 1428 | hdmiphy_wait_for_pll(hdata); | 1429 | hdmiphy_wait_for_pll(hdata); |
| 1429 | } | 1430 | } |
| 1430 | 1431 | ||
| 1432 | /* Should be called with hdata->mutex mutex held */ | ||
| 1431 | static void hdmi_conf_apply(struct hdmi_context *hdata) | 1433 | static void hdmi_conf_apply(struct hdmi_context *hdata) |
| 1432 | { | 1434 | { |
| 1433 | hdmi_start(hdata, false); | 1435 | hdmi_start(hdata, false); |
| 1434 | hdmi_conf_init(hdata); | 1436 | hdmi_conf_init(hdata); |
| 1435 | hdmi_audio_init(hdata); | 1437 | hdmi_audio_config(hdata); |
| 1436 | hdmi_mode_apply(hdata); | 1438 | hdmi_mode_apply(hdata); |
| 1437 | hdmi_audio_control(hdata, true); | 1439 | hdmi_audio_control(hdata); |
| 1438 | } | 1440 | } |
| 1439 | 1441 | ||
| 1440 | static void hdmi_set_refclk(struct hdmi_context *hdata, bool on) | 1442 | static void hdmi_set_refclk(struct hdmi_context *hdata, bool on) |
| @@ -1446,6 +1448,7 @@ static void hdmi_set_refclk(struct hdmi_context *hdata, bool on) | |||
| 1446 | SYSREG_HDMI_REFCLK_INT_CLK, on ? ~0 : 0); | 1448 | SYSREG_HDMI_REFCLK_INT_CLK, on ? ~0 : 0); |
| 1447 | } | 1449 | } |
| 1448 | 1450 | ||
| 1451 | /* Should be called with hdata->mutex mutex held. */ | ||
| 1449 | static void hdmiphy_enable(struct hdmi_context *hdata) | 1452 | static void hdmiphy_enable(struct hdmi_context *hdata) |
| 1450 | { | 1453 | { |
| 1451 | if (hdata->powered) | 1454 | if (hdata->powered) |
| @@ -1468,6 +1471,7 @@ static void hdmiphy_enable(struct hdmi_context *hdata) | |||
| 1468 | hdata->powered = true; | 1471 | hdata->powered = true; |
| 1469 | } | 1472 | } |
| 1470 | 1473 | ||
| 1474 | /* Should be called with hdata->mutex mutex held. */ | ||
| 1471 | static void hdmiphy_disable(struct hdmi_context *hdata) | 1475 | static void hdmiphy_disable(struct hdmi_context *hdata) |
| 1472 | { | 1476 | { |
| 1473 | if (!hdata->powered) | 1477 | if (!hdata->powered) |
| @@ -1493,28 +1497,38 @@ static void hdmi_enable(struct drm_encoder *encoder) | |||
| 1493 | { | 1497 | { |
| 1494 | struct hdmi_context *hdata = encoder_to_hdmi(encoder); | 1498 | struct hdmi_context *hdata = encoder_to_hdmi(encoder); |
| 1495 | 1499 | ||
| 1500 | mutex_lock(&hdata->mutex); | ||
| 1501 | |||
| 1496 | hdmiphy_enable(hdata); | 1502 | hdmiphy_enable(hdata); |
| 1497 | hdmi_conf_apply(hdata); | 1503 | hdmi_conf_apply(hdata); |
| 1504 | |||
| 1505 | mutex_unlock(&hdata->mutex); | ||
| 1498 | } | 1506 | } |
| 1499 | 1507 | ||
| 1500 | static void hdmi_disable(struct drm_encoder *encoder) | 1508 | static void hdmi_disable(struct drm_encoder *encoder) |
| 1501 | { | 1509 | { |
| 1502 | struct hdmi_context *hdata = encoder_to_hdmi(encoder); | 1510 | struct hdmi_context *hdata = encoder_to_hdmi(encoder); |
| 1503 | 1511 | ||
| 1504 | if (!hdata->powered) | 1512 | mutex_lock(&hdata->mutex); |
| 1513 | |||
| 1514 | if (hdata->powered) { | ||
| 1515 | /* | ||
| 1516 | * The SFRs of VP and Mixer are updated by Vertical Sync of | ||
| 1517 | * Timing generator which is a part of HDMI so the sequence | ||
| 1518 | * to disable TV Subsystem should be as following, | ||
| 1519 | * VP -> Mixer -> HDMI | ||
| 1520 | * | ||
| 1521 | * To achieve such sequence HDMI is disabled together with | ||
| 1522 | * HDMI PHY, via pipe clock callback. | ||
| 1523 | */ | ||
| 1524 | mutex_unlock(&hdata->mutex); | ||
| 1525 | cancel_delayed_work(&hdata->hotplug_work); | ||
| 1526 | cec_notifier_set_phys_addr(hdata->notifier, | ||
| 1527 | CEC_PHYS_ADDR_INVALID); | ||
| 1505 | return; | 1528 | return; |
| 1529 | } | ||
| 1506 | 1530 | ||
| 1507 | /* | 1531 | mutex_unlock(&hdata->mutex); |
| 1508 | * The SFRs of VP and Mixer are updated by Vertical Sync of | ||
| 1509 | * Timing generator which is a part of HDMI so the sequence | ||
| 1510 | * to disable TV Subsystem should be as following, | ||
| 1511 | * VP -> Mixer -> HDMI | ||
| 1512 | * | ||
| 1513 | * To achieve such sequence HDMI is disabled together with HDMI PHY, via | ||
| 1514 | * pipe clock callback. | ||
| 1515 | */ | ||
| 1516 | cancel_delayed_work(&hdata->hotplug_work); | ||
| 1517 | cec_notifier_set_phys_addr(hdata->notifier, CEC_PHYS_ADDR_INVALID); | ||
| 1518 | } | 1532 | } |
| 1519 | 1533 | ||
| 1520 | static const struct drm_encoder_helper_funcs exynos_hdmi_encoder_helper_funcs = { | 1534 | static const struct drm_encoder_helper_funcs exynos_hdmi_encoder_helper_funcs = { |
| @@ -1527,6 +1541,99 @@ static const struct drm_encoder_funcs exynos_hdmi_encoder_funcs = { | |||
| 1527 | .destroy = drm_encoder_cleanup, | 1541 | .destroy = drm_encoder_cleanup, |
| 1528 | }; | 1542 | }; |
| 1529 | 1543 | ||
| 1544 | static void hdmi_audio_shutdown(struct device *dev, void *data) | ||
| 1545 | { | ||
| 1546 | struct hdmi_context *hdata = dev_get_drvdata(dev); | ||
| 1547 | |||
| 1548 | mutex_lock(&hdata->mutex); | ||
| 1549 | |||
| 1550 | hdata->audio.mute = true; | ||
| 1551 | |||
| 1552 | if (hdata->powered) | ||
| 1553 | hdmi_audio_control(hdata); | ||
| 1554 | |||
| 1555 | mutex_unlock(&hdata->mutex); | ||
| 1556 | } | ||
| 1557 | |||
| 1558 | static int hdmi_audio_hw_params(struct device *dev, void *data, | ||
| 1559 | struct hdmi_codec_daifmt *daifmt, | ||
| 1560 | struct hdmi_codec_params *params) | ||
| 1561 | { | ||
| 1562 | struct hdmi_context *hdata = dev_get_drvdata(dev); | ||
| 1563 | |||
| 1564 | if (daifmt->fmt != HDMI_I2S || daifmt->bit_clk_inv || | ||
| 1565 | daifmt->frame_clk_inv || daifmt->bit_clk_master || | ||
| 1566 | daifmt->frame_clk_master) { | ||
| 1567 | dev_err(dev, "%s: Bad flags %d %d %d %d\n", __func__, | ||
| 1568 | daifmt->bit_clk_inv, daifmt->frame_clk_inv, | ||
| 1569 | daifmt->bit_clk_master, | ||
| 1570 | daifmt->frame_clk_master); | ||
| 1571 | return -EINVAL; | ||
| 1572 | } | ||
| 1573 | |||
| 1574 | mutex_lock(&hdata->mutex); | ||
| 1575 | |||
| 1576 | hdata->audio.params = *params; | ||
| 1577 | |||
| 1578 | if (hdata->powered) { | ||
| 1579 | hdmi_audio_config(hdata); | ||
| 1580 | hdmi_audio_infoframe_apply(hdata); | ||
| 1581 | } | ||
| 1582 | |||
| 1583 | mutex_unlock(&hdata->mutex); | ||
| 1584 | |||
| 1585 | return 0; | ||
| 1586 | } | ||
| 1587 | |||
| 1588 | static int hdmi_audio_digital_mute(struct device *dev, void *data, bool mute) | ||
| 1589 | { | ||
| 1590 | struct hdmi_context *hdata = dev_get_drvdata(dev); | ||
| 1591 | |||
| 1592 | mutex_lock(&hdata->mutex); | ||
| 1593 | |||
| 1594 | hdata->audio.mute = mute; | ||
| 1595 | |||
| 1596 | if (hdata->powered) | ||
| 1597 | hdmi_audio_control(hdata); | ||
| 1598 | |||
| 1599 | mutex_unlock(&hdata->mutex); | ||
| 1600 | |||
| 1601 | return 0; | ||
| 1602 | } | ||
| 1603 | |||
| 1604 | static int hdmi_audio_get_eld(struct device *dev, void *data, uint8_t *buf, | ||
| 1605 | size_t len) | ||
| 1606 | { | ||
| 1607 | struct hdmi_context *hdata = dev_get_drvdata(dev); | ||
| 1608 | struct drm_connector *connector = &hdata->connector; | ||
| 1609 | |||
| 1610 | memcpy(buf, connector->eld, min(sizeof(connector->eld), len)); | ||
| 1611 | |||
| 1612 | return 0; | ||
| 1613 | } | ||
| 1614 | |||
| 1615 | static const struct hdmi_codec_ops audio_codec_ops = { | ||
| 1616 | .hw_params = hdmi_audio_hw_params, | ||
| 1617 | .audio_shutdown = hdmi_audio_shutdown, | ||
| 1618 | .digital_mute = hdmi_audio_digital_mute, | ||
| 1619 | .get_eld = hdmi_audio_get_eld, | ||
| 1620 | }; | ||
| 1621 | |||
| 1622 | static int hdmi_register_audio_device(struct hdmi_context *hdata) | ||
| 1623 | { | ||
| 1624 | struct hdmi_codec_pdata codec_data = { | ||
| 1625 | .ops = &audio_codec_ops, | ||
| 1626 | .max_i2s_channels = 6, | ||
| 1627 | .i2s = 1, | ||
| 1628 | }; | ||
| 1629 | |||
| 1630 | hdata->audio.pdev = platform_device_register_data( | ||
| 1631 | hdata->dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO, | ||
| 1632 | &codec_data, sizeof(codec_data)); | ||
| 1633 | |||
| 1634 | return PTR_ERR_OR_ZERO(hdata->audio.pdev); | ||
| 1635 | } | ||
| 1636 | |||
| 1530 | static void hdmi_hotplug_work_func(struct work_struct *work) | 1637 | static void hdmi_hotplug_work_func(struct work_struct *work) |
| 1531 | { | 1638 | { |
| 1532 | struct hdmi_context *hdata; | 1639 | struct hdmi_context *hdata; |
| @@ -1602,11 +1709,14 @@ static void hdmiphy_clk_enable(struct exynos_drm_clk *clk, bool enable) | |||
| 1602 | { | 1709 | { |
| 1603 | struct hdmi_context *hdata = container_of(clk, struct hdmi_context, | 1710 | struct hdmi_context *hdata = container_of(clk, struct hdmi_context, |
| 1604 | phy_clk); | 1711 | phy_clk); |
| 1712 | mutex_lock(&hdata->mutex); | ||
| 1605 | 1713 | ||
| 1606 | if (enable) | 1714 | if (enable) |
| 1607 | hdmiphy_enable(hdata); | 1715 | hdmiphy_enable(hdata); |
| 1608 | else | 1716 | else |
| 1609 | hdmiphy_disable(hdata); | 1717 | hdmiphy_disable(hdata); |
| 1718 | |||
| 1719 | mutex_unlock(&hdata->mutex); | ||
| 1610 | } | 1720 | } |
| 1611 | 1721 | ||
| 1612 | static int hdmi_bridge_init(struct hdmi_context *hdata) | 1722 | static int hdmi_bridge_init(struct hdmi_context *hdata) |
| @@ -1817,6 +1927,7 @@ out: | |||
| 1817 | 1927 | ||
| 1818 | static int hdmi_probe(struct platform_device *pdev) | 1928 | static int hdmi_probe(struct platform_device *pdev) |
| 1819 | { | 1929 | { |
| 1930 | struct hdmi_audio_infoframe *audio_infoframe; | ||
| 1820 | struct device *dev = &pdev->dev; | 1931 | struct device *dev = &pdev->dev; |
| 1821 | struct hdmi_context *hdata; | 1932 | struct hdmi_context *hdata; |
| 1822 | struct resource *res; | 1933 | struct resource *res; |
| @@ -1832,6 +1943,8 @@ static int hdmi_probe(struct platform_device *pdev) | |||
| 1832 | 1943 | ||
| 1833 | hdata->dev = dev; | 1944 | hdata->dev = dev; |
| 1834 | 1945 | ||
| 1946 | mutex_init(&hdata->mutex); | ||
| 1947 | |||
| 1835 | ret = hdmi_resources_init(hdata); | 1948 | ret = hdmi_resources_init(hdata); |
| 1836 | if (ret) { | 1949 | if (ret) { |
| 1837 | if (ret != -EPROBE_DEFER) | 1950 | if (ret != -EPROBE_DEFER) |
| @@ -1891,12 +2004,26 @@ static int hdmi_probe(struct platform_device *pdev) | |||
| 1891 | 2004 | ||
| 1892 | pm_runtime_enable(dev); | 2005 | pm_runtime_enable(dev); |
| 1893 | 2006 | ||
| 1894 | ret = component_add(&pdev->dev, &hdmi_component_ops); | 2007 | audio_infoframe = &hdata->audio.infoframe; |
| 2008 | hdmi_audio_infoframe_init(audio_infoframe); | ||
| 2009 | audio_infoframe->coding_type = HDMI_AUDIO_CODING_TYPE_STREAM; | ||
| 2010 | audio_infoframe->sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM; | ||
| 2011 | audio_infoframe->sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM; | ||
| 2012 | audio_infoframe->channels = 2; | ||
| 2013 | |||
| 2014 | ret = hdmi_register_audio_device(hdata); | ||
| 1895 | if (ret) | 2015 | if (ret) |
| 1896 | goto err_notifier_put; | 2016 | goto err_notifier_put; |
| 1897 | 2017 | ||
| 2018 | ret = component_add(&pdev->dev, &hdmi_component_ops); | ||
| 2019 | if (ret) | ||
| 2020 | goto err_unregister_audio; | ||
| 2021 | |||
| 1898 | return ret; | 2022 | return ret; |
| 1899 | 2023 | ||
| 2024 | err_unregister_audio: | ||
| 2025 | platform_device_unregister(hdata->audio.pdev); | ||
| 2026 | |||
| 1900 | err_notifier_put: | 2027 | err_notifier_put: |
| 1901 | cec_notifier_put(hdata->notifier); | 2028 | cec_notifier_put(hdata->notifier); |
| 1902 | pm_runtime_disable(dev); | 2029 | pm_runtime_disable(dev); |
| @@ -1920,6 +2047,7 @@ static int hdmi_remove(struct platform_device *pdev) | |||
| 1920 | cec_notifier_set_phys_addr(hdata->notifier, CEC_PHYS_ADDR_INVALID); | 2047 | cec_notifier_set_phys_addr(hdata->notifier, CEC_PHYS_ADDR_INVALID); |
| 1921 | 2048 | ||
| 1922 | component_del(&pdev->dev, &hdmi_component_ops); | 2049 | component_del(&pdev->dev, &hdmi_component_ops); |
| 2050 | platform_device_unregister(hdata->audio.pdev); | ||
| 1923 | 2051 | ||
| 1924 | cec_notifier_put(hdata->notifier); | 2052 | cec_notifier_put(hdata->notifier); |
| 1925 | pm_runtime_disable(&pdev->dev); | 2053 | pm_runtime_disable(&pdev->dev); |
| @@ -1935,6 +2063,8 @@ static int hdmi_remove(struct platform_device *pdev) | |||
| 1935 | 2063 | ||
| 1936 | put_device(&hdata->ddc_adpt->dev); | 2064 | put_device(&hdata->ddc_adpt->dev); |
| 1937 | 2065 | ||
| 2066 | mutex_destroy(&hdata->mutex); | ||
| 2067 | |||
| 1938 | return 0; | 2068 | return 0; |
| 1939 | } | 2069 | } |
| 1940 | 2070 | ||
diff --git a/drivers/gpu/drm/exynos/regs-hdmi.h b/drivers/gpu/drm/exynos/regs-hdmi.h index a0507dc18d9e..04be0f7e8193 100644 --- a/drivers/gpu/drm/exynos/regs-hdmi.h +++ b/drivers/gpu/drm/exynos/regs-hdmi.h | |||
| @@ -419,11 +419,9 @@ | |||
| 419 | #define HDMI_I2S_DSD_CON HDMI_I2S_BASE(0x01c) | 419 | #define HDMI_I2S_DSD_CON HDMI_I2S_BASE(0x01c) |
| 420 | #define HDMI_I2S_MUX_CON HDMI_I2S_BASE(0x020) | 420 | #define HDMI_I2S_MUX_CON HDMI_I2S_BASE(0x020) |
| 421 | #define HDMI_I2S_CH_ST_CON HDMI_I2S_BASE(0x024) | 421 | #define HDMI_I2S_CH_ST_CON HDMI_I2S_BASE(0x024) |
| 422 | #define HDMI_I2S_CH_ST_0 HDMI_I2S_BASE(0x028) | 422 | /* n must be within range 0...(HDMI_I2S_CH_ST_MAXNUM - 1) */ |
| 423 | #define HDMI_I2S_CH_ST_1 HDMI_I2S_BASE(0x02c) | 423 | #define HDMI_I2S_CH_ST_MAXNUM 5 |
| 424 | #define HDMI_I2S_CH_ST_2 HDMI_I2S_BASE(0x030) | 424 | #define HDMI_I2S_CH_ST(n) HDMI_I2S_BASE(0x028 + 4 * (n)) |
| 425 | #define HDMI_I2S_CH_ST_3 HDMI_I2S_BASE(0x034) | ||
| 426 | #define HDMI_I2S_CH_ST_4 HDMI_I2S_BASE(0x038) | ||
| 427 | #define HDMI_I2S_CH_ST_SH_0 HDMI_I2S_BASE(0x03c) | 425 | #define HDMI_I2S_CH_ST_SH_0 HDMI_I2S_BASE(0x03c) |
| 428 | #define HDMI_I2S_CH_ST_SH_1 HDMI_I2S_BASE(0x040) | 426 | #define HDMI_I2S_CH_ST_SH_1 HDMI_I2S_BASE(0x040) |
| 429 | #define HDMI_I2S_CH_ST_SH_2 HDMI_I2S_BASE(0x044) | 427 | #define HDMI_I2S_CH_ST_SH_2 HDMI_I2S_BASE(0x044) |
