summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/sti/sti_hdmi.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-02-17 02:48:00 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2015-02-17 02:48:00 +0300
commit796e1c55717e9a6ff5c81b12289ffa1ffd919b6f (patch)
tree27ce45cb1227156b72c641dbcbf2b399d23ba63d /drivers/gpu/drm/sti/sti_hdmi.c
parent8c334ce8f0fec7122fc3059c52a697b669a01b41 (diff)
parent45ee2dbc65cbf6910892c480e6f428be342fa733 (diff)
downloadlinux-796e1c55717e9a6ff5c81b12289ffa1ffd919b6f.tar.xz
Merge branch 'drm-next' of git://people.freedesktop.org/~airlied/linux
Pull drm updates from Dave Airlie: "This is the main drm pull, it has a shared branch with some alsa crossover but everything should be acked by relevant people. New drivers: - ATMEL HLCDC driver - designware HDMI core support (used in multiple SoCs). core: - lots more atomic modesetting work, properties and atomic ioctl (hidden under option) - bridge rework allows support for Samsung exynos chromebooks to work finally. - some more panels supported i915: - atomic plane update support - DSI uses shared DSI infrastructure - Skylake basic support is all merged now - component framework used for i915/snd-hda interactions - write-combine cpu memory mappings - engine init code refactored - full ppgtt enabled where execlists are enabled. - cherryview rps/gpu turbo and pipe CRC support. radeon: - indirect draw support for evergreen/cayman - SMC and manual fan control for SI/CI - Displayport audio support amdkfd: - SDMA usermode queue support - replace suballocator usage with more suitable one - rework for allowing interfacing to more than radeon nouveau: - major renaming in prep for later splitting work - merge arm platform driver into nouveau - GK20A reclocking support msm: - conversion to atomic modesetting - YUV support for mdp4/5 - eDP support - hw cursor for mdp5 tegra: - conversion to atomic modesetting - better suspend/resume support for child devices rcar-du: - interlaced support imx: - move to using dw_hdmi shared support - mode_fixup support sti: - DVO support - HDMI infoframe support exynos: - refactoring and cleanup, removed lots of internal unnecessary abstraction - exynos7 DECON display controller support Along with the usual bunch of fixes, cleanups etc" * 'drm-next' of git://people.freedesktop.org/~airlied/linux: (724 commits) drm/radeon: fix voltage setup on hawaii drm/radeon/dp: Set EDP_CONFIGURATION_SET for bridge chips if necessary drm/radeon: only enable kv/kb dpm interrupts once v3 drm/radeon: workaround for CP HW bug on CIK drm/radeon: Don't try to enable write-combining without PAT drm/radeon: use 0-255 rather than 0-100 for pwm fan range drm/i915: Clamp efficient frequency to valid range drm/i915: Really ignore long HPD pulses on eDP drm/exynos: Add DECON driver drm/i915: Correct the base value while updating LP_OUTPUT_HOLD in MIPI_PORT_CTRL drm/i915: Insert a command barrier on BLT/BSD cache flushes drm/i915: Drop vblank wait from intel_dp_link_down drm/exynos: fix NULL pointer reference drm/exynos: remove exynos_plane_dpms drm/exynos: remove mode property of exynos crtc drm/exynos: Remove exynos_plane_dpms() call with no effect drm/i915: Squelch overzealous uncore reset WARN_ON drm/i915: Take runtime pm reference on hangcheck_info drm/i915: Correct the IOSF Dev_FN field for IOSF transfers drm/exynos: fix DMA_ATTR_NO_KERNEL_MAPPING usage ...
Diffstat (limited to 'drivers/gpu/drm/sti/sti_hdmi.c')
-rw-r--r--drivers/gpu/drm/sti/sti_hdmi.c190
1 files changed, 139 insertions, 51 deletions
diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c
index d032e024b0b8..1485ade98710 100644
--- a/drivers/gpu/drm/sti/sti_hdmi.c
+++ b/drivers/gpu/drm/sti/sti_hdmi.c
@@ -42,8 +42,17 @@
#define HDMI_SW_DI_1_PKT_WORD5 0x0228
#define HDMI_SW_DI_1_PKT_WORD6 0x022C
#define HDMI_SW_DI_CFG 0x0230
+#define HDMI_SW_DI_2_HEAD_WORD 0x0600
+#define HDMI_SW_DI_2_PKT_WORD0 0x0604
+#define HDMI_SW_DI_2_PKT_WORD1 0x0608
+#define HDMI_SW_DI_2_PKT_WORD2 0x060C
+#define HDMI_SW_DI_2_PKT_WORD3 0x0610
+#define HDMI_SW_DI_2_PKT_WORD4 0x0614
+#define HDMI_SW_DI_2_PKT_WORD5 0x0618
+#define HDMI_SW_DI_2_PKT_WORD6 0x061C
#define HDMI_IFRAME_SLOT_AVI 1
+#define HDMI_IFRAME_SLOT_AUDIO 2
#define XCAT(prefix, x, suffix) prefix ## x ## suffix
#define HDMI_SW_DI_N_HEAD_WORD(x) XCAT(HDMI_SW_DI_, x, _HEAD_WORD)
@@ -99,6 +108,10 @@
#define HDMI_STA_SW_RST BIT(1)
+#define HDMI_INFOFRAME_HEADER_TYPE(x) (((x) & 0xff) << 0)
+#define HDMI_INFOFRAME_HEADER_VERSION(x) (((x) & 0xff) << 8)
+#define HDMI_INFOFRAME_HEADER_LEN(x) (((x) & 0x0f) << 16)
+
struct sti_hdmi_connector {
struct drm_connector drm_connector;
struct drm_encoder *encoder;
@@ -228,6 +241,90 @@ static void hdmi_config(struct sti_hdmi *hdmi)
}
/**
+ * Helper to concatenate infoframe in 32 bits word
+ *
+ * @ptr: pointer on the hdmi internal structure
+ * @data: infoframe to write
+ * @size: size to write
+ */
+static inline unsigned int hdmi_infoframe_subpack(const u8 *ptr, size_t size)
+{
+ unsigned long value = 0;
+ size_t i;
+
+ for (i = size; i > 0; i--)
+ value = (value << 8) | ptr[i - 1];
+
+ return value;
+}
+
+/**
+ * Helper to write info frame
+ *
+ * @hdmi: pointer on the hdmi internal structure
+ * @data: infoframe to write
+ * @size: size to write
+ */
+static void hdmi_infoframe_write_infopack(struct sti_hdmi *hdmi, const u8 *data)
+{
+ const u8 *ptr = data;
+ u32 val, slot, mode, i;
+ u32 head_offset, pack_offset;
+ size_t size;
+
+ switch (*ptr) {
+ case HDMI_INFOFRAME_TYPE_AVI:
+ slot = HDMI_IFRAME_SLOT_AVI;
+ mode = HDMI_IFRAME_FIELD;
+ head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AVI);
+ pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AVI);
+ size = HDMI_AVI_INFOFRAME_SIZE;
+ break;
+
+ case HDMI_INFOFRAME_TYPE_AUDIO:
+ slot = HDMI_IFRAME_SLOT_AUDIO;
+ mode = HDMI_IFRAME_FRAME;
+ head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AUDIO);
+ pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AUDIO);
+ size = HDMI_AUDIO_INFOFRAME_SIZE;
+ break;
+
+ default:
+ DRM_ERROR("unsupported infoframe type: %#x\n", *ptr);
+ return;
+ }
+
+ /* Disable transmission slot for updated infoframe */
+ val = hdmi_read(hdmi, HDMI_SW_DI_CFG);
+ val &= ~HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, slot);
+ hdmi_write(hdmi, val, HDMI_SW_DI_CFG);
+
+ val = HDMI_INFOFRAME_HEADER_TYPE(*ptr++);
+ val |= HDMI_INFOFRAME_HEADER_VERSION(*ptr++);
+ val |= HDMI_INFOFRAME_HEADER_LEN(*ptr++);
+ writel(val, hdmi->regs + head_offset);
+
+ /*
+ * Each subpack contains 4 bytes
+ * The First Bytes of the first subpacket must contain the checksum
+ * Packet size in increase by one.
+ */
+ for (i = 0; i < size; i += sizeof(u32)) {
+ size_t num;
+
+ num = min_t(size_t, size - i, sizeof(u32));
+ val = hdmi_infoframe_subpack(ptr, num);
+ ptr += sizeof(u32);
+ writel(val, hdmi->regs + pack_offset + i);
+ }
+
+ /* Enable transmission slot for updated infoframe */
+ val = hdmi_read(hdmi, HDMI_SW_DI_CFG);
+ val |= HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_FIELD, slot);
+ hdmi_write(hdmi, val, HDMI_SW_DI_CFG);
+}
+
+/**
* Prepare and configure the AVI infoframe
*
* AVI infoframe are transmitted at least once per two video field and
@@ -243,8 +340,6 @@ static int hdmi_avi_infoframe_config(struct sti_hdmi *hdmi)
struct drm_display_mode *mode = &hdmi->mode;
struct hdmi_avi_infoframe infoframe;
u8 buffer[HDMI_INFOFRAME_SIZE(AVI)];
- u8 *frame = buffer + HDMI_INFOFRAME_HEADER_SIZE;
- u32 val;
int ret;
DRM_DEBUG_DRIVER("\n");
@@ -266,47 +361,43 @@ static int hdmi_avi_infoframe_config(struct sti_hdmi *hdmi)
return ret;
}
- /* Disable transmission slot for AVI infoframe */
- val = hdmi_read(hdmi, HDMI_SW_DI_CFG);
- val &= ~HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, HDMI_IFRAME_SLOT_AVI);
- hdmi_write(hdmi, val, HDMI_SW_DI_CFG);
+ hdmi_infoframe_write_infopack(hdmi, buffer);
- /* Infoframe header */
- val = buffer[0];
- val |= buffer[1] << 8;
- val |= buffer[2] << 16;
- hdmi_write(hdmi, val, HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AVI));
-
- /* Infoframe packet bytes */
- val = buffer[3];
- val |= *(frame++) << 8;
- val |= *(frame++) << 16;
- val |= *(frame++) << 24;
- hdmi_write(hdmi, val, HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AVI));
-
- val = *(frame++);
- val |= *(frame++) << 8;
- val |= *(frame++) << 16;
- val |= *(frame++) << 24;
- hdmi_write(hdmi, val, HDMI_SW_DI_N_PKT_WORD1(HDMI_IFRAME_SLOT_AVI));
-
- val = *(frame++);
- val |= *(frame++) << 8;
- val |= *(frame++) << 16;
- val |= *(frame++) << 24;
- hdmi_write(hdmi, val, HDMI_SW_DI_N_PKT_WORD2(HDMI_IFRAME_SLOT_AVI));
-
- val = *(frame++);
- val |= *(frame) << 8;
- hdmi_write(hdmi, val, HDMI_SW_DI_N_PKT_WORD3(HDMI_IFRAME_SLOT_AVI));
-
- /* Enable transmission slot for AVI infoframe
- * According to the hdmi specification, AVI infoframe should be
- * transmitted at least once per two video fields
- */
- val = hdmi_read(hdmi, HDMI_SW_DI_CFG);
- val |= HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_FIELD, HDMI_IFRAME_SLOT_AVI);
- hdmi_write(hdmi, val, HDMI_SW_DI_CFG);
+ return 0;
+}
+
+/**
+ * Prepare and configure the AUDIO infoframe
+ *
+ * AUDIO infoframe are transmitted once per frame and
+ * contains information about HDMI transmission mode such as audio codec,
+ * sample size, ...
+ *
+ * @hdmi: pointer on the hdmi internal structure
+ *
+ * Return negative value if error occurs
+ */
+static int hdmi_audio_infoframe_config(struct sti_hdmi *hdmi)
+{
+ struct hdmi_audio_infoframe infofame;
+ u8 buffer[HDMI_INFOFRAME_SIZE(AUDIO)];
+ int ret;
+
+ ret = hdmi_audio_infoframe_init(&infofame);
+ if (ret < 0) {
+ DRM_ERROR("failed to setup audio infoframe: %d\n", ret);
+ return ret;
+ }
+
+ infofame.channels = 2;
+
+ ret = hdmi_audio_infoframe_pack(&infofame, buffer, sizeof(buffer));
+ if (ret < 0) {
+ DRM_ERROR("failed to pack audio infoframe: %d\n", ret);
+ return ret;
+ }
+
+ hdmi_infoframe_write_infopack(hdmi, buffer);
return 0;
}
@@ -427,6 +518,10 @@ static void sti_hdmi_pre_enable(struct drm_bridge *bridge)
if (hdmi_avi_infoframe_config(hdmi))
DRM_ERROR("Unable to configure AVI infoframe\n");
+ /* Program AUDIO infoframe */
+ if (hdmi_audio_infoframe_config(hdmi))
+ DRM_ERROR("Unable to configure AUDIO infoframe\n");
+
/* Sw reset */
hdmi_swreset(hdmi);
}
@@ -463,19 +558,12 @@ static void sti_hdmi_bridge_nope(struct drm_bridge *bridge)
/* do nothing */
}
-static void sti_hdmi_brigde_destroy(struct drm_bridge *bridge)
-{
- drm_bridge_cleanup(bridge);
- kfree(bridge);
-}
-
static const struct drm_bridge_funcs sti_hdmi_bridge_funcs = {
.pre_enable = sti_hdmi_pre_enable,
.enable = sti_hdmi_bridge_nope,
.disable = sti_hdmi_disable,
.post_disable = sti_hdmi_bridge_nope,
.mode_set = sti_hdmi_set_mode,
- .destroy = sti_hdmi_brigde_destroy,
};
static int sti_hdmi_connector_get_modes(struct drm_connector *connector)
@@ -635,7 +723,8 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data)
goto err_adapt;
bridge->driver_private = hdmi;
- drm_bridge_init(drm_dev, bridge, &sti_hdmi_bridge_funcs);
+ bridge->funcs = &sti_hdmi_bridge_funcs;
+ drm_bridge_attach(drm_dev, bridge);
encoder->bridge = bridge;
connector->encoder = encoder;
@@ -667,7 +756,6 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data)
err_sysfs:
drm_connector_unregister(drm_connector);
err_connector:
- drm_bridge_cleanup(bridge);
drm_connector_cleanup(drm_connector);
err_adapt:
put_device(&hdmi->ddc_adapt->dev);