Commit 28ee4618 authored by Dave Airlie's avatar Dave Airlie

Merge branch 'drm/hdmi-for-3.9' of git://anongit.freedesktop.org/tegra/linux into drm-next

Thierry writes:
"Remove a duplicate implementation of the CEA VIC lookup and move the CEA
and other mode tables to drm_edid.c to make it more difficult to create
duplicates of the tables.

Add some helpers to pack CEA-861/HDMI AVI, audio and SPD infoframes into
binary buffers that can easily be written into hardware registers. A new
helper function makes it easy construct an AVI infoframe from a DRM
display mode.

Convert the Tegra and Radeon drivers to use the new HDMI helpers."
* 'drm/hdmi-for-3.9' of git://anongit.freedesktop.org/tegra/linux:
  drm/radeon: Use generic HDMI infoframe helpers
  drm/tegra: Use generic HDMI infoframe helpers
  drm: Add EDID helper documentation
  drm: Add HDMI infoframe helpers
  video: Add generic HDMI infoframe helpers
  drm: Add some missing forward declarations
  drm: Move mode tables to drm_edid.c
  drm: Remove duplicate drm_mode_cea_vic()
parents a497bfe9 e3b2e034
...@@ -2157,6 +2157,10 @@ void intel_crt_init(struct drm_device *dev) ...@@ -2157,6 +2157,10 @@ void intel_crt_init(struct drm_device *dev)
!Iinclude/drm/drm_dp_helper.h !Iinclude/drm/drm_dp_helper.h
!Edrivers/gpu/drm/drm_dp_helper.c !Edrivers/gpu/drm/drm_dp_helper.c
</sect2> </sect2>
<sect2>
<title>EDID Helper Functions Reference</title>
!Edrivers/gpu/drm/drm_edid.c
</sect2>
</sect1> </sect1>
<!-- Internals: vertical blanking --> <!-- Internals: vertical blanking -->
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
menuconfig DRM menuconfig DRM
tristate "Direct Rendering Manager (XFree86 4.1.0 and higher DRI support)" tristate "Direct Rendering Manager (XFree86 4.1.0 and higher DRI support)"
depends on (AGP || AGP=n) && !EMULATED_CMPXCHG && MMU depends on (AGP || AGP=n) && !EMULATED_CMPXCHG && MMU
select HDMI
select I2C select I2C
select I2C_ALGOBIT select I2C_ALGOBIT
select DMA_SHARED_BUFFER select DMA_SHARED_BUFFER
......
This diff is collapsed.
This diff is collapsed.
...@@ -770,7 +770,7 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, ...@@ -770,7 +770,7 @@ intel_dp_mode_fixup(struct drm_encoder *encoder,
* CEA-861-E - 5.1 Default Encoding Parameters * CEA-861-E - 5.1 Default Encoding Parameters
* VESA DisplayPort Ver.1.2a - 5.1.1.1 Video Colorimetry * VESA DisplayPort Ver.1.2a - 5.1.1.1 Video Colorimetry
*/ */
if (bpp != 18 && drm_mode_cea_vic(adjusted_mode) > 1) if (bpp != 18 && drm_match_cea_mode(adjusted_mode) > 1)
intel_dp->color_range = DP_COLOR_RANGE_16_235; intel_dp->color_range = DP_COLOR_RANGE_16_235;
else else
intel_dp->color_range = 0; intel_dp->color_range = 0;
......
...@@ -348,7 +348,7 @@ static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder, ...@@ -348,7 +348,7 @@ static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder,
avi_if.body.avi.ITC_EC_Q_SC |= DIP_AVI_RGB_QUANT_RANGE_FULL; avi_if.body.avi.ITC_EC_Q_SC |= DIP_AVI_RGB_QUANT_RANGE_FULL;
} }
avi_if.body.avi.VIC = drm_mode_cea_vic(adjusted_mode); avi_if.body.avi.VIC = drm_match_cea_mode(adjusted_mode);
intel_set_infoframe(encoder, &avi_if); intel_set_infoframe(encoder, &avi_if);
} }
...@@ -781,7 +781,7 @@ bool intel_hdmi_mode_fixup(struct drm_encoder *encoder, ...@@ -781,7 +781,7 @@ bool intel_hdmi_mode_fixup(struct drm_encoder *encoder,
if (intel_hdmi->color_range_auto) { if (intel_hdmi->color_range_auto) {
/* See CEA-861-E - 5.1 Default Encoding Parameters */ /* See CEA-861-E - 5.1 Default Encoding Parameters */
if (intel_hdmi->has_hdmi_sink && if (intel_hdmi->has_hdmi_sink &&
drm_mode_cea_vic(adjusted_mode) > 1) drm_match_cea_mode(adjusted_mode) > 1)
intel_hdmi->color_range = SDVO_COLOR_RANGE_16_235; intel_hdmi->color_range = SDVO_COLOR_RANGE_16_235;
else else
intel_hdmi->color_range = 0; intel_hdmi->color_range = 0;
......
...@@ -1077,7 +1077,7 @@ static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder, ...@@ -1077,7 +1077,7 @@ static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder,
if (intel_sdvo->color_range_auto) { if (intel_sdvo->color_range_auto) {
/* See CEA-861-E - 5.1 Default Encoding Parameters */ /* See CEA-861-E - 5.1 Default Encoding Parameters */
if (intel_sdvo->has_hdmi_monitor && if (intel_sdvo->has_hdmi_monitor &&
drm_mode_cea_vic(adjusted_mode) > 1) drm_match_cea_mode(adjusted_mode) > 1)
intel_sdvo->color_range = SDVO_COLOR_RANGE_16_235; intel_sdvo->color_range = SDVO_COLOR_RANGE_16_235;
else else
intel_sdvo->color_range = 0; intel_sdvo->color_range = 0;
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
* Authors: Christian König * Authors: Christian König
* Rafał Miłecki * Rafał Miłecki
*/ */
#include <linux/hdmi.h>
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/radeon_drm.h> #include <drm/radeon_drm.h>
#include "radeon.h" #include "radeon.h"
...@@ -53,80 +54,19 @@ static void evergreen_hdmi_update_ACR(struct drm_encoder *encoder, uint32_t cloc ...@@ -53,80 +54,19 @@ static void evergreen_hdmi_update_ACR(struct drm_encoder *encoder, uint32_t cloc
WREG32(HDMI_ACR_48_1 + offset, acr.n_48khz); WREG32(HDMI_ACR_48_1 + offset, acr.n_48khz);
} }
/*
* calculate the crc for a given info frame
*/
static void evergreen_hdmi_infoframe_checksum(uint8_t packetType,
uint8_t versionNumber,
uint8_t length,
uint8_t *frame)
{
int i;
frame[0] = packetType + versionNumber + length;
for (i = 1; i <= length; i++)
frame[0] += frame[i];
frame[0] = 0x100 - frame[0];
}
/* /*
* build a HDMI Video Info Frame * build a HDMI Video Info Frame
*/ */
static void evergreen_hdmi_videoinfoframe( static void evergreen_hdmi_update_avi_infoframe(struct drm_encoder *encoder,
struct drm_encoder *encoder, void *buffer, size_t size)
uint8_t color_format,
int active_information_present,
uint8_t active_format_aspect_ratio,
uint8_t scan_information,
uint8_t colorimetry,
uint8_t ex_colorimetry,
uint8_t quantization,
int ITC,
uint8_t picture_aspect_ratio,
uint8_t video_format_identification,
uint8_t pixel_repetition,
uint8_t non_uniform_picture_scaling,
uint8_t bar_info_data_valid,
uint16_t top_bar,
uint16_t bottom_bar,
uint16_t left_bar,
uint16_t right_bar
)
{ {
struct drm_device *dev = encoder->dev; struct drm_device *dev = encoder->dev;
struct radeon_device *rdev = dev->dev_private; struct radeon_device *rdev = dev->dev_private;
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
uint32_t offset = dig->afmt->offset; uint32_t offset = dig->afmt->offset;
uint8_t *frame = buffer + 3;
uint8_t frame[14];
frame[0x0] = 0;
frame[0x1] =
(scan_information & 0x3) |
((bar_info_data_valid & 0x3) << 2) |
((active_information_present & 0x1) << 4) |
((color_format & 0x3) << 5);
frame[0x2] =
(active_format_aspect_ratio & 0xF) |
((picture_aspect_ratio & 0x3) << 4) |
((colorimetry & 0x3) << 6);
frame[0x3] =
(non_uniform_picture_scaling & 0x3) |
((quantization & 0x3) << 2) |
((ex_colorimetry & 0x7) << 4) |
((ITC & 0x1) << 7);
frame[0x4] = (video_format_identification & 0x7F);
frame[0x5] = (pixel_repetition & 0xF);
frame[0x6] = (top_bar & 0xFF);
frame[0x7] = (top_bar >> 8);
frame[0x8] = (bottom_bar & 0xFF);
frame[0x9] = (bottom_bar >> 8);
frame[0xA] = (left_bar & 0xFF);
frame[0xB] = (left_bar >> 8);
frame[0xC] = (right_bar & 0xFF);
frame[0xD] = (right_bar >> 8);
evergreen_hdmi_infoframe_checksum(0x82, 0x02, 0x0D, frame);
/* Our header values (type, version, length) should be alright, Intel /* Our header values (type, version, length) should be alright, Intel
* is using the same. Checksum function also seems to be OK, it works * is using the same. Checksum function also seems to be OK, it works
* fine for audio infoframe. However calculated value is always lower * fine for audio infoframe. However calculated value is always lower
...@@ -154,7 +94,10 @@ void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode ...@@ -154,7 +94,10 @@ void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode
struct radeon_device *rdev = dev->dev_private; struct radeon_device *rdev = dev->dev_private;
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
u8 buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AVI_INFOFRAME_SIZE];
struct hdmi_avi_infoframe frame;
uint32_t offset; uint32_t offset;
ssize_t err;
/* Silent, r600_hdmi_enable will raise WARN for us */ /* Silent, r600_hdmi_enable will raise WARN for us */
if (!dig->afmt->enabled) if (!dig->afmt->enabled)
...@@ -200,9 +143,19 @@ void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode ...@@ -200,9 +143,19 @@ void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode
WREG32(HDMI_GC + offset, 0); /* unset HDMI_GC_AVMUTE */ WREG32(HDMI_GC + offset, 0); /* unset HDMI_GC_AVMUTE */
evergreen_hdmi_videoinfoframe(encoder, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, err = drm_hdmi_avi_infoframe_from_display_mode(&frame, mode);
0, 0, 0, 0, 0, 0); if (err < 0) {
DRM_ERROR("failed to setup AVI infoframe: %zd\n", err);
return;
}
err = hdmi_avi_infoframe_pack(&frame, buffer, sizeof(buffer));
if (err < 0) {
DRM_ERROR("failed to pack AVI infoframe: %zd\n", err);
return;
}
evergreen_hdmi_update_avi_infoframe(encoder, buffer, sizeof(buffer));
evergreen_hdmi_update_ACR(encoder, mode->clock); evergreen_hdmi_update_ACR(encoder, mode->clock);
/* it's unknown what these bits do excatly, but it's indeed quite useful for debugging */ /* it's unknown what these bits do excatly, but it's indeed quite useful for debugging */
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
* *
* Authors: Christian König * Authors: Christian König
*/ */
#include <linux/hdmi.h>
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/radeon_drm.h> #include <drm/radeon_drm.h>
#include "radeon.h" #include "radeon.h"
...@@ -120,80 +121,19 @@ static void r600_hdmi_update_ACR(struct drm_encoder *encoder, uint32_t clock) ...@@ -120,80 +121,19 @@ static void r600_hdmi_update_ACR(struct drm_encoder *encoder, uint32_t clock)
WREG32(HDMI0_ACR_48_1 + offset, acr.n_48khz); WREG32(HDMI0_ACR_48_1 + offset, acr.n_48khz);
} }
/*
* calculate the crc for a given info frame
*/
static void r600_hdmi_infoframe_checksum(uint8_t packetType,
uint8_t versionNumber,
uint8_t length,
uint8_t *frame)
{
int i;
frame[0] = packetType + versionNumber + length;
for (i = 1; i <= length; i++)
frame[0] += frame[i];
frame[0] = 0x100 - frame[0];
}
/* /*
* build a HDMI Video Info Frame * build a HDMI Video Info Frame
*/ */
static void r600_hdmi_videoinfoframe( static void r600_hdmi_update_avi_infoframe(struct drm_encoder *encoder,
struct drm_encoder *encoder, void *buffer, size_t size)
enum r600_hdmi_color_format color_format,
int active_information_present,
uint8_t active_format_aspect_ratio,
uint8_t scan_information,
uint8_t colorimetry,
uint8_t ex_colorimetry,
uint8_t quantization,
int ITC,
uint8_t picture_aspect_ratio,
uint8_t video_format_identification,
uint8_t pixel_repetition,
uint8_t non_uniform_picture_scaling,
uint8_t bar_info_data_valid,
uint16_t top_bar,
uint16_t bottom_bar,
uint16_t left_bar,
uint16_t right_bar
)
{ {
struct drm_device *dev = encoder->dev; struct drm_device *dev = encoder->dev;
struct radeon_device *rdev = dev->dev_private; struct radeon_device *rdev = dev->dev_private;
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
uint32_t offset = dig->afmt->offset; uint32_t offset = dig->afmt->offset;
uint8_t *frame = buffer + 3;
uint8_t frame[14];
frame[0x0] = 0;
frame[0x1] =
(scan_information & 0x3) |
((bar_info_data_valid & 0x3) << 2) |
((active_information_present & 0x1) << 4) |
((color_format & 0x3) << 5);
frame[0x2] =
(active_format_aspect_ratio & 0xF) |
((picture_aspect_ratio & 0x3) << 4) |
((colorimetry & 0x3) << 6);
frame[0x3] =
(non_uniform_picture_scaling & 0x3) |
((quantization & 0x3) << 2) |
((ex_colorimetry & 0x7) << 4) |
((ITC & 0x1) << 7);
frame[0x4] = (video_format_identification & 0x7F);
frame[0x5] = (pixel_repetition & 0xF);
frame[0x6] = (top_bar & 0xFF);
frame[0x7] = (top_bar >> 8);
frame[0x8] = (bottom_bar & 0xFF);
frame[0x9] = (bottom_bar >> 8);
frame[0xA] = (left_bar & 0xFF);
frame[0xB] = (left_bar >> 8);
frame[0xC] = (right_bar & 0xFF);
frame[0xD] = (right_bar >> 8);
r600_hdmi_infoframe_checksum(0x82, 0x02, 0x0D, frame);
/* Our header values (type, version, length) should be alright, Intel /* Our header values (type, version, length) should be alright, Intel
* is using the same. Checksum function also seems to be OK, it works * is using the same. Checksum function also seems to be OK, it works
* fine for audio infoframe. However calculated value is always lower * fine for audio infoframe. However calculated value is always lower
...@@ -215,39 +155,15 @@ static void r600_hdmi_videoinfoframe( ...@@ -215,39 +155,15 @@ static void r600_hdmi_videoinfoframe(
/* /*
* build a Audio Info Frame * build a Audio Info Frame
*/ */
static void r600_hdmi_audioinfoframe( static void r600_hdmi_update_audio_infoframe(struct drm_encoder *encoder,
struct drm_encoder *encoder, const void *buffer, size_t size)
uint8_t channel_count,
uint8_t coding_type,
uint8_t sample_size,
uint8_t sample_frequency,
uint8_t format,
uint8_t channel_allocation,
uint8_t level_shift,
int downmix_inhibit
)
{ {
struct drm_device *dev = encoder->dev; struct drm_device *dev = encoder->dev;
struct radeon_device *rdev = dev->dev_private; struct radeon_device *rdev = dev->dev_private;
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
uint32_t offset = dig->afmt->offset; uint32_t offset = dig->afmt->offset;
const u8 *frame = buffer + 3;
uint8_t frame[11];
frame[0x0] = 0;
frame[0x1] = (channel_count & 0x7) | ((coding_type & 0xF) << 4);
frame[0x2] = (sample_size & 0x3) | ((sample_frequency & 0x7) << 2);
frame[0x3] = format;
frame[0x4] = channel_allocation;
frame[0x5] = ((level_shift & 0xF) << 3) | ((downmix_inhibit & 0x1) << 7);
frame[0x6] = 0;
frame[0x7] = 0;
frame[0x8] = 0;
frame[0x9] = 0;
frame[0xA] = 0;
r600_hdmi_infoframe_checksum(0x84, 0x01, 0x0A, frame);
WREG32(HDMI0_AUDIO_INFO0 + offset, WREG32(HDMI0_AUDIO_INFO0 + offset,
frame[0x0] | (frame[0x1] << 8) | (frame[0x2] << 16) | (frame[0x3] << 24)); frame[0x0] | (frame[0x1] << 8) | (frame[0x2] << 16) | (frame[0x3] << 24));
...@@ -320,7 +236,10 @@ void r600_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mod ...@@ -320,7 +236,10 @@ void r600_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mod
struct radeon_device *rdev = dev->dev_private; struct radeon_device *rdev = dev->dev_private;
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
u8 buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AVI_INFOFRAME_SIZE];
struct hdmi_avi_infoframe frame;
uint32_t offset; uint32_t offset;
ssize_t err;
/* Silent, r600_hdmi_enable will raise WARN for us */ /* Silent, r600_hdmi_enable will raise WARN for us */
if (!dig->afmt->enabled) if (!dig->afmt->enabled)
...@@ -371,9 +290,19 @@ void r600_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mod ...@@ -371,9 +290,19 @@ void r600_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mod
WREG32(HDMI0_GC + offset, 0); /* unset HDMI0_GC_AVMUTE */ WREG32(HDMI0_GC + offset, 0); /* unset HDMI0_GC_AVMUTE */
r600_hdmi_videoinfoframe(encoder, RGB, 0, 0, 0, 0, err = drm_hdmi_avi_infoframe_from_display_mode(&frame, mode);
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); if (err < 0) {
DRM_ERROR("failed to setup AVI infoframe: %zd\n", err);
return;
}
err = hdmi_avi_infoframe_pack(&frame, buffer, sizeof(buffer));
if (err < 0) {
DRM_ERROR("failed to pack AVI infoframe: %zd\n", err);
return;
}
r600_hdmi_update_avi_infoframe(encoder, buffer, sizeof(buffer));
r600_hdmi_update_ACR(encoder, mode->clock); r600_hdmi_update_ACR(encoder, mode->clock);
/* it's unknown what these bits do excatly, but it's indeed quite useful for debugging */ /* it's unknown what these bits do excatly, but it's indeed quite useful for debugging */
...@@ -395,8 +324,11 @@ void r600_hdmi_update_audio_settings(struct drm_encoder *encoder) ...@@ -395,8 +324,11 @@ void r600_hdmi_update_audio_settings(struct drm_encoder *encoder)
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
struct r600_audio audio = r600_audio_status(rdev); struct r600_audio audio = r600_audio_status(rdev);
uint8_t buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AUDIO_INFOFRAME_SIZE];
struct hdmi_audio_infoframe frame;
uint32_t offset; uint32_t offset;
uint32_t iec; uint32_t iec;
ssize_t err;
if (!dig->afmt || !dig->afmt->enabled) if (!dig->afmt || !dig->afmt->enabled)
return; return;
...@@ -462,9 +394,21 @@ void r600_hdmi_update_audio_settings(struct drm_encoder *encoder) ...@@ -462,9 +394,21 @@ void r600_hdmi_update_audio_settings(struct drm_encoder *encoder)
iec |= 0x5 << 16; iec |= 0x5 << 16;
WREG32_P(HDMI0_60958_1 + offset, iec, ~0x5000f); WREG32_P(HDMI0_60958_1 + offset, iec, ~0x5000f);
r600_hdmi_audioinfoframe(encoder, audio.channels - 1, 0, 0, 0, 0, 0, 0, err = hdmi_audio_infoframe_init(&frame);
0); if (err < 0) {
DRM_ERROR("failed to setup audio infoframe\n");
return;
}
frame.channels = audio.channels;
err = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
if (err < 0) {
DRM_ERROR("failed to pack audio infoframe\n");
return;
}
r600_hdmi_update_audio_infoframe(encoder, buffer, sizeof(buffer));
r600_hdmi_audio_workaround(encoder); r600_hdmi_audio_workaround(encoder);
} }
......
...@@ -4,6 +4,7 @@ config DRM_TEGRA ...@@ -4,6 +4,7 @@ config DRM_TEGRA
select DRM_KMS_HELPER select DRM_KMS_HELPER
select DRM_GEM_CMA_HELPER select DRM_GEM_CMA_HELPER
select DRM_KMS_CMA_HELPER select DRM_KMS_CMA_HELPER
select DRM_HDMI
select FB_CFB_FILLRECT select FB_CFB_FILLRECT
select FB_CFB_COPYAREA select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT select FB_CFB_IMAGEBLIT
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/hdmi.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
...@@ -17,6 +18,8 @@ ...@@ -17,6 +18,8 @@
#include <mach/clk.h> #include <mach/clk.h>
#include <drm/drm_edid.h>
#include "hdmi.h" #include "hdmi.h"
#include "drm.h" #include "drm.h"
#include "dc.h" #include "dc.h"
...@@ -401,54 +404,65 @@ static int tegra_hdmi_setup_audio(struct tegra_hdmi *hdmi, unsigned int pclk) ...@@ -401,54 +404,65 @@ static int tegra_hdmi_setup_audio(struct tegra_hdmi *hdmi, unsigned int pclk)
return 0; return 0;
} }
static void tegra_hdmi_write_infopack(struct tegra_hdmi *hdmi, static inline unsigned long tegra_hdmi_subpack(const u8 *ptr, size_t size)
unsigned int offset, u8 type,
u8 version, void *data, size_t size)
{ {
unsigned long value; unsigned long value = 0;
u8 *ptr = data;
u32 subpack[2];
size_t i; size_t i;
u8 csum;
/* first byte of data is the checksum */ for (i = size; i > 0; i--)
csum = type + version + size - 1; value = (value << 8) | ptr[i - 1];
for (i = 1; i < size; i++) return value;
csum += ptr[i]; }
ptr[0] = 0x100 - csum; static void tegra_hdmi_write_infopack(struct tegra_hdmi *hdmi, const void *data,
size_t size)
{
const u8 *ptr = data;
unsigned long offset;
unsigned long value;
size_t i, j;
value = INFOFRAME_HEADER_TYPE(type) | switch (ptr[0]) {
INFOFRAME_HEADER_VERSION(version) | case HDMI_INFOFRAME_TYPE_AVI:
INFOFRAME_HEADER_LEN(size - 1); offset = HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_HEADER;
tegra_hdmi_writel(hdmi, value, offset); break;
/* The audio inforame only has one set of subpack registers. The hdmi case HDMI_INFOFRAME_TYPE_AUDIO:
* block pads the rest of the data as per the spec so we have to fixup offset = HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER;
* the length before filling in the subpacks. break;
*/
if (offset == HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER)
size = 6;
/* each subpack 7 bytes devided into: case HDMI_INFOFRAME_TYPE_VENDOR:
* subpack_low - bytes 0 - 3 offset = HDMI_NV_PDISP_HDMI_GENERIC_HEADER;
* subpack_high - bytes 4 - 6 (with byte 7 padded to 0x00) break;
*/
for (i = 0; i < size; i++) { default:
size_t index = i % 7; dev_err(hdmi->dev, "unsupported infoframe type: %02x\n",
ptr[0]);
return;
}
value = INFOFRAME_HEADER_TYPE(ptr[0]) |
INFOFRAME_HEADER_VERSION(ptr[1]) |
INFOFRAME_HEADER_LEN(ptr[2]);
tegra_hdmi_writel(hdmi, value, offset);
offset++;
if (index == 0) /*
memset(subpack, 0x0, sizeof(subpack)); * Each subpack contains 7 bytes, divided into:
* - subpack_low: bytes 0 - 3
* - subpack_high: bytes 4 - 6 (with byte 7 padded to 0x00)
*/
for (i = 3, j = 0; i < size; i += 7, j += 8) {
size_t rem = size - i, num = min_t(size_t, rem, 4);
((u8 *)subpack)[index] = ptr[i]; value = tegra_hdmi_subpack(&ptr[i], num);
tegra_hdmi_writel(hdmi, value, offset++);
if (index == 6 || (i + 1 == size)) { num = min_t(size_t, rem - num, 3);
unsigned int reg = offset + 1 + (i / 7) * 2;
tegra_hdmi_writel(hdmi, subpack[0], reg); value = tegra_hdmi_subpack(&ptr[i + 4], num);
tegra_hdmi_writel(hdmi, subpack[1], reg + 1); tegra_hdmi_writel(hdmi, value, offset++);
}
} }
} }
...@@ -456,9 +470,8 @@ static void tegra_hdmi_setup_avi_infoframe(struct tegra_hdmi *hdmi, ...@@ -456,9 +470,8 @@ static void tegra_hdmi_setup_avi_infoframe(struct tegra_hdmi *hdmi,
struct drm_display_mode *mode) struct drm_display_mode *mode)
{ {
struct hdmi_avi_infoframe frame; struct hdmi_avi_infoframe frame;
unsigned int h_front_porch; u8 buffer[17];
unsigned int hsize = 16; ssize_t err;
unsigned int vsize = 9;
if (hdmi->dvi) { if (hdmi->dvi) {
tegra_hdmi_writel(hdmi, 0, tegra_hdmi_writel(hdmi, 0,
...@@ -466,69 +479,19 @@ static void tegra_hdmi_setup_avi_infoframe(struct tegra_hdmi *hdmi, ...@@ -466,69 +479,19 @@ static void tegra_hdmi_setup_avi_infoframe(struct tegra_hdmi *hdmi,
return; return;
} }
h_front_porch = mode->hsync_start - mode->hdisplay; err = drm_hdmi_avi_infoframe_from_display_mode(&frame, mode);
memset(&frame, 0, sizeof(frame)); if (err < 0) {
frame.r = HDMI_AVI_R_SAME; dev_err(hdmi->dev, "failed to setup AVI infoframe: %zd\n", err);
return;
switch (mode->vdisplay) { }
case 480:
if (mode->hdisplay == 640) {
frame.m = HDMI_AVI_M_4_3;
frame.vic = 1;
} else {
frame.m = HDMI_AVI_M_16_9;
frame.vic = 3;
}
break;
case 576:
if (((hsize * 10) / vsize) > 14) {
frame.m = HDMI_AVI_M_16_9;
frame.vic = 18;
} else {
frame.m = HDMI_AVI_M_4_3;
frame.vic = 17;
}
break;
case 720:
case 1470: /* stereo mode */
frame.m = HDMI_AVI_M_16_9;
if (h_front_porch == 110)
frame.vic = 4;
else
frame.vic = 19;
break;
case 1080:
case 2205: /* stereo mode */
frame.m = HDMI_AVI_M_16_9;
switch (h_front_porch) {
case 88:
frame.vic = 16;
break;
case 528:
frame.vic = 31;
break;
default:
frame.vic = 32;
break;
}
break;
default: err = hdmi_avi_infoframe_pack(&frame, buffer, sizeof(buffer));
frame.m = HDMI_AVI_M_16_9; if (err < 0) {
frame.vic = 0; dev_err(hdmi->dev, "failed to pack AVI infoframe: %zd\n", err);
break; return;
} }
tegra_hdmi_write_infopack(hdmi, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_HEADER, tegra_hdmi_write_infopack(hdmi, buffer, err);
HDMI_INFOFRAME_TYPE_AVI, HDMI_AVI_VERSION,
&frame, sizeof(frame));
tegra_hdmi_writel(hdmi, INFOFRAME_CTRL_ENABLE, tegra_hdmi_writel(hdmi, INFOFRAME_CTRL_ENABLE,
HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL); HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL);
...@@ -537,6 +500,8 @@ static void tegra_hdmi_setup_avi_infoframe(struct tegra_hdmi *hdmi, ...@@ -537,6 +500,8 @@ static void tegra_hdmi_setup_avi_infoframe(struct tegra_hdmi *hdmi,
static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi) static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi)
{ {
struct hdmi_audio_infoframe frame; struct hdmi_audio_infoframe frame;
u8 buffer[14];
ssize_t err;
if (hdmi->dvi) { if (hdmi->dvi) {
tegra_hdmi_writel(hdmi, 0, tegra_hdmi_writel(hdmi, 0,
...@@ -544,14 +509,29 @@ static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi) ...@@ -544,14 +509,29 @@ static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi)
return; return;
} }
memset(&frame, 0, sizeof(frame)); err = hdmi_audio_infoframe_init(&frame);
frame.cc = HDMI_AUDIO_CC_2; if (err < 0) {
dev_err(hdmi->dev, "failed to initialize audio infoframe: %d\n",
err);
return;
}
frame.channels = 2;
err = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
if (err < 0) {
dev_err(hdmi->dev, "failed to pack audio infoframe: %zd\n",
err);
return;
}
tegra_hdmi_write_infopack(hdmi, /*
HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER, * The audio infoframe has only one set of subpack registers, so the
HDMI_INFOFRAME_TYPE_AUDIO, * infoframe needs to be truncated. One set of subpack registers can
HDMI_AUDIO_VERSION, * contain 7 bytes. Including the 3 byte header only the first 10
&frame, sizeof(frame)); * bytes can be programmed.
*/
tegra_hdmi_write_infopack(hdmi, buffer, min(10, err));
tegra_hdmi_writel(hdmi, INFOFRAME_CTRL_ENABLE, tegra_hdmi_writel(hdmi, INFOFRAME_CTRL_ENABLE,
HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL); HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL);
...@@ -559,8 +539,10 @@ static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi) ...@@ -559,8 +539,10 @@ static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi)
static void tegra_hdmi_setup_stereo_infoframe(struct tegra_hdmi *hdmi) static void tegra_hdmi_setup_stereo_infoframe(struct tegra_hdmi *hdmi)
{ {
struct hdmi_stereo_infoframe frame; struct hdmi_vendor_infoframe frame;
unsigned long value; unsigned long value;
u8 buffer[10];
ssize_t err;
if (!hdmi->stereo) { if (!hdmi->stereo) {
value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_CTRL);
...@@ -570,22 +552,32 @@ static void tegra_hdmi_setup_stereo_infoframe(struct tegra_hdmi *hdmi) ...@@ -570,22 +552,32 @@ static void tegra_hdmi_setup_stereo_infoframe(struct tegra_hdmi *hdmi)
} }
memset(&frame, 0, sizeof(frame)); memset(&frame, 0, sizeof(frame));
frame.regid0 = 0x03;
frame.regid1 = 0x0c; frame.type = HDMI_INFOFRAME_TYPE_VENDOR;
frame.regid2 = 0x00; frame.version = 0x01;
frame.hdmi_video_format = 2; frame.length = 6;
frame.data[0] = 0x03; /* regid0 */
frame.data[1] = 0x0c; /* regid1 */
frame.data[2] = 0x00; /* regid2 */
frame.data[3] = 0x02 << 5; /* video format */
/* TODO: 74 MHz limit? */ /* TODO: 74 MHz limit? */
if (1) { if (1) {
frame._3d_structure = 0; frame.data[4] = 0x00 << 4; /* 3D structure */
} else { } else {
frame._3d_structure = 8; frame.data[4] = 0x08 << 4; /* 3D structure */
frame._3d_ext_data = 0; frame.data[5] = 0x00 << 4; /* 3D ext. data */
}
err = hdmi_vendor_infoframe_pack(&frame, buffer, sizeof(buffer));
if (err < 0) {
dev_err(hdmi->dev, "failed to pack vendor infoframe: %zd\n",
err);
return;
} }
tegra_hdmi_write_infopack(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_HEADER, tegra_hdmi_write_infopack(hdmi, buffer, err);
HDMI_INFOFRAME_TYPE_VENDOR,
HDMI_VENDOR_VERSION, &frame, 6);
value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_CTRL);
value |= GENERIC_CTRL_ENABLE; value |= GENERIC_CTRL_ENABLE;
......
...@@ -10,195 +10,6 @@ ...@@ -10,195 +10,6 @@
#ifndef TEGRA_HDMI_H #ifndef TEGRA_HDMI_H
#define TEGRA_HDMI_H 1 #define TEGRA_HDMI_H 1
#define HDMI_INFOFRAME_TYPE_VENDOR 0x81
#define HDMI_INFOFRAME_TYPE_AVI 0x82
#define HDMI_INFOFRAME_TYPE_SPD 0x83
#define HDMI_INFOFRAME_TYPE_AUDIO 0x84
#define HDMI_INFOFRAME_TYPE_MPEG_SRC 0x85
#define HDMI_INFOFRAME_TYPE_NTSC_VBI 0x86
/* all fields little endian */
struct hdmi_avi_infoframe {
/* PB0 */
u8 csum;
/* PB1 */
unsigned s:2; /* scan information */
unsigned b:2; /* bar info data valid */
unsigned a:1; /* active info present */
unsigned y:2; /* RGB or YCbCr */
unsigned res1:1;
/* PB2 */
unsigned r:4; /* active format aspect ratio */
unsigned m:2; /* picture aspect ratio */
unsigned c:2; /* colorimetry */
/* PB3 */
unsigned sc:2; /* scan information */
unsigned q:2; /* quantization range */
unsigned ec:3; /* extended colorimetry */
unsigned itc:1; /* it content */
/* PB4 */
unsigned vic:7; /* video format id code */
unsigned res4:1;
/* PB5 */
unsigned pr:4; /* pixel repetition factor */
unsigned cn:2; /* it content type*/
unsigned yq:2; /* ycc quantization range */
/* PB6-7 */
u16 top_bar_end_line;
/* PB8-9 */
u16 bot_bar_start_line;
/* PB10-11 */
u16 left_bar_end_pixel;
/* PB12-13 */
u16 right_bar_start_pixel;
} __packed;
#define HDMI_AVI_VERSION 0x02
#define HDMI_AVI_Y_RGB 0x0
#define HDMI_AVI_Y_YCBCR_422 0x1
#define HDMI_AVI_Y_YCBCR_444 0x2
#define HDMI_AVI_B_VERT 0x1
#define HDMI_AVI_B_HORIZ 0x2
#define HDMI_AVI_S_NONE 0x0
#define HDMI_AVI_S_OVERSCAN 0x1
#define HDMI_AVI_S_UNDERSCAN 0x2
#define HDMI_AVI_C_NONE 0x0
#define HDMI_AVI_C_SMPTE 0x1
#define HDMI_AVI_C_ITU_R 0x2
#define HDMI_AVI_C_EXTENDED 0x4
#define HDMI_AVI_M_4_3 0x1
#define HDMI_AVI_M_16_9 0x2
#define HDMI_AVI_R_SAME 0x8
#define HDMI_AVI_R_4_3_CENTER 0x9
#define HDMI_AVI_R_16_9_CENTER 0xa
#define HDMI_AVI_R_14_9_CENTER 0xb
/* all fields little endian */
struct hdmi_audio_infoframe {
/* PB0 */
u8 csum;
/* PB1 */
unsigned cc:3; /* channel count */
unsigned res1:1;
unsigned ct:4; /* coding type */
/* PB2 */
unsigned ss:2; /* sample size */
unsigned sf:3; /* sample frequency */
unsigned res2:3;
/* PB3 */
unsigned cxt:5; /* coding extention type */
unsigned res3:3;
/* PB4 */
u8 ca; /* channel/speaker allocation */
/* PB5 */
unsigned res5:3;
unsigned lsv:4; /* level shift value */
unsigned dm_inh:1; /* downmix inhibit */
/* PB6-10 reserved */
u8 res6;
u8 res7;
u8 res8;
u8 res9;
u8 res10;
} __packed;
#define HDMI_AUDIO_VERSION 0x01
#define HDMI_AUDIO_CC_STREAM 0x0 /* specified by audio stream */
#define HDMI_AUDIO_CC_2 0x1
#define HDMI_AUDIO_CC_3 0x2
#define HDMI_AUDIO_CC_4 0x3
#define HDMI_AUDIO_CC_5 0x4
#define HDMI_AUDIO_CC_6 0x5
#define HDMI_AUDIO_CC_7 0x6
#define HDMI_AUDIO_CC_8 0x7
#define HDMI_AUDIO_CT_STREAM 0x0 /* specified by audio stream */
#define HDMI_AUDIO_CT_PCM 0x1
#define HDMI_AUDIO_CT_AC3 0x2
#define HDMI_AUDIO_CT_MPEG1 0x3
#define HDMI_AUDIO_CT_MP3 0x4
#define HDMI_AUDIO_CT_MPEG2 0x5
#define HDMI_AUDIO_CT_AAC_LC 0x6
#define HDMI_AUDIO_CT_DTS 0x7
#define HDMI_AUDIO_CT_ATRAC 0x8
#define HDMI_AUDIO_CT_DSD 0x9
#define HDMI_AUDIO_CT_E_AC3 0xa
#define HDMI_AUDIO_CT_DTS_HD 0xb
#define HDMI_AUDIO_CT_MLP 0xc
#define HDMI_AUDIO_CT_DST 0xd
#define HDMI_AUDIO_CT_WMA_PRO 0xe
#define HDMI_AUDIO_CT_CXT 0xf
#define HDMI_AUDIO_SF_STREAM 0x0 /* specified by audio stream */
#define HDMI_AUIDO_SF_32K 0x1
#define HDMI_AUDIO_SF_44_1K 0x2
#define HDMI_AUDIO_SF_48K 0x3
#define HDMI_AUDIO_SF_88_2K 0x4
#define HDMI_AUDIO_SF_96K 0x5
#define HDMI_AUDIO_SF_176_4K 0x6
#define HDMI_AUDIO_SF_192K 0x7
#define HDMI_AUDIO_SS_STREAM 0x0 /* specified by audio stream */
#define HDMI_AUDIO_SS_16BIT 0x1
#define HDMI_AUDIO_SS_20BIT 0x2
#define HDMI_AUDIO_SS_24BIT 0x3
#define HDMI_AUDIO_CXT_CT 0x0 /* refer to coding in CT */
#define HDMI_AUDIO_CXT_HE_AAC 0x1
#define HDMI_AUDIO_CXT_HE_AAC_V2 0x2
#define HDMI_AUDIO_CXT_MPEG_SURROUND 0x3
/* all fields little endian */
struct hdmi_stereo_infoframe {
/* PB0 */
u8 csum;
/* PB1 */
u8 regid0;
/* PB2 */
u8 regid1;
/* PB3 */
u8 regid2;
/* PB4 */
unsigned res1:5;
unsigned hdmi_video_format:3;
/* PB5 */
unsigned res2:4;
unsigned _3d_structure:4;
/* PB6*/
unsigned res3:4;
unsigned _3d_ext_data:4;
} __packed;
#define HDMI_VENDOR_VERSION 0x01
/* register definitions */ /* register definitions */
#define HDMI_CTXSW 0x00 #define HDMI_CTXSW 0x00
......
...@@ -52,6 +52,9 @@ config OF_VIDEOMODE ...@@ -52,6 +52,9 @@ config OF_VIDEOMODE
help help
helper to get videomodes from the devicetree helper to get videomodes from the devicetree
config HDMI
bool
menuconfig FB menuconfig FB
tristate "Support for frame buffer devices" tristate "Support for frame buffer devices"
---help--- ---help---
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
# Each configuration option enables a list of files. # Each configuration option enables a list of files.
obj-$(CONFIG_VGASTATE) += vgastate.o obj-$(CONFIG_VGASTATE) += vgastate.o
obj-$(CONFIG_HDMI) += hdmi.o
obj-y += fb_notify.o obj-y += fb_notify.o
obj-$(CONFIG_FB) += fb.o obj-$(CONFIG_FB) += fb.o
fb-y := fbmem.o fbmon.o fbcmap.o fbsysfs.o \ fb-y := fbmem.o fbmon.o fbcmap.o fbsysfs.o \
......
/*
* Copyright (C) 2012 Avionic Design GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/bitops.h>
#include <linux/errno.h>
#include <linux/export.h>
#include <linux/hdmi.h>
#include <linux/string.h>
static void hdmi_infoframe_checksum(void *buffer, size_t size)
{
u8 *ptr = buffer;
u8 csum = 0;
size_t i;
/* compute checksum */
for (i = 0; i < size; i++)
csum += ptr[i];
ptr[3] = 256 - csum;
}
/**
* hdmi_avi_infoframe_init() - initialize an HDMI AVI infoframe
* @frame: HDMI AVI infoframe
*
* Returns 0 on success or a negative error code on failure.
*/
int hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame)
{
memset(frame, 0, sizeof(*frame));
frame->type = HDMI_INFOFRAME_TYPE_AVI;
frame->version = 2;
frame->length = 13;
return 0;
}
EXPORT_SYMBOL(hdmi_avi_infoframe_init);
/**
* hdmi_avi_infoframe_pack() - write HDMI AVI infoframe to binary buffer
* @frame: HDMI AVI infoframe
* @buffer: destination buffer
* @size: size of buffer
*
* Packs the information contained in the @frame structure into a binary
* representation that can be written into the corresponding controller
* registers. Also computes the checksum as required by section 5.3.5 of
* the HDMI 1.4 specification.
*
* Returns the number of bytes packed into the binary buffer or a negative
* error code on failure.
*/
ssize_t hdmi_avi_infoframe_pack(struct hdmi_avi_infoframe *frame, void *buffer,
size_t size)
{
u8 *ptr = buffer;
size_t length;
length = HDMI_INFOFRAME_HEADER_SIZE + frame->length;
if (size < length)
return -ENOSPC;
memset(buffer, 0, length);
ptr[0] = frame->type;
ptr[1] = frame->version;
ptr[2] = frame->length;
ptr[3] = 0; /* checksum */
/* start infoframe payload */
ptr += HDMI_INFOFRAME_HEADER_SIZE;
ptr[0] = ((frame->colorspace & 0x3) << 5) | (frame->scan_mode & 0x3);
if (frame->active_info_valid)
ptr[0] |= BIT(4);
if (frame->horizontal_bar_valid)
ptr[0] |= BIT(3);
if (frame->vertical_bar_valid)
ptr[0] |= BIT(2);
ptr[1] = ((frame->colorimetry & 0x3) << 6) |
((frame->picture_aspect & 0x3) << 4) |
(frame->active_aspect & 0xf);
ptr[2] = ((frame->extended_colorimetry & 0x7) << 4) |
((frame->quantization_range & 0x3) << 2) |
(frame->nups & 0x3);
if (frame->itc)
ptr[2] |= BIT(7);
ptr[3] = frame->video_code & 0x7f;
ptr[4] = ((frame->ycc_quantization_range & 0x3) << 6) |
((frame->content_type & 0x3) << 4) |
(frame->pixel_repeat & 0xf);
ptr[5] = frame->top_bar & 0xff;
ptr[6] = (frame->top_bar >> 8) & 0xff;
ptr[7] = frame->bottom_bar & 0xff;
ptr[8] = (frame->bottom_bar >> 8) & 0xff;
ptr[9] = frame->left_bar & 0xff;
ptr[10] = (frame->left_bar >> 8) & 0xff;
ptr[11] = frame->right_bar & 0xff;
ptr[12] = (frame->right_bar >> 8) & 0xff;
hdmi_infoframe_checksum(buffer, length);
return length;
}
EXPORT_SYMBOL(hdmi_avi_infoframe_pack);
/**
* hdmi_spd_infoframe_init() - initialize an HDMI SPD infoframe
* @frame: HDMI SPD infoframe
* @vendor: vendor string
* @product: product string
*
* Returns 0 on success or a negative error code on failure.
*/
int hdmi_spd_infoframe_init(struct hdmi_spd_infoframe *frame,
const char *vendor, const char *product)
{
memset(frame, 0, sizeof(*frame));
frame->type = HDMI_INFOFRAME_TYPE_SPD;
frame->version = 1;
frame->length = 25;
strncpy(frame->vendor, vendor, sizeof(frame->vendor));
strncpy(frame->product, product, sizeof(frame->product));
return 0;
}
EXPORT_SYMBOL(hdmi_spd_infoframe_init);
/**
* hdmi_spd_infoframe_pack() - write HDMI SPD infoframe to binary buffer
* @frame: HDMI SPD infoframe
* @buffer: destination buffer
* @size: size of buffer
*
* Packs the information contained in the @frame structure into a binary
* representation that can be written into the corresponding controller
* registers. Also computes the checksum as required by section 5.3.5 of
* the HDMI 1.4 specification.
*
* Returns the number of bytes packed into the binary buffer or a negative
* error code on failure.
*/
ssize_t hdmi_spd_infoframe_pack(struct hdmi_spd_infoframe *frame, void *buffer,
size_t size)
{
u8 *ptr = buffer;
size_t length;
length = HDMI_INFOFRAME_HEADER_SIZE + frame->length;
if (size < length)
return -ENOSPC;
memset(buffer, 0, length);
ptr[0] = frame->type;
ptr[1] = frame->version;
ptr[2] = frame->length;
ptr[3] = 0; /* checksum */
/* start infoframe payload */
ptr += HDMI_INFOFRAME_HEADER_SIZE;
memcpy(ptr, frame->vendor, sizeof(frame->vendor));
memcpy(ptr + 8, frame->product, sizeof(frame->product));
ptr[24] = frame->sdi;
hdmi_infoframe_checksum(buffer, length);
return length;
}
EXPORT_SYMBOL(hdmi_spd_infoframe_pack);
/**
* hdmi_audio_infoframe_init() - initialize an HDMI audio infoframe
* @frame: HDMI audio infoframe
*
* Returns 0 on success or a negative error code on failure.
*/
int hdmi_audio_infoframe_init(struct hdmi_audio_infoframe *frame)
{
memset(frame, 0, sizeof(*frame));
frame->type = HDMI_INFOFRAME_TYPE_AUDIO;
frame->version = 1;
frame->length = 10;
return 0;
}
EXPORT_SYMBOL(hdmi_audio_infoframe_init);
/**
* hdmi_audio_infoframe_pack() - write HDMI audio infoframe to binary buffer
* @frame: HDMI audio infoframe
* @buffer: destination buffer
* @size: size of buffer
*
* Packs the information contained in the @frame structure into a binary
* representation that can be written into the corresponding controller
* registers. Also computes the checksum as required by section 5.3.5 of
* the HDMI 1.4 specification.
*
* Returns the number of bytes packed into the binary buffer or a negative
* error code on failure.
*/
ssize_t hdmi_audio_infoframe_pack(struct hdmi_audio_infoframe *frame,
void *buffer, size_t size)
{
unsigned char channels;
u8 *ptr = buffer;
size_t length;
length = HDMI_INFOFRAME_HEADER_SIZE + frame->length;
if (size < length)
return -ENOSPC;
memset(buffer, 0, length);
if (frame->channels >= 2)
channels = frame->channels - 1;
else
channels = 0;
ptr[0] = frame->type;
ptr[1] = frame->version;
ptr[2] = frame->length;
ptr[3] = 0; /* checksum */
/* start infoframe payload */
ptr += HDMI_INFOFRAME_HEADER_SIZE;
ptr[0] = ((frame->coding_type & 0xf) << 4) | (channels & 0x7);
ptr[1] = ((frame->sample_frequency & 0x7) << 2) |
(frame->sample_size & 0x3);
ptr[2] = frame->coding_type_ext & 0x1f;
ptr[3] = frame->channel_allocation;
ptr[4] = (frame->level_shift_value & 0xf) << 3;
if (frame->downmix_inhibit)
ptr[4] |= BIT(7);
hdmi_infoframe_checksum(buffer, length);
return length;
}
EXPORT_SYMBOL(hdmi_audio_infoframe_pack);
/**
* hdmi_vendor_infoframe_pack() - write a HDMI vendor infoframe to binary
* buffer
* @frame: HDMI vendor infoframe
* @buffer: destination buffer
* @size: size of buffer
*
* Packs the information contained in the @frame structure into a binary
* representation that can be written into the corresponding controller
* registers. Also computes the checksum as required by section 5.3.5 of
* the HDMI 1.4 specification.
*
* Returns the number of bytes packed into the binary buffer or a negative
* error code on failure.
*/
ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame,
void *buffer, size_t size)
{
u8 *ptr = buffer;
size_t length;
length = HDMI_INFOFRAME_HEADER_SIZE + frame->length;
if (size < length)
return -ENOSPC;
memset(buffer, 0, length);
ptr[0] = frame->type;
ptr[1] = frame->version;
ptr[2] = frame->length;
ptr[3] = 0; /* checksum */
memcpy(&ptr[HDMI_INFOFRAME_HEADER_SIZE], frame->data, frame->length);
hdmi_infoframe_checksum(buffer, length);
return length;
}
EXPORT_SYMBOL(hdmi_vendor_infoframe_pack);
...@@ -38,7 +38,8 @@ struct drm_device; ...@@ -38,7 +38,8 @@ struct drm_device;
struct drm_mode_set; struct drm_mode_set;
struct drm_framebuffer; struct drm_framebuffer;
struct drm_object_properties; struct drm_object_properties;
struct drm_file;
struct drm_clip_rect;
#define DRM_MODE_OBJECT_CRTC 0xcccccccc #define DRM_MODE_OBJECT_CRTC 0xcccccccc
#define DRM_MODE_OBJECT_CONNECTOR 0xc0c0c0c0 #define DRM_MODE_OBJECT_CONNECTOR 0xc0c0c0c0
...@@ -1061,7 +1062,7 @@ extern int drm_mode_gamma_get_ioctl(struct drm_device *dev, ...@@ -1061,7 +1062,7 @@ extern int drm_mode_gamma_get_ioctl(struct drm_device *dev,
extern int drm_mode_gamma_set_ioctl(struct drm_device *dev, extern int drm_mode_gamma_set_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv); void *data, struct drm_file *file_priv);
extern u8 *drm_find_cea_extension(struct edid *edid); extern u8 *drm_find_cea_extension(struct edid *edid);
extern u8 drm_match_cea_mode(struct drm_display_mode *to_match); extern u8 drm_match_cea_mode(const struct drm_display_mode *to_match);
extern bool drm_detect_hdmi_monitor(struct edid *edid); extern bool drm_detect_hdmi_monitor(struct edid *edid);
extern bool drm_detect_monitor_audio(struct edid *edid); extern bool drm_detect_monitor_audio(struct edid *edid);
extern bool drm_rgb_quant_range_selectable(struct edid *edid); extern bool drm_rgb_quant_range_selectable(struct edid *edid);
...@@ -1079,7 +1080,6 @@ extern struct drm_display_mode *drm_gtf_mode_complex(struct drm_device *dev, ...@@ -1079,7 +1080,6 @@ extern struct drm_display_mode *drm_gtf_mode_complex(struct drm_device *dev,
int GTF_2C, int GTF_K, int GTF_2J); int GTF_2C, int GTF_K, int GTF_2J);
extern int drm_add_modes_noedid(struct drm_connector *connector, extern int drm_add_modes_noedid(struct drm_connector *connector,
int hdisplay, int vdisplay); int hdisplay, int vdisplay);
extern uint8_t drm_mode_cea_vic(const struct drm_display_mode *mode);
extern int drm_edid_header_is_valid(const u8 *raw_edid); extern int drm_edid_header_is_valid(const u8 *raw_edid);
extern bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid); extern bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid);
......
...@@ -247,6 +247,8 @@ struct edid { ...@@ -247,6 +247,8 @@ struct edid {
struct drm_encoder; struct drm_encoder;
struct drm_connector; struct drm_connector;
struct drm_display_mode; struct drm_display_mode;
struct hdmi_avi_infoframe;
void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid); void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid);
int drm_av_sync_delay(struct drm_connector *connector, int drm_av_sync_delay(struct drm_connector *connector,
struct drm_display_mode *mode); struct drm_display_mode *mode);
...@@ -254,4 +256,8 @@ struct drm_connector *drm_select_eld(struct drm_encoder *encoder, ...@@ -254,4 +256,8 @@ struct drm_connector *drm_select_eld(struct drm_encoder *encoder,
struct drm_display_mode *mode); struct drm_display_mode *mode);
int drm_load_edid_firmware(struct drm_connector *connector); int drm_load_edid_firmware(struct drm_connector *connector);
int
drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame,
const struct drm_display_mode *mode);
#endif /* __DRM_EDID_H__ */ #endif /* __DRM_EDID_H__ */
/*
* Copyright (C) 2012 Avionic Design GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __LINUX_HDMI_H_
#define __LINUX_HDMI_H_
#include <linux/types.h>
enum hdmi_infoframe_type {
HDMI_INFOFRAME_TYPE_VENDOR = 0x81,
HDMI_INFOFRAME_TYPE_AVI = 0x82,
HDMI_INFOFRAME_TYPE_SPD = 0x83,
HDMI_INFOFRAME_TYPE_AUDIO = 0x84,
};
#define HDMI_INFOFRAME_HEADER_SIZE 4
#define HDMI_AVI_INFOFRAME_SIZE 13
#define HDMI_SPD_INFOFRAME_SIZE 25
#define HDMI_AUDIO_INFOFRAME_SIZE 10
enum hdmi_colorspace {
HDMI_COLORSPACE_RGB,
HDMI_COLORSPACE_YUV422,
HDMI_COLORSPACE_YUV444,
};
enum hdmi_scan_mode {
HDMI_SCAN_MODE_NONE,
HDMI_SCAN_MODE_OVERSCAN,
HDMI_SCAN_MODE_UNDERSCAN,
};
enum hdmi_colorimetry {
HDMI_COLORIMETRY_NONE,
HDMI_COLORIMETRY_ITU_601,
HDMI_COLORIMETRY_ITU_709,
HDMI_COLORIMETRY_EXTENDED,
};
enum hdmi_picture_aspect {
HDMI_PICTURE_ASPECT_NONE,
HDMI_PICTURE_ASPECT_4_3,
HDMI_PICTURE_ASPECT_16_9,
};
enum hdmi_active_aspect {
HDMI_ACTIVE_ASPECT_16_9_TOP = 2,
HDMI_ACTIVE_ASPECT_14_9_TOP = 3,
HDMI_ACTIVE_ASPECT_16_9_CENTER = 4,
HDMI_ACTIVE_ASPECT_PICTURE = 8,
HDMI_ACTIVE_ASPECT_4_3 = 9,
HDMI_ACTIVE_ASPECT_16_9 = 10,
HDMI_ACTIVE_ASPECT_14_9 = 11,
HDMI_ACTIVE_ASPECT_4_3_SP_14_9 = 13,
HDMI_ACTIVE_ASPECT_16_9_SP_14_9 = 14,
HDMI_ACTIVE_ASPECT_16_9_SP_4_3 = 15,
};
enum hdmi_extended_colorimetry {
HDMI_EXTENDED_COLORIMETRY_XV_YCC_601,
HDMI_EXTENDED_COLORIMETRY_XV_YCC_709,
HDMI_EXTENDED_COLORIMETRY_S_YCC_601,
HDMI_EXTENDED_COLORIMETRY_ADOBE_YCC_601,
HDMI_EXTENDED_COLORIMETRY_ADOBE_RGB,
};
enum hdmi_quantization_range {
HDMI_QUANTIZATION_RANGE_DEFAULT,
HDMI_QUANTIZATION_RANGE_LIMITED,
HDMI_QUANTIZATION_RANGE_FULL,
};
/* non-uniform picture scaling */
enum hdmi_nups {
HDMI_NUPS_UNKNOWN,
HDMI_NUPS_HORIZONTAL,
HDMI_NUPS_VERTICAL,
HDMI_NUPS_BOTH,
};
enum hdmi_ycc_quantization_range {
HDMI_YCC_QUANTIZATION_RANGE_LIMITED,
HDMI_YCC_QUANTIZATION_RANGE_FULL,
};
enum hdmi_content_type {
HDMI_CONTENT_TYPE_NONE,
HDMI_CONTENT_TYPE_PHOTO,
HDMI_CONTENT_TYPE_CINEMA,
HDMI_CONTENT_TYPE_GAME,
};
struct hdmi_avi_infoframe {
enum hdmi_infoframe_type type;
unsigned char version;
unsigned char length;
enum hdmi_colorspace colorspace;
bool active_info_valid;
bool horizontal_bar_valid;
bool vertical_bar_valid;
enum hdmi_scan_mode scan_mode;
enum hdmi_colorimetry colorimetry;
enum hdmi_picture_aspect picture_aspect;
enum hdmi_active_aspect active_aspect;
bool itc;
enum hdmi_extended_colorimetry extended_colorimetry;
enum hdmi_quantization_range quantization_range;
enum hdmi_nups nups;
unsigned char video_code;
enum hdmi_ycc_quantization_range ycc_quantization_range;
enum hdmi_content_type content_type;
unsigned char pixel_repeat;
unsigned short top_bar;
unsigned short bottom_bar;
unsigned short left_bar;
unsigned short right_bar;
};
int hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame);
ssize_t hdmi_avi_infoframe_pack(struct hdmi_avi_infoframe *frame, void *buffer,
size_t size);
enum hdmi_spd_sdi {
HDMI_SPD_SDI_UNKNOWN,
HDMI_SPD_SDI_DSTB,
HDMI_SPD_SDI_DVDP,
HDMI_SPD_SDI_DVHS,
HDMI_SPD_SDI_HDDVR,
HDMI_SPD_SDI_DVC,
HDMI_SPD_SDI_DSC,
HDMI_SPD_SDI_VCD,
HDMI_SPD_SDI_GAME,
HDMI_SPD_SDI_PC,
HDMI_SPD_SDI_BD,
HDMI_SPD_SDI_SACD,
HDMI_SPD_SDI_HDDVD,
HDMI_SPD_SDI_PMP,
};
struct hdmi_spd_infoframe {
enum hdmi_infoframe_type type;
unsigned char version;
unsigned char length;
char vendor[8];
char product[16];
enum hdmi_spd_sdi sdi;
};
int hdmi_spd_infoframe_init(struct hdmi_spd_infoframe *frame,
const char *vendor, const char *product);
ssize_t hdmi_spd_infoframe_pack(struct hdmi_spd_infoframe *frame, void *buffer,
size_t size);
enum hdmi_audio_coding_type {
HDMI_AUDIO_CODING_TYPE_STREAM,
HDMI_AUDIO_CODING_TYPE_PCM,
HDMI_AUDIO_CODING_TYPE_AC3,
HDMI_AUDIO_CODING_TYPE_MPEG1,
HDMI_AUDIO_CODING_TYPE_MP3,
HDMI_AUDIO_CODING_TYPE_MPEG2,
HDMI_AUDIO_CODING_TYPE_AAC_LC,
HDMI_AUDIO_CODING_TYPE_DTS,
HDMI_AUDIO_CODING_TYPE_ATRAC,
HDMI_AUDIO_CODING_TYPE_DSD,
HDMI_AUDIO_CODING_TYPE_EAC3,
HDMI_AUDIO_CODING_TYPE_DTS_HD,
HDMI_AUDIO_CODING_TYPE_MLP,
HDMI_AUDIO_CODING_TYPE_DST,
HDMI_AUDIO_CODING_TYPE_WMA_PRO,
};
enum hdmi_audio_sample_size {
HDMI_AUDIO_SAMPLE_SIZE_STREAM,
HDMI_AUDIO_SAMPLE_SIZE_16,
HDMI_AUDIO_SAMPLE_SIZE_20,
HDMI_AUDIO_SAMPLE_SIZE_24,
};
enum hdmi_audio_sample_frequency {
HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM,
HDMI_AUDIO_SAMPLE_FREQUENCY_32000,
HDMI_AUDIO_SAMPLE_FREQUENCY_44100,
HDMI_AUDIO_SAMPLE_FREQUENCY_48000,
HDMI_AUDIO_SAMPLE_FREQUENCY_88200,
HDMI_AUDIO_SAMPLE_FREQUENCY_96000,
HDMI_AUDIO_SAMPLE_FREQUENCY_176400,
HDMI_AUDIO_SAMPLE_FREQUENCY_192000,
};
enum hdmi_audio_coding_type_ext {
HDMI_AUDIO_CODING_TYPE_EXT_STREAM,
HDMI_AUDIO_CODING_TYPE_EXT_HE_AAC,
HDMI_AUDIO_CODING_TYPE_EXT_HE_AAC_V2,
HDMI_AUDIO_CODING_TYPE_EXT_MPEG_SURROUND,
};
struct hdmi_audio_infoframe {
enum hdmi_infoframe_type type;
unsigned char version;
unsigned char length;
unsigned char channels;
enum hdmi_audio_coding_type coding_type;
enum hdmi_audio_sample_size sample_size;
enum hdmi_audio_sample_frequency sample_frequency;
enum hdmi_audio_coding_type_ext coding_type_ext;
unsigned char channel_allocation;
unsigned char level_shift_value;
bool downmix_inhibit;
};
int hdmi_audio_infoframe_init(struct hdmi_audio_infoframe *frame);
ssize_t hdmi_audio_infoframe_pack(struct hdmi_audio_infoframe *frame,
void *buffer, size_t size);
struct hdmi_vendor_infoframe {
enum hdmi_infoframe_type type;
unsigned char version;
unsigned char length;
u8 data[27];
};
ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame,
void *buffer, size_t size);
#endif /* _DRM_HDMI_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment