diff options
Diffstat (limited to 'drivers/gpu/drm/sti/sti_hdmi.c')
-rw-r--r-- | drivers/gpu/drm/sti/sti_hdmi.c | 179 |
1 files changed, 137 insertions, 42 deletions
diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c index e840ca5de401..1485ade98710 100644 --- a/drivers/gpu/drm/sti/sti_hdmi.c +++ b/drivers/gpu/drm/sti/sti_hdmi.c | |||
@@ -42,8 +42,17 @@ | |||
42 | #define HDMI_SW_DI_1_PKT_WORD5 0x0228 | 42 | #define HDMI_SW_DI_1_PKT_WORD5 0x0228 |
43 | #define HDMI_SW_DI_1_PKT_WORD6 0x022C | 43 | #define HDMI_SW_DI_1_PKT_WORD6 0x022C |
44 | #define HDMI_SW_DI_CFG 0x0230 | 44 | #define HDMI_SW_DI_CFG 0x0230 |
45 | #define HDMI_SW_DI_2_HEAD_WORD 0x0600 | ||
46 | #define HDMI_SW_DI_2_PKT_WORD0 0x0604 | ||
47 | #define HDMI_SW_DI_2_PKT_WORD1 0x0608 | ||
48 | #define HDMI_SW_DI_2_PKT_WORD2 0x060C | ||
49 | #define HDMI_SW_DI_2_PKT_WORD3 0x0610 | ||
50 | #define HDMI_SW_DI_2_PKT_WORD4 0x0614 | ||
51 | #define HDMI_SW_DI_2_PKT_WORD5 0x0618 | ||
52 | #define HDMI_SW_DI_2_PKT_WORD6 0x061C | ||
45 | 53 | ||
46 | #define HDMI_IFRAME_SLOT_AVI 1 | 54 | #define HDMI_IFRAME_SLOT_AVI 1 |
55 | #define HDMI_IFRAME_SLOT_AUDIO 2 | ||
47 | 56 | ||
48 | #define XCAT(prefix, x, suffix) prefix ## x ## suffix | 57 | #define XCAT(prefix, x, suffix) prefix ## x ## suffix |
49 | #define HDMI_SW_DI_N_HEAD_WORD(x) XCAT(HDMI_SW_DI_, x, _HEAD_WORD) | 58 | #define HDMI_SW_DI_N_HEAD_WORD(x) XCAT(HDMI_SW_DI_, x, _HEAD_WORD) |
@@ -99,6 +108,10 @@ | |||
99 | 108 | ||
100 | #define HDMI_STA_SW_RST BIT(1) | 109 | #define HDMI_STA_SW_RST BIT(1) |
101 | 110 | ||
111 | #define HDMI_INFOFRAME_HEADER_TYPE(x) (((x) & 0xff) << 0) | ||
112 | #define HDMI_INFOFRAME_HEADER_VERSION(x) (((x) & 0xff) << 8) | ||
113 | #define HDMI_INFOFRAME_HEADER_LEN(x) (((x) & 0x0f) << 16) | ||
114 | |||
102 | struct sti_hdmi_connector { | 115 | struct sti_hdmi_connector { |
103 | struct drm_connector drm_connector; | 116 | struct drm_connector drm_connector; |
104 | struct drm_encoder *encoder; | 117 | struct drm_encoder *encoder; |
@@ -228,6 +241,90 @@ static void hdmi_config(struct sti_hdmi *hdmi) | |||
228 | } | 241 | } |
229 | 242 | ||
230 | /** | 243 | /** |
244 | * Helper to concatenate infoframe in 32 bits word | ||
245 | * | ||
246 | * @ptr: pointer on the hdmi internal structure | ||
247 | * @data: infoframe to write | ||
248 | * @size: size to write | ||
249 | */ | ||
250 | static inline unsigned int hdmi_infoframe_subpack(const u8 *ptr, size_t size) | ||
251 | { | ||
252 | unsigned long value = 0; | ||
253 | size_t i; | ||
254 | |||
255 | for (i = size; i > 0; i--) | ||
256 | value = (value << 8) | ptr[i - 1]; | ||
257 | |||
258 | return value; | ||
259 | } | ||
260 | |||
261 | /** | ||
262 | * Helper to write info frame | ||
263 | * | ||
264 | * @hdmi: pointer on the hdmi internal structure | ||
265 | * @data: infoframe to write | ||
266 | * @size: size to write | ||
267 | */ | ||
268 | static void hdmi_infoframe_write_infopack(struct sti_hdmi *hdmi, const u8 *data) | ||
269 | { | ||
270 | const u8 *ptr = data; | ||
271 | u32 val, slot, mode, i; | ||
272 | u32 head_offset, pack_offset; | ||
273 | size_t size; | ||
274 | |||
275 | switch (*ptr) { | ||
276 | case HDMI_INFOFRAME_TYPE_AVI: | ||
277 | slot = HDMI_IFRAME_SLOT_AVI; | ||
278 | mode = HDMI_IFRAME_FIELD; | ||
279 | head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AVI); | ||
280 | pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AVI); | ||
281 | size = HDMI_AVI_INFOFRAME_SIZE; | ||
282 | break; | ||
283 | |||
284 | case HDMI_INFOFRAME_TYPE_AUDIO: | ||
285 | slot = HDMI_IFRAME_SLOT_AUDIO; | ||
286 | mode = HDMI_IFRAME_FRAME; | ||
287 | head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AUDIO); | ||
288 | pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AUDIO); | ||
289 | size = HDMI_AUDIO_INFOFRAME_SIZE; | ||
290 | break; | ||
291 | |||
292 | default: | ||
293 | DRM_ERROR("unsupported infoframe type: %#x\n", *ptr); | ||
294 | return; | ||
295 | } | ||
296 | |||
297 | /* Disable transmission slot for updated infoframe */ | ||
298 | val = hdmi_read(hdmi, HDMI_SW_DI_CFG); | ||
299 | val &= ~HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, slot); | ||
300 | hdmi_write(hdmi, val, HDMI_SW_DI_CFG); | ||
301 | |||
302 | val = HDMI_INFOFRAME_HEADER_TYPE(*ptr++); | ||
303 | val |= HDMI_INFOFRAME_HEADER_VERSION(*ptr++); | ||
304 | val |= HDMI_INFOFRAME_HEADER_LEN(*ptr++); | ||
305 | writel(val, hdmi->regs + head_offset); | ||
306 | |||
307 | /* | ||
308 | * Each subpack contains 4 bytes | ||
309 | * The First Bytes of the first subpacket must contain the checksum | ||
310 | * Packet size in increase by one. | ||
311 | */ | ||
312 | for (i = 0; i < size; i += sizeof(u32)) { | ||
313 | size_t num; | ||
314 | |||
315 | num = min_t(size_t, size - i, sizeof(u32)); | ||
316 | val = hdmi_infoframe_subpack(ptr, num); | ||
317 | ptr += sizeof(u32); | ||
318 | writel(val, hdmi->regs + pack_offset + i); | ||
319 | } | ||
320 | |||
321 | /* Enable transmission slot for updated infoframe */ | ||
322 | val = hdmi_read(hdmi, HDMI_SW_DI_CFG); | ||
323 | val |= HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_FIELD, slot); | ||
324 | hdmi_write(hdmi, val, HDMI_SW_DI_CFG); | ||
325 | } | ||
326 | |||
327 | /** | ||
231 | * Prepare and configure the AVI infoframe | 328 | * Prepare and configure the AVI infoframe |
232 | * | 329 | * |
233 | * AVI infoframe are transmitted at least once per two video field and | 330 | * 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) | |||
243 | struct drm_display_mode *mode = &hdmi->mode; | 340 | struct drm_display_mode *mode = &hdmi->mode; |
244 | struct hdmi_avi_infoframe infoframe; | 341 | struct hdmi_avi_infoframe infoframe; |
245 | u8 buffer[HDMI_INFOFRAME_SIZE(AVI)]; | 342 | u8 buffer[HDMI_INFOFRAME_SIZE(AVI)]; |
246 | u8 *frame = buffer + HDMI_INFOFRAME_HEADER_SIZE; | ||
247 | u32 val; | ||
248 | int ret; | 343 | int ret; |
249 | 344 | ||
250 | DRM_DEBUG_DRIVER("\n"); | 345 | DRM_DEBUG_DRIVER("\n"); |
@@ -266,47 +361,43 @@ static int hdmi_avi_infoframe_config(struct sti_hdmi *hdmi) | |||
266 | return ret; | 361 | return ret; |
267 | } | 362 | } |
268 | 363 | ||
269 | /* Disable transmission slot for AVI infoframe */ | 364 | hdmi_infoframe_write_infopack(hdmi, buffer); |
270 | val = hdmi_read(hdmi, HDMI_SW_DI_CFG); | ||
271 | val &= ~HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, HDMI_IFRAME_SLOT_AVI); | ||
272 | hdmi_write(hdmi, val, HDMI_SW_DI_CFG); | ||
273 | 365 | ||
274 | /* Infoframe header */ | 366 | return 0; |
275 | val = buffer[0]; | 367 | } |
276 | val |= buffer[1] << 8; | 368 | |
277 | val |= buffer[2] << 16; | 369 | /** |
278 | hdmi_write(hdmi, val, HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AVI)); | 370 | * Prepare and configure the AUDIO infoframe |
279 | 371 | * | |
280 | /* Infoframe packet bytes */ | 372 | * AUDIO infoframe are transmitted once per frame and |
281 | val = buffer[3]; | 373 | * contains information about HDMI transmission mode such as audio codec, |
282 | val |= *(frame++) << 8; | 374 | * sample size, ... |
283 | val |= *(frame++) << 16; | 375 | * |
284 | val |= *(frame++) << 24; | 376 | * @hdmi: pointer on the hdmi internal structure |
285 | hdmi_write(hdmi, val, HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AVI)); | 377 | * |
286 | 378 | * Return negative value if error occurs | |
287 | val = *(frame++); | 379 | */ |
288 | val |= *(frame++) << 8; | 380 | static int hdmi_audio_infoframe_config(struct sti_hdmi *hdmi) |
289 | val |= *(frame++) << 16; | 381 | { |
290 | val |= *(frame++) << 24; | 382 | struct hdmi_audio_infoframe infofame; |
291 | hdmi_write(hdmi, val, HDMI_SW_DI_N_PKT_WORD1(HDMI_IFRAME_SLOT_AVI)); | 383 | u8 buffer[HDMI_INFOFRAME_SIZE(AUDIO)]; |
292 | 384 | int ret; | |
293 | val = *(frame++); | 385 | |
294 | val |= *(frame++) << 8; | 386 | ret = hdmi_audio_infoframe_init(&infofame); |
295 | val |= *(frame++) << 16; | 387 | if (ret < 0) { |
296 | val |= *(frame++) << 24; | 388 | DRM_ERROR("failed to setup audio infoframe: %d\n", ret); |
297 | hdmi_write(hdmi, val, HDMI_SW_DI_N_PKT_WORD2(HDMI_IFRAME_SLOT_AVI)); | 389 | return ret; |
298 | 390 | } | |
299 | val = *(frame++); | 391 | |
300 | val |= *(frame) << 8; | 392 | infofame.channels = 2; |
301 | hdmi_write(hdmi, val, HDMI_SW_DI_N_PKT_WORD3(HDMI_IFRAME_SLOT_AVI)); | 393 | |
302 | 394 | ret = hdmi_audio_infoframe_pack(&infofame, buffer, sizeof(buffer)); | |
303 | /* Enable transmission slot for AVI infoframe | 395 | if (ret < 0) { |
304 | * According to the hdmi specification, AVI infoframe should be | 396 | DRM_ERROR("failed to pack audio infoframe: %d\n", ret); |
305 | * transmitted at least once per two video fields | 397 | return ret; |
306 | */ | 398 | } |
307 | val = hdmi_read(hdmi, HDMI_SW_DI_CFG); | 399 | |
308 | val |= HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_FIELD, HDMI_IFRAME_SLOT_AVI); | 400 | hdmi_infoframe_write_infopack(hdmi, buffer); |
309 | hdmi_write(hdmi, val, HDMI_SW_DI_CFG); | ||
310 | 401 | ||
311 | return 0; | 402 | return 0; |
312 | } | 403 | } |
@@ -427,6 +518,10 @@ static void sti_hdmi_pre_enable(struct drm_bridge *bridge) | |||
427 | if (hdmi_avi_infoframe_config(hdmi)) | 518 | if (hdmi_avi_infoframe_config(hdmi)) |
428 | DRM_ERROR("Unable to configure AVI infoframe\n"); | 519 | DRM_ERROR("Unable to configure AVI infoframe\n"); |
429 | 520 | ||
521 | /* Program AUDIO infoframe */ | ||
522 | if (hdmi_audio_infoframe_config(hdmi)) | ||
523 | DRM_ERROR("Unable to configure AUDIO infoframe\n"); | ||
524 | |||
430 | /* Sw reset */ | 525 | /* Sw reset */ |
431 | hdmi_swreset(hdmi); | 526 | hdmi_swreset(hdmi); |
432 | } | 527 | } |