diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-02-17 02:48:00 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-02-17 02:48:00 +0300 |
commit | 796e1c55717e9a6ff5c81b12289ffa1ffd919b6f (patch) | |
tree | 27ce45cb1227156b72c641dbcbf2b399d23ba63d /drivers/gpu/drm/sti/sti_hdmi.c | |
parent | 8c334ce8f0fec7122fc3059c52a697b669a01b41 (diff) | |
parent | 45ee2dbc65cbf6910892c480e6f428be342fa733 (diff) | |
download | linux-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.c | 190 |
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); |