提交 f150891f 编写于 作者: D Dave Airlie

Merge tag 'exynos-drm-next-for-v4.15' of...

Merge tag 'exynos-drm-next-for-v4.15' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos into drm-next

- Improved HDMI and Mixer drivers
  . It moves mode setup and plane update code to commit
    like other CRTC drivers
  . It makes mode commit to be called in enable callback only one time
  . some cleanup and fixup to HDMI and Mixer drivers.
  . It adds 1024x768, 1280x1024 and 1366x768 modes support
- Added HDMI audio interface driver
  . As of now, HDMI audio worked on boards with external audio codec connected
    in parallel with the HDMI audio transmitter's I2S interface.
    This patch is required to support HDMI audio properly.

* tag 'exynos-drm-next-for-v4.15' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos:
  drm: exynos: Add driver for HDMI audio interface
  drm/exynos/hdmi: add 85.5MHz pixel clock for v14 HDMI PHY
  drm/exynos/mixer: enable support for 1024x768 and 1280x1024 modes
  drm/exynos/hdmi: quirk for support mode timings conversion
  drm/exynos/mixer: pass actual mode on MIXER to encoder
  drm/exynos: add mode_fixup callback to exynos_drm_crtc_ops
  drm/exynos/hdmi: remove redundant mode field
  drm/exynos/mixer: remove mixer_resources sub-structure
  drm/exynos/mixer: fix mode validation code
  drm/exynos/mixer: move resolution configuration to single function
  drm/exynos/mixer: move mode commit to enable callback
  drm/exynos/mixer: abstract out output mode setup code
