Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f150891f authored by Dave Airlie's avatar Dave Airlie
Browse files

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
parents fc150d6b 691da76f
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -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.
+15 −0
Original line number Original line Diff line number Diff line
@@ -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,
+3 −0
Original line number Original line Diff line number Diff line
@@ -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);
+223 −87
Original line number Original line Diff line number Diff line
@@ -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 {
	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 {
	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[] = {
			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)
	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)
		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)
		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)
		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)
	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)
		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)
			| 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)
			| 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)


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)


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)
	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)


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)
	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)
			   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)
	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)
{
{
	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);
		return;


	if (hdata->powered) {
		/*
		/*
		 * The SFRs of VP and Mixer are updated by Vertical Sync of
		 * The SFRs of VP and Mixer are updated by Vertical Sync of
		 * Timing generator which is a part of HDMI so the sequence
		 * Timing generator which is a part of HDMI so the sequence
		 * to disable TV Subsystem should be as following,
		 * to disable TV Subsystem should be as following,
		 *	VP -> Mixer -> HDMI
		 *	VP -> Mixer -> HDMI
		 *
		 *
	 * To achieve such sequence HDMI is disabled together with HDMI PHY, via
		 * To achieve such sequence HDMI is disabled together with
	 * pipe clock callback.
		 * HDMI PHY, via pipe clock callback.
		 */
		 */
		mutex_unlock(&hdata->mutex);
		cancel_delayed_work(&hdata->hotplug_work);
		cancel_delayed_work(&hdata->hotplug_work);
	cec_notifier_set_phys_addr(hdata->notifier, CEC_PHYS_ADDR_INVALID);
		cec_notifier_set_phys_addr(hdata->notifier,
					   CEC_PHYS_ADDR_INVALID);
		return;
	}

	mutex_unlock(&hdata->mutex);
}
}


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 = {
	.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)
{
{
	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)


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)


	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)


	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)
	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)


	put_device(&hdata->ddc_adpt->dev);
	put_device(&hdata->ddc_adpt->dev);


	mutex_destroy(&hdata->mutex);

	return 0;
	return 0;
}
}


+231 −229

File changed.

Preview size limit exceeded, changes collapsed.

Loading