...@@ -3,6 +3,7 @@ config DRM_EXYNOS ...@@ -3,6 +3,7 @@ config DRM_EXYNOS
depends on OF && DRM && (ARCH_S3C64XX || ARCH_EXYNOS || ARCH_MULTIPLATFORM) depends on OF && DRM && (ARCH_S3C64XX || ARCH_EXYNOS || ARCH_MULTIPLATFORM)
select DRM_KMS_HELPER select DRM_KMS_HELPER
select VIDEOMODE_HELPERS select VIDEOMODE_HELPERS
select SND_SOC_HDMI_CODEC if SND_SOC
help help
Choose this option if you have a Samsung SoC EXYNOS chipset. Choose this option if you have a Samsung SoC EXYNOS chipset.
If M is selected the module will be called exynosdrm. If M is selected the module will be called exynosdrm.
......
...@@ -95,8 +95,23 @@ static enum drm_mode_status exynos_crtc_mode_valid(struct drm_crtc *crtc, ...@@ -95,8 +95,23 @@ static enum drm_mode_status exynos_crtc_mode_valid(struct drm_crtc *crtc,
return MODE_OK; return MODE_OK;
} }
static bool exynos_crtc_mode_fixup(struct drm_crtc *crtc,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
if (exynos_crtc->ops->mode_fixup)
return exynos_crtc->ops->mode_fixup(exynos_crtc, mode,
adjusted_mode);
return true;
}
static const struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = { static const struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
.mode_valid = exynos_crtc_mode_valid, .mode_valid = exynos_crtc_mode_valid,
.mode_fixup = exynos_crtc_mode_fixup,
.atomic_check = exynos_crtc_atomic_check, .atomic_check = exynos_crtc_atomic_check,
.atomic_begin = exynos_crtc_atomic_begin, .atomic_begin = exynos_crtc_atomic_begin,
.atomic_flush = exynos_crtc_atomic_flush, .atomic_flush = exynos_crtc_atomic_flush,
......
...@@ -136,6 +136,9 @@ struct exynos_drm_crtc_ops { ...@@ -136,6 +136,9 @@ struct exynos_drm_crtc_ops {
u32 (*get_vblank_counter)(struct exynos_drm_crtc *crtc); u32 (*get_vblank_counter)(struct exynos_drm_crtc *crtc);
enum drm_mode_status (*mode_valid)(struct exynos_drm_crtc *crtc, enum drm_mode_status (*mode_valid)(struct exynos_drm_crtc *crtc,
const struct drm_display_mode *mode); const struct drm_display_mode *mode);
bool (*mode_fixup)(struct exynos_drm_crtc *crtc,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
int (*atomic_check)(struct exynos_drm_crtc *crtc, int (*atomic_check)(struct exynos_drm_crtc *crtc,
struct drm_crtc_state *state); struct drm_crtc_state *state);
void (*atomic_begin)(struct exynos_drm_crtc *crtc); void (*atomic_begin)(struct exynos_drm_crtc *crtc);
......
...@@ -40,7 +40,7 @@ ...@@ -40,7 +40,7 @@
#include <linux/component.h> #include <linux/component.h>
#include <linux/mfd/syscon.h> #include <linux/mfd/syscon.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <sound/hdmi-codec.h>
#include <drm/exynos_drm.h> #include <drm/exynos_drm.h>
#include <media/cec-notifier.h> #include <media/cec-notifier.h>
...@@ -111,15 +111,20 @@ struct hdmi_driver_data { ...@@ -111,15 +111,20 @@ struct hdmi_driver_data {
struct string_array_spec clk_muxes; struct string_array_spec clk_muxes;
}; };
struct hdmi_audio {
struct platform_device *pdev;
struct hdmi_audio_infoframe infoframe;
struct hdmi_codec_params params;
bool mute;
};
struct hdmi_context { struct hdmi_context {
struct drm_encoder encoder; struct drm_encoder encoder;
struct device *dev; struct device *dev;
struct drm_device *drm_dev; struct drm_device *drm_dev;
struct drm_connector connector; struct drm_connector connector;
bool powered;
bool dvi_mode; bool dvi_mode;
struct delayed_work hotplug_work; struct delayed_work hotplug_work;
struct drm_display_mode current_mode;
struct cec_notifier *notifier; struct cec_notifier *notifier;
const struct hdmi_driver_data *drv_data; const struct hdmi_driver_data *drv_data;
...@@ -137,6 +142,11 @@ struct hdmi_context { ...@@ -137,6 +142,11 @@ struct hdmi_context {
struct regulator *reg_hdmi_en; struct regulator *reg_hdmi_en;
struct exynos_drm_clk phy_clk; struct exynos_drm_clk phy_clk;
struct drm_bridge *bridge; struct drm_bridge *bridge;
/* mutex protecting subsequent fields below */
struct mutex mutex;
struct hdmi_audio audio;
bool powered;
}; };
static inline struct hdmi_context *encoder_to_hdmi(struct drm_encoder *e) static inline struct hdmi_context *encoder_to_hdmi(struct drm_encoder *e)
...@@ -297,6 +307,15 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = { ...@@ -297,6 +307,15 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = {
0x54, 0x93, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80, 0x54, 0x93, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
}, },
}, },
{
.pixel_clock = 85500000,
.conf = {
0x01, 0xd1, 0x24, 0x11, 0x40, 0x40, 0xd0, 0x08,
0x84, 0xa0, 0xd6, 0xd8, 0x45, 0xa0, 0xac, 0x80,
0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
0x54, 0x90, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
},
},
{ {
.pixel_clock = 106500000, .pixel_clock = 106500000,
.conf = { .conf = {
...@@ -768,8 +787,25 @@ static int hdmi_clk_set_parents(struct hdmi_context *hdata, bool to_phy) ...@@ -768,8 +787,25 @@ static int hdmi_clk_set_parents(struct hdmi_context *hdata, bool to_phy)
return ret; return ret;
} }
static int hdmi_audio_infoframe_apply(struct hdmi_context *hdata)
{
struct hdmi_audio_infoframe *infoframe = &hdata->audio.infoframe;
u8 buf[HDMI_INFOFRAME_SIZE(AUDIO)];
int len;
len = hdmi_audio_infoframe_pack(infoframe, buf, sizeof(buf));
if (len < 0)
return len;
hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_EVERY_VSYNC);
hdmi_reg_write_buf(hdata, HDMI_AUI_HEADER0, buf, len);
return 0;
}
static void hdmi_reg_infoframes(struct hdmi_context *hdata) static void hdmi_reg_infoframes(struct hdmi_context *hdata)
{ {
struct drm_display_mode *m = &hdata->encoder.crtc->state->mode;
union hdmi_infoframe frm; union hdmi_infoframe frm;
u8 buf[25]; u8 buf[25];
int ret; int ret;
...@@ -783,8 +819,7 @@ static void hdmi_reg_infoframes(struct hdmi_context *hdata) ...@@ -783,8 +819,7 @@ static void hdmi_reg_infoframes(struct hdmi_context *hdata)
return; return;
} }
ret = drm_hdmi_avi_infoframe_from_display_mode(&frm.avi, ret = drm_hdmi_avi_infoframe_from_display_mode(&frm.avi, m, false);
&hdata->current_mode, false);
if (!ret) if (!ret)
ret = hdmi_avi_infoframe_pack(&frm.avi, buf, sizeof(buf)); ret = hdmi_avi_infoframe_pack(&frm.avi, buf, sizeof(buf));
if (ret > 0) { if (ret > 0) {
...@@ -794,8 +829,7 @@ static void hdmi_reg_infoframes(struct hdmi_context *hdata) ...@@ -794,8 +829,7 @@ static void hdmi_reg_infoframes(struct hdmi_context *hdata)
DRM_INFO("%s: invalid AVI infoframe (%d)\n", __func__, ret); DRM_INFO("%s: invalid AVI infoframe (%d)\n", __func__, ret);
} }
ret = drm_hdmi_vendor_infoframe_from_display_mode(&frm.vendor.hdmi, ret = drm_hdmi_vendor_infoframe_from_display_mode(&frm.vendor.hdmi, m);
&hdata->current_mode);
if (!ret) if (!ret)
ret = hdmi_vendor_infoframe_pack(&frm.vendor.hdmi, buf, ret = hdmi_vendor_infoframe_pack(&frm.vendor.hdmi, buf,
sizeof(buf)); sizeof(buf));
...@@ -805,15 +839,7 @@ static void hdmi_reg_infoframes(struct hdmi_context *hdata) ...@@ -805,15 +839,7 @@ static void hdmi_reg_infoframes(struct hdmi_context *hdata)
hdmi_reg_write_buf(hdata, HDMI_VSI_DATA(0), buf + 3, ret - 3); hdmi_reg_write_buf(hdata, HDMI_VSI_DATA(0), buf + 3, ret - 3);
} }
ret = hdmi_audio_infoframe_init(&frm.audio); hdmi_audio_infoframe_apply(hdata);
if (!ret) {
frm.audio.channels = 2;
ret = hdmi_audio_infoframe_pack(&frm.audio, buf, sizeof(buf));
}
if (ret > 0) {
hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_EVERY_VSYNC);
hdmi_reg_write_buf(hdata, HDMI_AUI_HEADER0, buf, ret);
}
} }
static enum drm_connector_status hdmi_detect(struct drm_connector *connector, static enum drm_connector_status hdmi_detect(struct drm_connector *connector,
...@@ -1003,23 +1029,18 @@ static void hdmi_reg_acr(struct hdmi_context *hdata, u32 freq) ...@@ -1003,23 +1029,18 @@ static void hdmi_reg_acr(struct hdmi_context *hdata, u32 freq)
hdmi_reg_writeb(hdata, HDMI_ACR_CON, 4); hdmi_reg_writeb(hdata, HDMI_ACR_CON, 4);
} }
static void hdmi_audio_init(struct hdmi_context *hdata) static void hdmi_audio_config(struct hdmi_context *hdata)
{ {
u32 sample_rate, bits_per_sample; u32 bit_ch = 1;
u32 data_num, bit_ch, sample_frq; u32 data_num, val;
u32 val; int i;
sample_rate = 44100;
bits_per_sample = 16;
switch (bits_per_sample) { switch (hdata->audio.params.sample_width) {
case 20: case 20:
data_num = 2; data_num = 2;
bit_ch = 1;
break; break;
case 24: case 24:
data_num = 3; data_num = 3;
bit_ch = 1;
break; break;
default: default:
data_num = 1; data_num = 1;
...@@ -1027,7 +1048,7 @@ static void hdmi_audio_init(struct hdmi_context *hdata) ...@@ -1027,7 +1048,7 @@ static void hdmi_audio_init(struct hdmi_context *hdata)
break; break;
} }
hdmi_reg_acr(hdata, sample_rate); hdmi_reg_acr(hdata, hdata->audio.params.sample_rate);
hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CON, HDMI_I2S_IN_DISABLE hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CON, HDMI_I2S_IN_DISABLE
| HDMI_I2S_AUD_I2S | HDMI_I2S_CUV_I2S_ENABLE | HDMI_I2S_AUD_I2S | HDMI_I2S_CUV_I2S_ENABLE
...@@ -1037,12 +1058,6 @@ static void hdmi_audio_init(struct hdmi_context *hdata) ...@@ -1037,12 +1058,6 @@ static void hdmi_audio_init(struct hdmi_context *hdata)
| HDMI_I2S_CH1_EN | HDMI_I2S_CH2_EN); | HDMI_I2S_CH1_EN | HDMI_I2S_CH2_EN);
hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CUV, HDMI_I2S_CUV_RL_EN); hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CUV, HDMI_I2S_CUV_RL_EN);
sample_frq = (sample_rate == 44100) ? 0 :
(sample_rate == 48000) ? 2 :
(sample_rate == 32000) ? 3 :
(sample_rate == 96000) ? 0xa : 0x0;
hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_DIS); hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_DIS);
hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_EN); hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_EN);
...@@ -1066,39 +1081,33 @@ static void hdmi_audio_init(struct hdmi_context *hdata) ...@@ -1066,39 +1081,33 @@ static void hdmi_audio_init(struct hdmi_context *hdata)
| HDMI_I2S_SET_SDATA_BIT(data_num) | HDMI_I2S_SET_SDATA_BIT(data_num)
| HDMI_I2S_BASIC_FORMAT); | HDMI_I2S_BASIC_FORMAT);
/* Configure register related to CUV information */ /* Configuration of the audio channel status registers */
hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_0, HDMI_I2S_CH_STATUS_MODE_0 for (i = 0; i < HDMI_I2S_CH_ST_MAXNUM; i++)
| HDMI_I2S_2AUD_CH_WITHOUT_PREEMPH hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST(i),
| HDMI_I2S_COPYRIGHT hdata->audio.params.iec.status[i]);
| HDMI_I2S_LINEAR_PCM
| HDMI_I2S_CONSUMER_FORMAT);
hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_1, HDMI_I2S_CD_PLAYER);
hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_2, HDMI_I2S_SET_SOURCE_NUM(0));
hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_3, HDMI_I2S_CLK_ACCUR_LEVEL_2
| HDMI_I2S_SET_SMP_FREQ(sample_frq));
hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_4,
HDMI_I2S_ORG_SMP_FREQ_44_1
| HDMI_I2S_WORD_LEN_MAX24_24BITS
| HDMI_I2S_WORD_LEN_MAX_24BITS);
hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_CON, HDMI_I2S_CH_STATUS_RELOAD); hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_CON, HDMI_I2S_CH_STATUS_RELOAD);
} }
static void hdmi_audio_control(struct hdmi_context *hdata, bool onoff) static void hdmi_audio_control(struct hdmi_context *hdata)
{ {
bool enable = !hdata->audio.mute;
if (hdata->dvi_mode) if (hdata->dvi_mode)
return; return;
hdmi_reg_writeb(hdata, HDMI_AUI_CON, onoff ? 2 : 0); hdmi_reg_writeb(hdata, HDMI_AUI_CON, enable ?
hdmi_reg_writemask(hdata, HDMI_CON_0, onoff ? HDMI_AVI_CON_EVERY_VSYNC : HDMI_AUI_CON_NO_TRAN);
hdmi_reg_writemask(hdata, HDMI_CON_0, enable ?
HDMI_ASP_EN : HDMI_ASP_DIS, HDMI_ASP_MASK); HDMI_ASP_EN : HDMI_ASP_DIS, HDMI_ASP_MASK);
} }
static void hdmi_start(struct hdmi_context *hdata, bool start) static void hdmi_start(struct hdmi_context *hdata, bool start)
{ {
struct drm_display_mode *m = &hdata->encoder.crtc->state->mode;
u32 val = start ? HDMI_TG_EN : 0; u32 val = start ? HDMI_TG_EN : 0;
if (hdata->current_mode.flags & DRM_MODE_FLAG_INTERLACE) if (m->flags & DRM_MODE_FLAG_INTERLACE)
val |= HDMI_FIELD_EN; val |= HDMI_FIELD_EN;
hdmi_reg_writemask(hdata, HDMI_CON_0, val, HDMI_EN); hdmi_reg_writemask(hdata, HDMI_CON_0, val, HDMI_EN);
...@@ -1168,7 +1177,7 @@ static void hdmiphy_wait_for_pll(struct hdmi_context *hdata) ...@@ -1168,7 +1177,7 @@ static void hdmiphy_wait_for_pll(struct hdmi_context *hdata)
static void hdmi_v13_mode_apply(struct hdmi_context *hdata) static void hdmi_v13_mode_apply(struct hdmi_context *hdata)
{ {
struct drm_display_mode *m = &hdata->current_mode; struct drm_display_mode *m = &hdata->encoder.crtc->state->mode;
unsigned int val; unsigned int val;
hdmi_reg_writev(hdata, HDMI_H_BLANK_0, 2, m->htotal - m->hdisplay); hdmi_reg_writev(hdata, HDMI_H_BLANK_0, 2, m->htotal - m->hdisplay);
...@@ -1247,7 +1256,19 @@ static void hdmi_v13_mode_apply(struct hdmi_context *hdata) ...@@ -1247,7 +1256,19 @@ static void hdmi_v13_mode_apply(struct hdmi_context *hdata)
static void hdmi_v14_mode_apply(struct hdmi_context *hdata) static void hdmi_v14_mode_apply(struct hdmi_context *hdata)
{ {
struct drm_display_mode *m = &hdata->current_mode; struct drm_display_mode *m = &hdata->encoder.crtc->state->mode;
struct drm_display_mode *am =
&hdata->encoder.crtc->state->adjusted_mode;
int hquirk = 0;
/*
* In case video mode coming from CRTC differs from requested one HDMI
* sometimes is able to almost properly perform conversion - only
* first line is distorted.
*/
if ((m->vdisplay != am->vdisplay) &&
(m->hdisplay == 1280 || m->hdisplay == 1024 || m->hdisplay == 1366))
hquirk = 258;
hdmi_reg_writev(hdata, HDMI_H_BLANK_0, 2, m->htotal - m->hdisplay); hdmi_reg_writev(hdata, HDMI_H_BLANK_0, 2, m->htotal - m->hdisplay);
hdmi_reg_writev(hdata, HDMI_V_LINE_0, 2, m->vtotal); hdmi_reg_writev(hdata, HDMI_V_LINE_0, 2, m->vtotal);
...@@ -1341,8 +1362,9 @@ static void hdmi_v14_mode_apply(struct hdmi_context *hdata) ...@@ -1341,8 +1362,9 @@ static void hdmi_v14_mode_apply(struct hdmi_context *hdata)
hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_6_0, 2, 0xffff); hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_6_0, 2, 0xffff);
hdmi_reg_writev(hdata, HDMI_TG_H_FSZ_L, 2, m->htotal); hdmi_reg_writev(hdata, HDMI_TG_H_FSZ_L, 2, m->htotal);
hdmi_reg_writev(hdata, HDMI_TG_HACT_ST_L, 2, m->htotal - m->hdisplay); hdmi_reg_writev(hdata, HDMI_TG_HACT_ST_L, 2,
hdmi_reg_writev(hdata, HDMI_TG_HACT_SZ_L, 2, m->hdisplay); m->htotal - m->hdisplay - hquirk);
hdmi_reg_writev(hdata, HDMI_TG_HACT_SZ_L, 2, m->hdisplay + hquirk);
hdmi_reg_writev(hdata, HDMI_TG_V_FSZ_L, 2, m->vtotal); hdmi_reg_writev(hdata, HDMI_TG_V_FSZ_L, 2, m->vtotal);
if (hdata->drv_data == &exynos5433_hdmi_driver_data) if (hdata->drv_data == &exynos5433_hdmi_driver_data)
hdmi_reg_writeb(hdata, HDMI_TG_DECON_EN, 1); hdmi_reg_writeb(hdata, HDMI_TG_DECON_EN, 1);
...@@ -1380,10 +1402,11 @@ static void hdmiphy_enable_mode_set(struct hdmi_context *hdata, bool enable) ...@@ -1380,10 +1402,11 @@ static void hdmiphy_enable_mode_set(struct hdmi_context *hdata, bool enable)
static void hdmiphy_conf_apply(struct hdmi_context *hdata) static void hdmiphy_conf_apply(struct hdmi_context *hdata)
{ {
struct drm_display_mode *m = &hdata->encoder.crtc->state->mode;
int ret; int ret;
const u8 *phy_conf; const u8 *phy_conf;
ret = hdmi_find_phy_conf(hdata, hdata->current_mode.clock * 1000); ret = hdmi_find_phy_conf(hdata, m->clock * 1000);
if (ret < 0) { if (ret < 0) {
DRM_ERROR("failed to find hdmiphy conf\n"); DRM_ERROR("failed to find hdmiphy conf\n");
return; return;
...@@ -1406,28 +1429,14 @@ static void hdmiphy_conf_apply(struct hdmi_context *hdata) ...@@ -1406,28 +1429,14 @@ static void hdmiphy_conf_apply(struct hdmi_context *hdata)
hdmiphy_wait_for_pll(hdata); hdmiphy_wait_for_pll(hdata);
} }
/* Should be called with hdata->mutex mutex held */
static void hdmi_conf_apply(struct hdmi_context *hdata) static void hdmi_conf_apply(struct hdmi_context *hdata)
{ {
hdmi_start(hdata, false); hdmi_start(hdata, false);
hdmi_conf_init(hdata); hdmi_conf_init(hdata);
hdmi_audio_init(hdata); hdmi_audio_config(hdata);
hdmi_mode_apply(hdata); hdmi_mode_apply(hdata);
hdmi_audio_control(hdata, true); hdmi_audio_control(hdata);
}
static void hdmi_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct hdmi_context *hdata = encoder_to_hdmi(encoder);
struct drm_display_mode *m = adjusted_mode;
DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%s\n",
m->hdisplay, m->vdisplay,
m->vrefresh, (m->flags & DRM_MODE_FLAG_INTERLACE) ?
"INTERLACED" : "PROGRESSIVE");
drm_mode_copy(&hdata->current_mode, m);
} }
static void hdmi_set_refclk(struct hdmi_context *hdata, bool on) static void hdmi_set_refclk(struct hdmi_context *hdata, bool on)
...@@ -1439,6 +1448,7 @@ static void hdmi_set_refclk(struct hdmi_context *hdata, bool on) ...@@ -1439,6 +1448,7 @@ static void hdmi_set_refclk(struct hdmi_context *hdata, bool on)
SYSREG_HDMI_REFCLK_INT_CLK, on ? ~0 : 0); SYSREG_HDMI_REFCLK_INT_CLK, on ? ~0 : 0);
} }
/* Should be called with hdata->mutex mutex held. */
static void hdmiphy_enable(struct hdmi_context *hdata) static void hdmiphy_enable(struct hdmi_context *hdata)
{ {
if (hdata->powered) if (hdata->powered)
...@@ -1461,6 +1471,7 @@ static void hdmiphy_enable(struct hdmi_context *hdata) ...@@ -1461,6 +1471,7 @@ static void hdmiphy_enable(struct hdmi_context *hdata)
hdata->powered = true; hdata->powered = true;
} }
/* Should be called with hdata->mutex mutex held. */
static void hdmiphy_disable(struct hdmi_context *hdata) static void hdmiphy_disable(struct hdmi_context *hdata)
{ {
if (!hdata->powered) if (!hdata->powered)
...@@ -1486,33 +1497,42 @@ static void hdmi_enable(struct drm_encoder *encoder) ...@@ -1486,33 +1497,42 @@ static void hdmi_enable(struct drm_encoder *encoder)
{ {
struct hdmi_context *hdata = encoder_to_hdmi(encoder); struct hdmi_context *hdata = encoder_to_hdmi(encoder);
mutex_lock(&hdata->mutex);
hdmiphy_enable(hdata); hdmiphy_enable(hdata);
hdmi_conf_apply(hdata); hdmi_conf_apply(hdata);
mutex_unlock(&hdata->mutex);
} }
static void hdmi_disable(struct drm_encoder *encoder) static void hdmi_disable(struct drm_encoder *encoder)
{ {
struct hdmi_context *hdata = encoder_to_hdmi(encoder); struct hdmi_context *hdata = encoder_to_hdmi(encoder);
if (!hdata->powered) mutex_lock(&hdata->mutex);
if (hdata->powered) {
/*
* The SFRs of VP and Mixer are updated by Vertical Sync of
* Timing generator which is a part of HDMI so the sequence
* to disable TV Subsystem should be as following,
* VP -> Mixer -> HDMI
*
* To achieve such sequence HDMI is disabled together with
* HDMI PHY, via pipe clock callback.
*/
mutex_unlock(&hdata->mutex);
cancel_delayed_work(&hdata->hotplug_work);
cec_notifier_set_phys_addr(hdata->notifier,
CEC_PHYS_ADDR_INVALID);
return; return;
}
/* mutex_unlock(&hdata->mutex);
* The SFRs of VP and Mixer are updated by Vertical Sync of
* Timing generator which is a part of HDMI so the sequence
* to disable TV Subsystem should be as following,
* VP -> Mixer -> HDMI
*
* To achieve such sequence HDMI is disabled together with HDMI PHY, via
* pipe clock callback.
*/
cancel_delayed_work(&hdata->hotplug_work);
cec_notifier_set_phys_addr(hdata->notifier, CEC_PHYS_ADDR_INVALID);
} }
static const struct drm_encoder_helper_funcs exynos_hdmi_encoder_helper_funcs = { static const struct drm_encoder_helper_funcs exynos_hdmi_encoder_helper_funcs = {
.mode_fixup = hdmi_mode_fixup, .mode_fixup = hdmi_mode_fixup,
.mode_set = hdmi_mode_set,
.enable = hdmi_enable, .enable = hdmi_enable,
.disable = hdmi_disable, .disable = hdmi_disable,
}; };
...@@ -1521,6 +1541,99 @@ static const struct drm_encoder_funcs exynos_hdmi_encoder_funcs = { ...@@ -1521,6 +1541,99 @@ static const struct drm_encoder_funcs exynos_hdmi_encoder_funcs = {
.destroy = drm_encoder_cleanup, .destroy = drm_encoder_cleanup,
}; };
static void hdmi_audio_shutdown(struct device *dev, void *data)
{
struct hdmi_context *hdata = dev_get_drvdata(dev);
mutex_lock(&hdata->mutex);
hdata->audio.mute = true;
if (hdata->powered)
hdmi_audio_control(hdata);
mutex_unlock(&hdata->mutex);
}
static int hdmi_audio_hw_params(struct device *dev, void *data,
struct hdmi_codec_daifmt *daifmt,
struct hdmi_codec_params *params)
{
struct hdmi_context *hdata = dev_get_drvdata(dev);
if (daifmt->fmt != HDMI_I2S || daifmt->bit_clk_inv ||
daifmt->frame_clk_inv || daifmt->bit_clk_master ||
daifmt->frame_clk_master) {
dev_err(dev, "%s: Bad flags %d %d %d %d\n", __func__,
daifmt->bit_clk_inv, daifmt->frame_clk_inv,
daifmt->bit_clk_master,
daifmt->frame_clk_master);
return -EINVAL;
}
mutex_lock(&hdata->mutex);
hdata->audio.params = *params;
if (hdata->powered) {
hdmi_audio_config(hdata);
hdmi_audio_infoframe_apply(hdata);
}
mutex_unlock(&hdata->mutex);
return 0;
}
static int hdmi_audio_digital_mute(struct device *dev, void *data, bool mute)
{
struct hdmi_context *hdata = dev_get_drvdata(dev);
mutex_lock(&hdata->mutex);
hdata->audio.mute = mute;
if (hdata->powered)
hdmi_audio_control(hdata);
mutex_unlock(&hdata->mutex);
return 0;
}
static int hdmi_audio_get_eld(struct device *dev, void *data, uint8_t *buf,
size_t len)
{
struct hdmi_context *hdata = dev_get_drvdata(dev);
struct drm_connector *connector = &hdata->connector;
memcpy(buf, connector->eld, min(sizeof(connector->eld), len));
return 0;
}
static const struct hdmi_codec_ops audio_codec_ops = {
.hw_params = hdmi_audio_hw_params,
.audio_shutdown = hdmi_audio_shutdown,
.digital_mute = hdmi_audio_digital_mute,
.get_eld = hdmi_audio_get_eld,
};
static int hdmi_register_audio_device(struct hdmi_context *hdata)
{
struct hdmi_codec_pdata codec_data = {
.ops = &audio_codec_ops,
.max_i2s_channels = 6,
.i2s = 1,
};
hdata->audio.pdev = platform_device_register_data(
hdata->dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO,
&codec_data, sizeof(codec_data));
return PTR_ERR_OR_ZERO(hdata->audio.pdev);
}
static void hdmi_hotplug_work_func(struct work_struct *work) static void hdmi_hotplug_work_func(struct work_struct *work)
{ {
struct hdmi_context *hdata; struct hdmi_context *hdata;
...@@ -1596,11 +1709,14 @@ static void hdmiphy_clk_enable(struct exynos_drm_clk *clk, bool enable) ...@@ -1596,11 +1709,14 @@ static void hdmiphy_clk_enable(struct exynos_drm_clk *clk, bool enable)
{ {
struct hdmi_context *hdata = container_of(clk, struct hdmi_context, struct hdmi_context *hdata = container_of(clk, struct hdmi_context,
phy_clk); phy_clk);
mutex_lock(&hdata->mutex);
if (enable) if (enable)
hdmiphy_enable(hdata); hdmiphy_enable(hdata);
else else
hdmiphy_disable(hdata); hdmiphy_disable(hdata);
mutex_unlock(&hdata->mutex);
} }
static int hdmi_bridge_init(struct hdmi_context *hdata) static int hdmi_bridge_init(struct hdmi_context *hdata)
...@@ -1811,6 +1927,7 @@ static int hdmi_get_phy_io(struct hdmi_context *hdata) ...@@ -1811,6 +1927,7 @@ static int hdmi_get_phy_io(struct hdmi_context *hdata)
static int hdmi_probe(struct platform_device *pdev) static int hdmi_probe(struct platform_device *pdev)
{ {
struct hdmi_audio_infoframe *audio_infoframe;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct hdmi_context *hdata; struct hdmi_context *hdata;
struct resource *res; struct resource *res;
...@@ -1826,6 +1943,8 @@ static int hdmi_probe(struct platform_device *pdev) ...@@ -1826,6 +1943,8 @@ static int hdmi_probe(struct platform_device *pdev)
hdata->dev = dev; hdata->dev = dev;
mutex_init(&hdata->mutex);
ret = hdmi_resources_init(hdata); ret = hdmi_resources_init(hdata);
if (ret) { if (ret) {
if (ret != -EPROBE_DEFER) if (ret != -EPROBE_DEFER)
...@@ -1885,12 +2004,26 @@ static int hdmi_probe(struct platform_device *pdev) ...@@ -1885,12 +2004,26 @@ static int hdmi_probe(struct platform_device *pdev)
pm_runtime_enable(dev); pm_runtime_enable(dev);
ret = component_add(&pdev->dev, &hdmi_component_ops); audio_infoframe = &hdata->audio.infoframe;
hdmi_audio_infoframe_init(audio_infoframe);
audio_infoframe->coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
audio_infoframe->sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
audio_infoframe->sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
audio_infoframe->channels = 2;
ret = hdmi_register_audio_device(hdata);
if (ret) if (ret)
goto err_notifier_put; goto err_notifier_put;
ret = component_add(&pdev->dev, &hdmi_component_ops);
if (ret)
goto err_unregister_audio;
return ret; return ret;
err_unregister_audio:
platform_device_unregister(hdata->audio.pdev);
err_notifier_put: err_notifier_put:
cec_notifier_put(hdata->notifier); cec_notifier_put(hdata->notifier);
pm_runtime_disable(dev); pm_runtime_disable(dev);
...@@ -1914,6 +2047,7 @@ static int hdmi_remove(struct platform_device *pdev) ...@@ -1914,6 +2047,7 @@ static int hdmi_remove(struct platform_device *pdev)
cec_notifier_set_phys_addr(hdata->notifier, CEC_PHYS_ADDR_INVALID); cec_notifier_set_phys_addr(hdata->notifier, CEC_PHYS_ADDR_INVALID);
component_del(&pdev->dev, &hdmi_component_ops); component_del(&pdev->dev, &hdmi_component_ops);
platform_device_unregister(hdata->audio.pdev);
cec_notifier_put(hdata->notifier); cec_notifier_put(hdata->notifier);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
...@@ -1929,6 +2063,8 @@ static int hdmi_remove(struct platform_device *pdev) ...@@ -1929,6 +2063,8 @@ static int hdmi_remove(struct platform_device *pdev)
put_device(&hdata->ddc_adpt->dev); put_device(&hdata->ddc_adpt->dev);
mutex_destroy(&hdata->mutex);
return 0; return 0;
} }
......
...@@ -419,11 +419,9 @@ ...@@ -419,11 +419,9 @@
#define HDMI_I2S_DSD_CON HDMI_I2S_BASE(0x01c) #define HDMI_I2S_DSD_CON HDMI_I2S_BASE(0x01c)
#define HDMI_I2S_MUX_CON HDMI_I2S_BASE(0x020) #define HDMI_I2S_MUX_CON HDMI_I2S_BASE(0x020)
#define HDMI_I2S_CH_ST_CON HDMI_I2S_BASE(0x024) #define HDMI_I2S_CH_ST_CON HDMI_I2S_BASE(0x024)
#define HDMI_I2S_CH_ST_0 HDMI_I2S_BASE(0x028) /* n must be within range 0...(HDMI_I2S_CH_ST_MAXNUM - 1) */
#define HDMI_I2S_CH_ST_1 HDMI_I2S_BASE(0x02c) #define HDMI_I2S_CH_ST_MAXNUM 5
#define HDMI_I2S_CH_ST_2 HDMI_I2S_BASE(0x030) #define HDMI_I2S_CH_ST(n) HDMI_I2S_BASE(0x028 + 4 * (n))
#define HDMI_I2S_CH_ST_3 HDMI_I2S_BASE(0x034)
#define HDMI_I2S_CH_ST_4 HDMI_I2S_BASE(0x038)
#define HDMI_I2S_CH_ST_SH_0 HDMI_I2S_BASE(0x03c) #define HDMI_I2S_CH_ST_SH_0 HDMI_I2S_BASE(0x03c)
#define HDMI_I2S_CH_ST_SH_1 HDMI_I2S_BASE(0x040) #define HDMI_I2S_CH_ST_SH_1 HDMI_I2S_BASE(0x040)
#define HDMI_I2S_CH_ST_SH_2 HDMI_I2S_BASE(0x044) #define HDMI_I2S_CH_ST_SH_2 HDMI_I2S_BASE(0x044)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册