cregit-Linux how code gets into the kernel

Release 4.11 drivers/gpu/drm/sti/sti_hdmi.c

/*
 * Copyright (C) STMicroelectronics SA 2014
 * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
 * License terms:  GNU General Public License (GPL), version 2
 */

#include <linux/clk.h>
#include <linux/component.h>
#include <linux/debugfs.h>
#include <linux/hdmi.h>
#include <linux/module.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/reset.h>

#include <drm/drmP.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_edid.h>

#include <sound/hdmi-codec.h>

#include "sti_hdmi.h"
#include "sti_hdmi_tx3g4c28phy.h"
#include "sti_vtg.h"


#define HDMI_CFG                        0x0000

#define HDMI_INT_EN                     0x0004

#define HDMI_INT_STA                    0x0008

#define HDMI_INT_CLR                    0x000C

#define HDMI_STA                        0x0010

#define HDMI_ACTIVE_VID_XMIN            0x0100

#define HDMI_ACTIVE_VID_XMAX            0x0104

#define HDMI_ACTIVE_VID_YMIN            0x0108

#define HDMI_ACTIVE_VID_YMAX            0x010C

#define HDMI_DFLT_CHL0_DAT              0x0110

#define HDMI_DFLT_CHL1_DAT              0x0114

#define HDMI_DFLT_CHL2_DAT              0x0118

#define HDMI_AUDIO_CFG                  0x0200

#define HDMI_SPDIF_FIFO_STATUS          0x0204

#define HDMI_SW_DI_1_HEAD_WORD          0x0210

#define HDMI_SW_DI_1_PKT_WORD0          0x0214

#define HDMI_SW_DI_1_PKT_WORD1          0x0218

#define HDMI_SW_DI_1_PKT_WORD2          0x021C

#define HDMI_SW_DI_1_PKT_WORD3          0x0220

#define HDMI_SW_DI_1_PKT_WORD4          0x0224

#define HDMI_SW_DI_1_PKT_WORD5          0x0228

#define HDMI_SW_DI_1_PKT_WORD6          0x022C

#define HDMI_SW_DI_CFG                  0x0230

#define HDMI_SAMPLE_FLAT_MASK           0x0244

#define HDMI_AUDN                       0x0400

#define HDMI_AUD_CTS                    0x0404

#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_SW_DI_3_HEAD_WORD          0x0620

#define HDMI_SW_DI_3_PKT_WORD0          0x0624

#define HDMI_SW_DI_3_PKT_WORD1          0x0628

#define HDMI_SW_DI_3_PKT_WORD2          0x062C

#define HDMI_SW_DI_3_PKT_WORD3          0x0630

#define HDMI_SW_DI_3_PKT_WORD4          0x0634

#define HDMI_SW_DI_3_PKT_WORD5          0x0638

#define HDMI_SW_DI_3_PKT_WORD6          0x063C


#define HDMI_IFRAME_SLOT_AVI            1

#define HDMI_IFRAME_SLOT_AUDIO          2

#define HDMI_IFRAME_SLOT_VENDOR         3


#define  XCAT(prefix, x, suffix)        prefix ## x ## suffix

#define  HDMI_SW_DI_N_HEAD_WORD(x)      XCAT(HDMI_SW_DI_, x, _HEAD_WORD)

#define  HDMI_SW_DI_N_PKT_WORD0(x)      XCAT(HDMI_SW_DI_, x, _PKT_WORD0)

#define  HDMI_SW_DI_N_PKT_WORD1(x)      XCAT(HDMI_SW_DI_, x, _PKT_WORD1)

#define  HDMI_SW_DI_N_PKT_WORD2(x)      XCAT(HDMI_SW_DI_, x, _PKT_WORD2)

#define  HDMI_SW_DI_N_PKT_WORD3(x)      XCAT(HDMI_SW_DI_, x, _PKT_WORD3)

#define  HDMI_SW_DI_N_PKT_WORD4(x)      XCAT(HDMI_SW_DI_, x, _PKT_WORD4)

#define  HDMI_SW_DI_N_PKT_WORD5(x)      XCAT(HDMI_SW_DI_, x, _PKT_WORD5)

#define  HDMI_SW_DI_N_PKT_WORD6(x)      XCAT(HDMI_SW_DI_, x, _PKT_WORD6)


#define HDMI_SW_DI_MAX_WORD             7


#define HDMI_IFRAME_DISABLED            0x0

#define HDMI_IFRAME_SINGLE_SHOT         0x1

#define HDMI_IFRAME_FIELD               0x2

#define HDMI_IFRAME_FRAME               0x3

#define HDMI_IFRAME_MASK                0x3

#define HDMI_IFRAME_CFG_DI_N(x, n)       ((x) << ((n-1)*4)) 
/* n from 1 to 6 */


#define HDMI_CFG_DEVICE_EN              BIT(0)

#define HDMI_CFG_HDMI_NOT_DVI           BIT(1)

#define HDMI_CFG_HDCP_EN                BIT(2)

#define HDMI_CFG_ESS_NOT_OESS           BIT(3)

#define HDMI_CFG_H_SYNC_POL_NEG         BIT(4)

#define HDMI_CFG_V_SYNC_POL_NEG         BIT(6)

#define HDMI_CFG_422_EN                 BIT(8)

#define HDMI_CFG_FIFO_OVERRUN_CLR       BIT(12)

#define HDMI_CFG_FIFO_UNDERRUN_CLR      BIT(13)

#define HDMI_CFG_SW_RST_EN              BIT(31)


#define HDMI_INT_GLOBAL                 BIT(0)

#define HDMI_INT_SW_RST                 BIT(1)

#define HDMI_INT_PIX_CAP                BIT(3)

#define HDMI_INT_HOT_PLUG               BIT(4)

#define HDMI_INT_DLL_LCK                BIT(5)

#define HDMI_INT_NEW_FRAME              BIT(6)

#define HDMI_INT_GENCTRL_PKT            BIT(7)

#define HDMI_INT_AUDIO_FIFO_XRUN        BIT(8)

#define HDMI_INT_SINK_TERM_PRESENT      BIT(11)


#define HDMI_DEFAULT_INT (HDMI_INT_SINK_TERM_PRESENT \
                        | HDMI_INT_DLL_LCK \
                        | HDMI_INT_HOT_PLUG \
                        | HDMI_INT_GLOBAL)


#define HDMI_WORKING_INT (HDMI_INT_SINK_TERM_PRESENT \
                        | HDMI_INT_AUDIO_FIFO_XRUN \
                        | HDMI_INT_GENCTRL_PKT \
                        | HDMI_INT_NEW_FRAME \
                        | HDMI_INT_DLL_LCK \
                        | HDMI_INT_HOT_PLUG \
                        | HDMI_INT_PIX_CAP \
                        | HDMI_INT_SW_RST \
                        | HDMI_INT_GLOBAL)


#define HDMI_STA_SW_RST                 BIT(1)


#define HDMI_AUD_CFG_8CH		BIT(0)

#define HDMI_AUD_CFG_SPDIF_DIV_2	BIT(1)

#define HDMI_AUD_CFG_SPDIF_DIV_3	BIT(2)

#define HDMI_AUD_CFG_SPDIF_CLK_DIV_4	(BIT(1) | BIT(2))

#define HDMI_AUD_CFG_CTS_CLK_256FS	BIT(12)

#define HDMI_AUD_CFG_DTS_INVALID	BIT(16)

#define HDMI_AUD_CFG_ONE_BIT_INVALID	(BIT(18) | BIT(19) | BIT(20) |  BIT(21))

#define HDMI_AUD_CFG_CH12_VALID	BIT(28)

#define HDMI_AUD_CFG_CH34_VALID	BIT(29)

#define HDMI_AUD_CFG_CH56_VALID	BIT(30)

#define HDMI_AUD_CFG_CH78_VALID	BIT(31)

/* sample flat mask */

#define HDMI_SAMPLE_FLAT_NO	 0

#define HDMI_SAMPLE_FLAT_SP0 BIT(0)

#define HDMI_SAMPLE_FLAT_SP1 BIT(1)

#define HDMI_SAMPLE_FLAT_SP2 BIT(2)

#define HDMI_SAMPLE_FLAT_SP3 BIT(3)

#define HDMI_SAMPLE_FLAT_ALL (HDMI_SAMPLE_FLAT_SP0 | HDMI_SAMPLE_FLAT_SP1 |\
                              HDMI_SAMPLE_FLAT_SP2 | HDMI_SAMPLE_FLAT_SP3)


#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;
	
struct sti_hdmi *hdmi;
	
struct drm_property *colorspace_property;
};


#define to_sti_hdmi_connector(x) \
	container_of(x, struct sti_hdmi_connector, drm_connector)


u32 hdmi_read(struct sti_hdmi *hdmi, int offset) { return readl(hdmi->regs + offset); }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Gaignard23100.00%1100.00%
Total23100.00%1100.00%


void hdmi_write(struct sti_hdmi *hdmi, u32 val, int offset) { writel(val, hdmi->regs + offset); }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Gaignard27100.00%1100.00%
Total27100.00%1100.00%

/** * HDMI interrupt handler threaded * * @irq: irq number * @arg: connector structure */
static irqreturn_t hdmi_irq_thread(int irq, void *arg) { struct sti_hdmi *hdmi = arg; /* Hot plug/unplug IRQ */ if (hdmi->irq_status & HDMI_INT_HOT_PLUG) { hdmi->hpd = readl(hdmi->regs + HDMI_STA) & HDMI_STA_HOT_PLUG; if (hdmi->drm_dev) drm_helper_hpd_irq_event(hdmi->drm_dev); } /* Sw reset and PLL lock are exclusive so we can use the same * event to signal them */ if (hdmi->irq_status & (HDMI_INT_SW_RST | HDMI_INT_DLL_LCK)) { hdmi->event_received = true; wake_up_interruptible(&hdmi->wait_event); } /* Audio FIFO underrun IRQ */ if (hdmi->irq_status & HDMI_INT_AUDIO_FIFO_XRUN) DRM_INFO("Warning: audio FIFO underrun occurs!\n"); return IRQ_HANDLED; }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Gaignard9186.67%250.00%
Arnaud Pouliquen1312.38%125.00%
Fabien Dessenne10.95%125.00%
Total105100.00%4100.00%

/** * HDMI interrupt handler * * @irq: irq number * @arg: connector structure */
static irqreturn_t hdmi_irq(int irq, void *arg) { struct sti_hdmi *hdmi = arg; /* read interrupt status */ hdmi->irq_status = hdmi_read(hdmi, HDMI_INT_STA); /* clear interrupt status */ hdmi_write(hdmi, hdmi->irq_status, HDMI_INT_CLR); /* force sync bus write */ hdmi_read(hdmi, HDMI_INT_STA); return IRQ_WAKE_THREAD; }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Gaignard55100.00%1100.00%
Total55100.00%1100.00%

/** * Set hdmi active area depending on the drm display mode selected * * @hdmi: pointer on the hdmi internal structure */
static void hdmi_active_area(struct sti_hdmi *hdmi) { u32 xmin, xmax; u32 ymin, ymax; xmin = sti_vtg_get_pixel_number(hdmi->mode, 1); xmax = sti_vtg_get_pixel_number(hdmi->mode, hdmi->mode.hdisplay); ymin = sti_vtg_get_line_number(hdmi->mode, 0); ymax = sti_vtg_get_line_number(hdmi->mode, hdmi->mode.vdisplay - 1); hdmi_write(hdmi, xmin, HDMI_ACTIVE_VID_XMIN); hdmi_write(hdmi, xmax, HDMI_ACTIVE_VID_XMAX); hdmi_write(hdmi, ymin, HDMI_ACTIVE_VID_YMIN); hdmi_write(hdmi, ymax, HDMI_ACTIVE_VID_YMAX); }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Gaignard11099.10%150.00%
Vincent Abriou10.90%150.00%
Total111100.00%2100.00%

/** * Overall hdmi configuration * * @hdmi: pointer on the hdmi internal structure */
static void hdmi_config(struct sti_hdmi *hdmi) { u32 conf; DRM_DEBUG_DRIVER("\n"); /* Clear overrun and underrun fifo */ conf = HDMI_CFG_FIFO_OVERRUN_CLR | HDMI_CFG_FIFO_UNDERRUN_CLR; /* Select encryption type and the framing mode */ conf |= HDMI_CFG_ESS_NOT_OESS; if (hdmi->hdmi_monitor) conf |= HDMI_CFG_HDMI_NOT_DVI; /* Set Hsync polarity */ if (hdmi->mode.flags & DRM_MODE_FLAG_NHSYNC) { DRM_DEBUG_DRIVER("H Sync Negative\n"); conf |= HDMI_CFG_H_SYNC_POL_NEG; } /* Set Vsync polarity */ if (hdmi->mode.flags & DRM_MODE_FLAG_NVSYNC) { DRM_DEBUG_DRIVER("V Sync Negative\n"); conf |= HDMI_CFG_V_SYNC_POL_NEG; } /* Enable HDMI */ conf |= HDMI_CFG_DEVICE_EN; hdmi_write(hdmi, conf, HDMI_CFG); }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Gaignard8888.89%133.33%
Vincent Abriou1111.11%266.67%
Total99100.00%3100.00%

/* * Helper to reset info frame * * @hdmi: pointer on the hdmi internal structure * @slot: infoframe to reset */
static void hdmi_infoframe_reset(struct sti_hdmi *hdmi, u32 slot) { u32 val, i; u32 head_offset, pack_offset; switch (slot) { case HDMI_IFRAME_SLOT_AVI: head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AVI); pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AVI); break; case HDMI_IFRAME_SLOT_AUDIO: head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AUDIO); pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AUDIO); break; case HDMI_IFRAME_SLOT_VENDOR: head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_VENDOR); pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_VENDOR); break; default: DRM_ERROR("unsupported infoframe slot: %#x\n", slot); return; } /* Disable transmission for the selected slot */ 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); /* Reset info frame registers */ hdmi_write(hdmi, 0x0, head_offset); for (i = 0; i < HDMI_SW_DI_MAX_WORD; i += sizeof(u32)) hdmi_write(hdmi, 0x0, pack_offset + i); }

Contributors

PersonTokensPropCommitsCommitProp
Vincent Abriou160100.00%2100.00%
Total160100.00%2100.00%

/** * 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; }

Contributors

PersonTokensPropCommitsCommitProp
Arnaud Pouliquen4580.36%150.00%
Benjamin Gaignard1119.64%150.00%
Total56100.00%2100.00%

/** * 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, size_t size) { const u8 *ptr = data; u32 val, slot, mode, i; u32 head_offset, pack_offset; 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); 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); break; case HDMI_INFOFRAME_TYPE_VENDOR: slot = HDMI_IFRAME_SLOT_VENDOR; mode = HDMI_IFRAME_FRAME; head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_VENDOR); pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_VENDOR); 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 is increase by one. */ size = size - HDMI_INFOFRAME_HEADER_SIZE + 1; 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(mode, slot); hdmi_write(hdmi, val, HDMI_SW_DI_CFG); }

Contributors

PersonTokensPropCommitsCommitProp
Arnaud Pouliquen17356.54%125.00%
Benjamin Gaignard9631.37%125.00%
Vincent Abriou3712.09%250.00%
Total306100.00%4100.00%

/** * Prepare and configure the AVI infoframe * * AVI infoframe are transmitted at least once per two video field and * contains information about HDMI transmission mode such as color space, * colorimetry, ... * * @hdmi: pointer on the hdmi internal structure * * Return negative value if error occurs */
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)]; int ret; DRM_DEBUG_DRIVER("\n"); ret = drm_hdmi_avi_infoframe_from_display_mode(&infoframe, mode); if (ret < 0) { DRM_ERROR("failed to setup AVI infoframe: %d\n", ret); return ret; } /* fixed infoframe configuration not linked to the mode */ infoframe.colorspace = hdmi->colorspace; infoframe.quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT; infoframe.colorimetry = HDMI_COLORIMETRY_NONE; ret = hdmi_avi_infoframe_pack(&infoframe, buffer, sizeof(buffer)); if (ret < 0) { DRM_ERROR("failed to pack AVI infoframe: %d\n", ret); return ret; } hdmi_infoframe_write_infopack(hdmi, buffer, ret); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Arnaud Pouliquen11181.62%125.00%
Benjamin Gaignard2014.71%125.00%
Vincent Abriou53.68%250.00%
Total136100.00%4100.00%

/** * 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_params *audio = &hdmi->audio; u8 buffer[HDMI_INFOFRAME_SIZE(AUDIO)]; int ret, val; DRM_DEBUG_DRIVER("enter %s, AIF %s\n", __func__, audio->enabled ? "enable" : "disable"); if (audio->enabled) { /* set audio parameters stored*/ ret = hdmi_audio_infoframe_pack(&audio->cea, buffer, sizeof(buffer)); if (ret < 0) { DRM_ERROR("failed to pack audio infoframe: %d\n", ret); return ret; } hdmi_infoframe_write_infopack(hdmi, buffer, ret); } else { /*disable audio info frame transmission */ val = hdmi_read(hdmi, HDMI_SW_DI_CFG); val &= ~HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, HDMI_IFRAME_SLOT_AUDIO); hdmi_write(hdmi, val, HDMI_SW_DI_CFG); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Arnaud Pouliquen11583.33%250.00%
Benjamin Gaignard1510.87%125.00%
Vincent Abriou85.80%125.00%
Total138100.00%4100.00%

/* * Prepare and configure the VS infoframe * * Vendor Specific infoframe are transmitted once per frame and * contains vendor specific information. * * @hdmi: pointer on the hdmi internal structure * * Return negative value if error occurs */ #define HDMI_VENDOR_INFOFRAME_MAX_SIZE 6
static int hdmi_vendor_infoframe_config(struct sti_hdmi *hdmi) { struct drm_display_mode *mode = &hdmi->mode; struct hdmi_vendor_infoframe infoframe; u8 buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_VENDOR_INFOFRAME_MAX_SIZE]; int ret; DRM_DEBUG_DRIVER("\n"); ret = drm_hdmi_vendor_infoframe_from_display_mode(&infoframe, mode); if (ret < 0) { /* * Going into that statement does not means vendor infoframe * fails. It just informed us that vendor infoframe is not * needed for the selected mode. Only 4k or stereoscopic 3D * mode requires vendor infoframe. So just simply return 0. */ return 0; } ret = hdmi_vendor_infoframe_pack(&infoframe, buffer, sizeof(buffer)); if (ret < 0) { DRM_ERROR("failed to pack VS infoframe: %d\n", ret); return ret; } hdmi_infoframe_write_infopack(hdmi, buffer, ret); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Vincent Abriou10294.44%150.00%
Benjamin Gaignard65.56%150.00%
Total108100.00%2100.00%

/** * Software reset of the hdmi subsystem * * @hdmi: pointer on the hdmi internal structure * */ #define HDMI_TIMEOUT_SWRESET 100 /*milliseconds */
static void hdmi_swreset(struct sti_hdmi *hdmi) { u32 val; DRM_DEBUG_DRIVER("\n"); /* Enable hdmi_audio clock only during hdmi reset */ if (clk_prepare_enable(hdmi->clk_audio)) DRM_INFO("Failed to prepare/enable hdmi_audio clk\n"); /* Sw reset */ hdmi->event_received = false; val = hdmi_read(hdmi, HDMI_CFG); val |= HDMI_CFG_SW_RST_EN; hdmi_write(hdmi, val, HDMI_CFG); /* Wait reset completed */ wait_event_interruptible_timeout(hdmi->wait_event, hdmi->event_received, msecs_to_jiffies (HDMI_TIMEOUT_SWRESET)); /* * HDMI_STA_SW_RST bit is set to '1' when SW_RST bit in HDMI_CFG is * set to '1' and clk_audio is running. */ if ((hdmi_read(hdmi, HDMI_STA) & HDMI_STA_SW_RST) == 0) DRM_DEBUG_DRIVER("Warning: HDMI sw reset timeout occurs\n"); val = hdmi_read(hdmi, HDMI_CFG); val &= ~HDMI_CFG_SW_RST_EN; hdmi_write(hdmi, val, HDMI_CFG); /* Disable hdmi_audio clock. Not used anymore for drm purpose */ clk_disable_unprepare(hdmi->clk_audio); }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Gaignard132100.00%1100.00%
Total132100.00%1100.00%

#define DBGFS_PRINT_STR(str1, str2) seq_printf(s, "%-24s %s\n", str1, str2) #define DBGFS_PRINT_INT(str1, int2) seq_printf(s, "%-24s %d\n", str1, int2) #define DBGFS_DUMP(str, reg) seq_printf(s, "%s %-25s 0x%08X", str, #reg, \ hdmi_read(hdmi, reg)) #define DBGFS_DUMP_DI(reg, slot) DBGFS_DUMP("\n", reg(slot))
static void hdmi_dbg_cfg(struct seq_file *s, int val) { int tmp; seq_puts(s, "\t"); tmp = val & HDMI_CFG_HDMI_NOT_DVI; DBGFS_PRINT_STR("mode:", tmp ? "HDMI" : "DVI"); seq_puts(s, "\t\t\t\t\t"); tmp = val & HDMI_CFG_HDCP_EN; DBGFS_PRINT_STR("HDCP:", tmp ? "enable" : "disable"); seq_puts(s, "\t\t\t\t\t"); tmp = val & HDMI_CFG_ESS_NOT_OESS; DBGFS_PRINT_STR("HDCP mode:", tmp ? "ESS enable" : "OESS enable"); seq_puts(s, "\t\t\t\t\t"); tmp = val & HDMI_CFG_H_SYNC_POL_NEG; DBGFS_PRINT_STR("Hsync polarity:", tmp ? "inverted" : "normal"); seq_puts(s, "\t\t\t\t\t"); tmp = val & HDMI_CFG_V_SYNC_POL_NEG; DBGFS_PRINT_STR("Vsync polarity:", tmp ? "inverted" : "normal"); seq_puts(s, "\t\t\t\t\t"); tmp = val & HDMI_CFG_422_EN; DBGFS_PRINT_STR("YUV422 format:", tmp ? "enable" : "disable"); }

Contributors

PersonTokensPropCommitsCommitProp
Vincent Abriou11973.91%266.67%
Benjamin Gaignard4226.09%133.33%
Total161100.00%3100.00%


static void hdmi_dbg_sta(struct seq_file *s, int val) { int tmp; seq_puts(s, "\t"); tmp = (val & HDMI_STA_DLL_LCK); DBGFS_PRINT_STR("pll:", tmp ? "locked" : "not locked"); seq_puts(s, "\t\t\t\t\t"); tmp = (val & HDMI_STA_HOT_PLUG); DBGFS_PRINT_STR("hdmi cable:", tmp ? "connected" : "not connected"); }

Contributors

PersonTokensPropCommitsCommitProp
Vincent Abriou5782.61%150.00%
Benjamin Gaignard1217.39%150.00%
Total69100.00%2100.00%


static void hdmi_dbg_sw_di_cfg(struct seq_file *s, int val) { int tmp; char *const en_di[] = {"no transmission", "single transmission", "once every field", "once every frame"}; seq_puts(s, "\t"); tmp = (val & HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, 1)); DBGFS_PRINT_STR("Data island 1:", en_di[tmp]); seq_puts(s, "\t\t\t\t\t"); tmp = (val & HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, 2)) >> 4; DBGFS_PRINT_STR("Data island 2:", en_di[tmp]); seq_puts(s, "\t\t\t\t\t"); tmp = (val & HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, 3)) >> 8; DBGFS_PRINT_STR("Data island 3:", en_di[tmp]); seq_puts(s, "\t\t\t\t\t"); tmp = (val & HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, 4)) >> 12; DBGFS_PRINT_STR("Data island 4:", en_di[tmp]); seq_puts(s, "\t\t\t\t\t"); tmp = (val & HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, 5)) >> 16; DBGFS_PRINT_STR("Data island 5:", en_di[tmp]); seq_puts(s, "\t\t\t\t\t"); tmp = (val & HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, 6)) >> 20; DBGFS_PRINT_STR("Data island 6:", en_di[tmp]); }

Contributors

PersonTokensPropCommitsCommitProp
Vincent Abriou20792.83%150.00%
Benjamin Gaignard167.17%150.00%
Total223100.00%2100.00%


static int hdmi_dbg_show(struct seq_file *s, void *data) { struct drm_info_node *node = s->private; struct sti_hdmi *hdmi = (struct sti_hdmi *)node->info_ent->data; seq_printf(s, "HDMI: (vaddr = 0x%p)", hdmi->regs); DBGFS_DUMP("\n", HDMI_CFG); hdmi_dbg_cfg(s, hdmi_read(hdmi, HDMI_CFG)); DBGFS_DUMP("", HDMI_INT_EN); DBGFS_DUMP("\n", HDMI_STA); hdmi_dbg_sta(s, hdmi_read(hdmi, HDMI_STA)); DBGFS_DUMP("", HDMI_ACTIVE_VID_XMIN); seq_puts(s, "\t"); DBGFS_PRINT_INT("Xmin:", hdmi_read(hdmi, HDMI_ACTIVE_VID_XMIN)); DBGFS_DUMP("", HDMI_ACTIVE_VID_XMAX); seq_puts(s, "\t"); DBGFS_PRINT_INT("Xmax:", hdmi_read(hdmi, HDMI_ACTIVE_VID_XMAX)); DBGFS_DUMP("", HDMI_ACTIVE_VID_YMIN); seq_puts(s, "\t"); DBGFS_PRINT_INT("Ymin:", hdmi_read(hdmi, HDMI_ACTIVE_VID_YMIN)); DBGFS_DUMP("", HDMI_ACTIVE_VID_YMAX); seq_puts(s, "\t"); DBGFS_PRINT_INT("Ymax:", hdmi_read(hdmi, HDMI_ACTIVE_VID_YMAX)); DBGFS_DUMP("", HDMI_SW_DI_CFG); hdmi_dbg_sw_di_cfg(s, hdmi_read(hdmi, HDMI_SW_DI_CFG)); DBGFS_DUMP("\n", HDMI_AUDIO_CFG); DBGFS_DUMP("\n", HDMI_SPDIF_FIFO_STATUS); DBGFS_DUMP("\n", HDMI_AUDN); seq_printf(s, "\n AVI Infoframe (Data Island slot N=%d):", HDMI_IFRAME_SLOT_AVI); DBGFS_DUMP_DI(HDMI_SW_DI_N_HEAD_WORD, HDMI_IFRAME_SLOT_AVI); DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD0, HDMI_IFRAME_SLOT_AVI); DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD1, HDMI_IFRAME_SLOT_AVI); DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD2, HDMI_IFRAME_SLOT_AVI); DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD3, HDMI_IFRAME_SLOT_AVI); DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD4, HDMI_IFRAME_SLOT_AVI); DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD5, HDMI_IFRAME_SLOT_AVI); DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD6, HDMI_IFRAME_SLOT_AVI); seq_puts(s, "\n"); seq_printf(s, "\n AUDIO Infoframe (Data Island slot N=%d):", HDMI_IFRAME_SLOT_AUDIO); DBGFS_DUMP_DI(HDMI_SW_DI_N_HEAD_WORD, HDMI_IFRAME_SLOT_AUDIO); DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD0, HDMI_IFRAME_SLOT_AUDIO); DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD1, HDMI_IFRAME_SLOT_AUDIO); DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD2, HDMI_IFRAME_SLOT_AUDIO); DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD3, HDMI_IFRAME_SLOT_AUDIO); DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD4, HDMI_IFRAME_SLOT_AUDIO); DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD5, HDMI_IFRAME_SLOT_AUDIO); DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD6, HDMI_IFRAME_SLOT_AUDIO); seq_puts(s, "\n"); seq_printf(s, "\n VENDOR SPECIFIC Infoframe (Data Island slot N=%d):", HDMI_IFRAME_SLOT_VENDOR); DBGFS_DUMP_DI(HDMI_SW_DI_N_HEAD_WORD, HDMI_IFRAME_SLOT_VENDOR); DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD0, HDMI_IFRAME_SLOT_VENDOR); DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD1, HDMI_IFRAME_SLOT_VENDOR); DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD2, HDMI_IFRAME_SLOT_VENDOR); DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD3, HDMI_IFRAME_SLOT_VENDOR); DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD4, HDMI_IFRAME_SLOT_VENDOR); DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD5, HDMI_IFRAME_SLOT_VENDOR); DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD6, HDMI_IFRAME_SLOT_VENDOR); seq_puts(s, "\n"); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Vincent Abriou43895.42%150.00%
Arnaud Pouliquen214.58%150.00%
Total459100.00%2100.00%

static struct drm_info_list hdmi_debugfs_files[] = { { "hdmi", hdmi_dbg_show, 0, NULL }, };
static int hdmi_debugfs_init(struct sti_hdmi *hdmi, struct drm_minor *minor) { unsigned int i; for (i = 0; i < ARRAY_SIZE(hdmi_debugfs_files); i++) hdmi_debugfs_files[i].data = hdmi; return drm_debugfs_create_files(hdmi_debugfs_files, ARRAY_SIZE(hdmi_debugfs_files), minor->debugfs_root, minor); }

Contributors

PersonTokensPropCommitsCommitProp
Vincent Abriou62100.00%1100.00%
Total62100.00%1100.00%


static void sti_hdmi_disable(struct drm_bridge *bridge) { struct sti_hdmi *hdmi = bridge->driver_private; u32 val = hdmi_read(hdmi, HDMI_CFG); if (!hdmi->enabled) return; DRM_DEBUG_DRIVER("\n"); /* Disable HDMI */ val &= ~HDMI_CFG_DEVICE_EN; hdmi_write(hdmi, val, HDMI_CFG); hdmi_write(hdmi, 0xffffffff, HDMI_INT_CLR); /* Stop the phy */ hdmi->phy_ops->stop(hdmi); /* Reset info frame transmission */ hdmi_infoframe_reset(hdmi, HDMI_IFRAME_SLOT_AVI); hdmi_infoframe_reset(hdmi, HDMI_IFRAME_SLOT_AUDIO); hdmi_infoframe_reset(hdmi, HDMI_IFRAME_SLOT_VENDOR); /* Set the default channel data to be a dark red */ hdmi_write(hdmi, 0x0000, HDMI_DFLT_CHL0_DAT); hdmi_write(hdmi, 0x0000, HDMI_DFLT_CHL1_DAT); hdmi_write(hdmi, 0x0060, HDMI_DFLT_CHL2_DAT); /* Disable/unprepare hdmi clock */ clk_disable_unprepare(hdmi->clk_phy); clk_disable_unprepare(hdmi->clk_tmds); clk_disable_unprepare(hdmi->clk_pix); hdmi->enabled = false; }

Contributors

PersonTokensPropCommitsCommitProp
Vincent Abriou155100.00%1100.00%
Total155100.00%1100.00%

/** * sti_hdmi_audio_get_non_coherent_n() - get N parameter for non-coherent * clocks. None-coherent clocks means that audio and TMDS clocks have not the * same source (drifts between clocks). In this case assumption is that CTS is * automatically calculated by hardware. * * @audio_fs: audio frame clock frequency in Hz * * Values computed are based on table described in HDMI specification 1.4b * * Returns n value. */
static int sti_hdmi_audio_get_non_coherent_n(unsigned int audio_fs) { unsigned int n; switch (audio_fs) { case 32000: n = 4096; break; case 44100: n = 6272; break; case 48000: n = 6144; break; case 88200: n = 6272 * 2; break; case 96000: n = 6144 * 2; break; case 176400: n = 6272 * 4; break; case 192000: n = 6144 * 4; break; default: /* Not pre-defined, recommended value: 128 * fs / 1000 */ n = (audio_fs * 128) / 1000; } return n; }

Contributors

PersonTokensPropCommitsCommitProp
Arnaud Pouliquen99100.00%1100.00%
Total99100.00%1100.00%


static int hdmi_audio_configure(struct sti_hdmi *hdmi) { int audio_cfg, n; struct hdmi_audio_params *params = &hdmi->audio; struct hdmi_audio_infoframe *info = &params->cea; DRM_DEBUG_DRIVER("\n"); if (!hdmi->enabled) return 0; /* update N parameter */ n = sti_hdmi_audio_get_non_coherent_n(params->sample_rate); DRM_DEBUG_DRIVER("Audio rate = %d Hz, TMDS clock = %d Hz, n = %d\n", params->sample_rate, hdmi->mode.clock * 1000, n); hdmi_write(hdmi, n, HDMI_AUDN); /* update HDMI registers according to configuration */ audio_cfg = HDMI_AUD_CFG_SPDIF_DIV_2 | HDMI_AUD_CFG_DTS_INVALID | HDMI_AUD_CFG_ONE_BIT_INVALID; switch (info->channels) { case 8: audio_cfg |= HDMI_AUD_CFG_CH78_VALID; case 6: audio_cfg |= HDMI_AUD_CFG_CH56_VALID; case 4: audio_cfg |= HDMI_AUD_CFG_CH34_VALID | HDMI_AUD_CFG_8CH; case 2: audio_cfg |= HDMI_AUD_CFG_CH12_VALID; break; default: DRM_ERROR("ERROR: Unsupported number of channels (%d)!\n", info->channels); return -EINVAL; } hdmi_write(hdmi, audio_cfg, HDMI_AUDIO_CFG); return hdmi_audio_infoframe_config(hdmi); }

Contributors

PersonTokensPropCommitsCommitProp
Arnaud Pouliquen166100.00%1100.00%
Total166100.00%1100.00%


static void sti_hdmi_pre_enable(struct drm_bridge *bridge) { struct sti_hdmi *hdmi = bridge->driver_private; DRM_DEBUG_DRIVER("\n"); if (hdmi->enabled) return; /* Prepare/enable clocks */ if (clk_prepare_enable(hdmi->clk_pix)) DRM_ERROR("Failed to prepare/enable hdmi_pix clk\n"); if (clk_prepare_enable(hdmi->clk_tmds)) DRM_ERROR("Failed to prepare/enable hdmi_tmds clk\n"); if (clk_prepare_enable(hdmi->clk_phy)) DRM_ERROR("Failed to prepare/enable hdmi_rejec_pll clk\n"); hdmi->enabled = true; /* Program hdmi serializer and start phy */ if (!hdmi->phy_ops->start(hdmi)) { DRM_ERROR("Unable to start hdmi phy\n"); return; } /* Program hdmi active area */ hdmi_active_area(hdmi); /* Enable working interrupts */ hdmi_write(hdmi, HDMI_WORKING_INT, HDMI_INT_EN); /* Program hdmi config */ hdmi_config(hdmi); /* Program AVI infoframe */ if (hdmi_avi_infoframe_config(hdmi)) DRM_ERROR("Unable to configure AVI infoframe\n"); if (hdmi->audio.enabled) { if (hdmi_audio_configure(hdmi)) DRM_ERROR("Unable to configure audio\n"); } else { hdmi_audio_infoframe_config(hdmi); } /* Program VS infoframe */ if (hdmi_vendor_infoframe_config(hdmi)) DRM_ERROR("Unable to configure VS infoframe\n"); /* Sw reset */ hdmi_swreset(hdmi); }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Gaignard9953.23%120.00%
Vincent Abriou5730.65%240.00%
Arnaud Pouliquen3016.13%240.00%
Total186100.00%5100.00%


static void sti_hdmi_set_mode(struct drm_bridge *bridge, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { struct sti_hdmi *hdmi = bridge->driver_private; int ret; DRM_DEBUG_DRIVER("\n"); /* Copy the drm display mode in the connector local structure */ memcpy(&hdmi->mode, mode, sizeof(struct drm_display_mode)); /* Update clock framerate according to the selected mode */ ret = clk_set_rate(hdmi->clk_pix, mode->clock * 1000); if (ret < 0) { DRM_ERROR("Cannot set rate (%dHz) for hdmi_pix clk\n", mode->clock * 1000); return; } ret = clk_set_rate(hdmi->clk_phy, mode->clock * 1000); if (ret < 0) { DRM_ERROR("Cannot set rate (%dHz) for hdmi_rejection_pll clk\n", mode->clock * 1000); return; } }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Gaignard126100.00%1100.00%
Total126100.00%1100.00%


static void sti_hdmi_bridge_nope(struct drm_bridge *bridge) { /* do nothing */ }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Gaignard12100.00%1100.00%
Total12100.00%1100.00%

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, };
static int sti_hdmi_connector_get_modes(struct drm_connector *connector) { struct sti_hdmi_connector *hdmi_connector = to_sti_hdmi_connector(connector); struct sti_hdmi *hdmi = hdmi_connector->hdmi; struct edid *edid; int count; DRM_DEBUG_DRIVER("\n"); edid = drm_get_edid(connector, hdmi->ddc_adapt); if (!edid) goto fail; hdmi->hdmi_monitor = drm_detect_hdmi_monitor(edid); DRM_DEBUG_KMS("%s : %dx%d cm\n", (hdmi->hdmi_monitor ? "hdmi monitor" : "dvi monitor"), edid->width_cm, edid->height_cm); count = drm_add_edid_modes(connector, edid); drm_mode_connector_update_edid_property(connector, edid); drm_edid_to_eld(connector, edid); kfree(edid); return count; fail: DRM_ERROR("Can't read HDMI EDID\n"); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Gaignard9570.37%240.00%
Vincent Abriou3324.44%240.00%
Arnaud Pouliquen75.19%120.00%
Total135100.00%5100.00%

#define CLK_TOLERANCE_HZ 50
static int sti_hdmi_connector_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { int target = mode->clock * 1000; int target_min = target - CLK_TOLERANCE_HZ; int target_max = target + CLK_TOLERANCE_HZ; int result; struct sti_hdmi_connector *hdmi_connector = to_sti_hdmi_connector(connector); struct sti_hdmi *hdmi = hdmi_connector->hdmi; result = clk_round_rate(hdmi->clk_pix, target); DRM_DEBUG_DRIVER("target rate = %d => available rate = %d\n", target, result); if ((result < target_min) || (result > target_max)) { DRM_DEBUG_DRIVER("hdmi pixclk=%d not supported\n", target); return MODE_BAD; } return MODE_OK; }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Gaignard110100.00%1100.00%
Total110100.00%1100.00%

static const struct drm_connector_helper_funcs sti_hdmi_connector_helper_funcs = { .get_modes = sti_hdmi_connector_get_modes, .mode_valid = sti_hdmi_connector_mode_valid, }; /* get detection status of display device */
static enum drm_connector_status sti_hdmi_connector_detect(struct drm_connector *connector, bool force) { struct sti_hdmi_connector *hdmi_connector = to_sti_hdmi_connector(connector); struct sti_hdmi *hdmi = hdmi_connector->hdmi; DRM_DEBUG_DRIVER("\n"); if (hdmi->hpd) { DRM_DEBUG_DRIVER("hdmi cable connected\n"); return connector_status_connected; } DRM_DEBUG_DRIVER("hdmi cable disconnected\n"); return connector_status_disconnected; }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Gaignard63100.00%1100.00%
Total63100.00%1100.00%


static void sti_hdmi_connector_init_property(struct drm_device *drm_dev, struct drm_connector *connector) { struct sti_hdmi_connector *hdmi_connector = to_sti_hdmi_connector(connector); struct sti_hdmi *hdmi = hdmi_connector->hdmi; struct drm_property *prop; /* colorspace property */ hdmi->colorspace = DEFAULT_COLORSPACE_MODE; prop = drm_property_create_enum(drm_dev, 0, "colorspace", colorspace_mode_names, ARRAY_SIZE(colorspace_mode_names)); if (!prop) { DRM_ERROR("fails to create colorspace property\n"); return; } hdmi_connector->colorspace_property = prop; drm_object_attach_property(&connector->base, prop, hdmi->colorspace); }

Contributors

PersonTokensPropCommitsCommitProp
Vincent Abriou98100.00%1100.00%
Total98100.00%1100.00%


static int sti_hdmi_connector_set_property(struct drm_connector *connector, struct drm_connector_state *state, struct drm_property *property, uint64_t val) { struct sti_hdmi_connector *hdmi_connector = to_sti_hdmi_connector(connector); struct sti_hdmi *hdmi = hdmi_connector->hdmi; if (property == hdmi_connector->colorspace_property) { hdmi->colorspace = val; return 0; } DRM_ERROR("failed to set hdmi connector property\n"); return -EINVAL; }

Contributors

PersonTokensPropCommitsCommitProp
Vincent Abriou71100.00%1100.00%
Total71100.00%1100.00%


static int sti_hdmi_connector_get_property(struct drm_connector *connector, const struct drm_connector_state *state, struct drm_property *property, uint64_t *val) { struct sti_hdmi_connector *hdmi_connector = to_sti_hdmi_connector(connector); struct sti_hdmi *hdmi = hdmi_connector->hdmi; if (property == hdmi_connector->colorspace_property) { *val = hdmi->colorspace; return 0; } DRM_ERROR("failed to get hdmi connector property\n"); return -EINVAL; }

Contributors

PersonTokensPropCommitsCommitProp
Vincent Abriou74100.00%1100.00%
Total74100.00%1100.00%


static int sti_hdmi_late_register(struct drm_connector *connector) { struct sti_hdmi_connector *hdmi_connector = to_sti_hdmi_connector(connector); struct sti_hdmi *hdmi = hdmi_connector->hdmi; if (hdmi_debugfs_init(hdmi, hdmi->drm_dev->primary)) { DRM_ERROR("HDMI debugfs setup failed\n"); return -EINVAL; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Gaignard57100.00%1100.00%
Total57100.00%1100.00%

static const struct drm_connector_funcs sti_hdmi_connector_funcs = { .dpms = drm_atomic_helper_connector_dpms, .fill_modes = drm_helper_probe_single_connector_modes, .detect = sti_hdmi_connector_detect, .destroy = drm_connector_cleanup, .reset = drm_atomic_helper_connector_reset, .set_property = drm_atomic_helper_connector_set_property, .atomic_set_property = sti_hdmi_connector_set_property, .atomic_get_property = sti_hdmi_connector_get_property, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, .late_register = sti_hdmi_late_register, };
static struct drm_encoder *sti_hdmi_find_encoder(struct drm_device *dev) { struct drm_encoder *encoder; list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { if (encoder->encoder_type == DRM_MODE_ENCODER_TMDS) return encoder; } return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Arnaud Pouliquen42100.00%2100.00%
Total42100.00%2100.00%


static void hdmi_audio_shutdown(struct device *dev, void *data) { struct sti_hdmi *hdmi = dev_get_drvdata(dev); int audio_cfg; DRM_DEBUG_DRIVER("\n"); /* disable audio */ audio_cfg = HDMI_AUD_CFG_SPDIF_DIV_2 | HDMI_AUD_CFG_DTS_INVALID | HDMI_AUD_CFG_ONE_BIT_INVALID; hdmi_write(hdmi, audio_cfg, HDMI_AUDIO_CFG); hdmi->audio.enabled = false; hdmi_audio_infoframe_config(hdmi); }

Contributors

PersonTokensPropCommitsCommitProp
Arnaud Pouliquen5992.19%133.33%
Dave Airlie46.25%133.33%
Benjamin Gaignard11.56%133.33%
Total64100.00%3100.00%


static int hdmi_audio_hw_params(struct device *dev, void *data, struct hdmi_codec_daifmt *daifmt, struct hdmi_codec_params *params) { struct sti_hdmi *hdmi = dev_get_drvdata(dev); int ret; DRM_DEBUG_DRIVER("\n"); 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; } hdmi->audio.sample_width = params->sample_width; hdmi->audio.sample_rate = params->sample_rate; hdmi->audio.cea = params->cea; hdmi->audio.enabled = true; ret = hdmi_audio_configure(hdmi); if (ret < 0) return ret; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Arnaud Pouliquen15296.82%250.00%
Dave Airlie42.55%125.00%
Benjamin Gaignard10.64%125.00%
Total157100.00%4100.00%


static int hdmi_audio_digital_mute(struct device *dev, void *data, bool enable) { struct sti_hdmi *hdmi = dev_get_drvdata(dev); DRM_DEBUG_DRIVER("%s\n", enable ? "enable" : "disable"); if (enable) hdmi_write(hdmi, HDMI_SAMPLE_FLAT_ALL, HDMI_SAMPLE_FLAT_MASK); else hdmi_write(hdmi, HDMI_SAMPLE_FLAT_NO, HDMI_SAMPLE_FLAT_MASK); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Arnaud Pouliquen6193.85%150.00%
Dave Airlie46.15%150.00%
Total65100.00%2100.00%


static int hdmi_audio_get_eld(struct device *dev, void *data, uint8_t *buf, size_t len) { struct sti_hdmi *hdmi = dev_get_drvdata(dev); struct drm_connector *connector = hdmi->drm_connector; DRM_DEBUG_DRIVER("\n"); memcpy(buf, connector->eld, min(sizeof(connector->eld), len)); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Arnaud Pouliquen6694.29%150.00%
Dave Airlie45.71%150.00%
Total70100.00%2100.00%

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 sti_hdmi_register_audio_driver(struct device *dev, struct sti_hdmi *hdmi) { struct hdmi_codec_pdata codec_data = { .ops = &audio_codec_ops, .max_i2s_channels = 8, .i2s = 1, }; DRM_DEBUG_DRIVER("\n"); hdmi->audio.enabled = false; hdmi->audio_pdev = platform_device_register_data( dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO, &codec_data, sizeof(codec_data)); if (IS_ERR(hdmi->audio_pdev)) return PTR_ERR(hdmi->audio_pdev); DRM_INFO("%s Driver bound %s\n", HDMI_CODEC_DRV_NAME, dev_name(dev)); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Arnaud Pouliquen10399.04%150.00%
Benjamin Gaignard10.96%150.00%
Total104100.00%2100.00%


static int sti_hdmi_bind(struct device *dev, struct device *master, void *data) { struct sti_hdmi *hdmi = dev_get_drvdata(dev); struct drm_device *drm_dev = data; struct drm_encoder *encoder; struct sti_hdmi_connector *connector; struct drm_connector *drm_connector; struct drm_bridge *bridge; int err; /* Set the drm device handle */ hdmi->drm_dev = drm_dev; encoder = sti_hdmi_find_encoder(drm_dev); if (!encoder) return -EINVAL; connector = devm_kzalloc(dev, sizeof(*connector), GFP_KERNEL); if (!connector) return -EINVAL; connector->hdmi = hdmi; bridge = devm_kzalloc(dev, sizeof(*bridge), GFP_KERNEL); if (!bridge) return -EINVAL; bridge->driver_private = hdmi; bridge->funcs = &sti_hdmi_bridge_funcs; drm_bridge_attach(encoder, bridge, NULL); connector->encoder = encoder; drm_connector = (struct drm_connector *)connector; drm_connector->polled = DRM_CONNECTOR_POLL_HPD; drm_connector_init(drm_dev, drm_connector, &sti_hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA); drm_connector_helper_add(drm_connector, &sti_hdmi_connector_helper_funcs); /* initialise property */ sti_hdmi_connector_init_property(drm_dev, drm_connector); hdmi->drm_connector = drm_connector; err = drm_mode_connector_attach_encoder(drm_connector, encoder); if (err) { DRM_ERROR("Failed to attach a connector to a encoder\n"); goto err_sysfs; } err = sti_hdmi_register_audio_driver(dev, hdmi); if (err) { DRM_ERROR("Failed to attach an audio codec\n"); goto err_sysfs; } /* Initialize audio infoframe */ err = hdmi_audio_infoframe_init(&hdmi->audio.cea); if (err) { DRM_ERROR("Failed to init audio infoframe\n"); goto err_sysfs; } /* Enable default interrupts */ hdmi_write(hdmi, HDMI_DEFAULT_INT, HDMI_INT_EN); return 0; err_sysfs: drm_bridge_remove(bridge); hdmi->drm_connector = NULL; return -EINVAL; }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Gaignard18758.99%225.00%
Arnaud Pouliquen9931.23%112.50%
Vladimir Zapolskiy123.79%112.50%
Vincent Abriou82.52%112.50%
Ajay Kumar82.52%225.00%
Laurent Pinchart30.95%112.50%
Total317100.00%8100.00%


static void sti_hdmi_unbind(struct device *dev, struct device *master, void *data) { }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Gaignard1894.74%150.00%
Noralf Trönnes15.26%150.00%
Total19100.00%2100.00%

static const struct component_ops sti_hdmi_ops = { .bind = sti_hdmi_bind, .unbind = sti_hdmi_unbind, }; static const struct of_device_id hdmi_of_match[] = { { .compatible = "st,stih407-hdmi", .data = &tx3g4c28phy_ops, }, { /* end node */ } }; MODULE_DEVICE_TABLE(of, hdmi_of_match);
static int sti_hdmi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct sti_hdmi *hdmi; struct device_node *np = dev->of_node; struct resource *res; struct device_node *ddc; int ret; DRM_INFO("%s\n", __func__); hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); if (!hdmi) return -ENOMEM; ddc = of_parse_phandle(pdev->dev.of_node, "ddc", 0); if (ddc) { hdmi->ddc_adapt = of_get_i2c_adapter_by_node(ddc); of_node_put(ddc); if (!hdmi->ddc_adapt) return -EPROBE_DEFER; } hdmi->dev = pdev->dev; /* Get resources */ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hdmi-reg"); if (!res) { DRM_ERROR("Invalid hdmi resource\n"); ret = -ENOMEM; goto release_adapter; } hdmi->regs = devm_ioremap_nocache(dev, res->start, resource_size(res)); if (!hdmi->regs) { ret = -ENOMEM; goto release_adapter; } hdmi->phy_ops = (struct hdmi_phy_ops *) of_match_node(hdmi_of_match, np)->data; /* Get clock resources */ hdmi->clk_pix = devm_clk_get(dev, "pix"); if (IS_ERR(hdmi->clk_pix)) { DRM_ERROR("Cannot get hdmi_pix clock\n"); ret = PTR_ERR(hdmi->clk_pix); goto release_adapter; } hdmi->clk_tmds = devm_clk_get(dev, "tmds"); if (IS_ERR(hdmi->clk_tmds)) { DRM_ERROR("Cannot get hdmi_tmds clock\n"); ret = PTR_ERR(hdmi->clk_tmds); goto release_adapter; } hdmi->clk_phy = devm_clk_get(dev, "phy"); if (IS_ERR(hdmi->clk_phy)) { DRM_ERROR("Cannot get hdmi_phy clock\n"); ret = PTR_ERR(hdmi->clk_phy); goto release_adapter; } hdmi->clk_audio = devm_clk_get(dev, "audio"); if (IS_ERR(hdmi->clk_audio)) { DRM_ERROR("Cannot get hdmi_audio clock\n"); ret = PTR_ERR(hdmi->clk_audio); goto release_adapter; } hdmi->hpd = readl(hdmi->regs + HDMI_STA) & HDMI_STA_HOT_PLUG; init_waitqueue_head(&hdmi->wait_event); hdmi->irq = platform_get_irq_byname(pdev, "irq"); ret = devm_request_threaded_irq(dev, hdmi->irq, hdmi_irq, hdmi_irq_thread, IRQF_ONESHOT, dev_name(dev), hdmi); if (ret) { DRM_ERROR("Failed to register HDMI interrupt\n"); goto release_adapter; } hdmi->reset = devm_reset_control_get(dev, "hdmi"); /* Take hdmi out of reset */ if (!IS_ERR(hdmi->reset)) reset_control_deassert(hdmi->reset); platform_set_drvdata(pdev, hdmi); return component_add(&pdev->dev, &sti_hdmi_ops); release_adapter: i2c_put_adapter(hdmi->ddc_adapt); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Gaignard44587.77%350.00%
Vladimir Zapolskiy5911.64%233.33%
Wei Yongjun30.59%116.67%
Total507100.00%6100.00%


static int sti_hdmi_remove(struct platform_device *pdev) { struct sti_hdmi *hdmi = dev_get_drvdata(&pdev->dev); i2c_put_adapter(hdmi->ddc_adapt); if (hdmi->audio_pdev) platform_device_unregister(hdmi->audio_pdev); component_del(&pdev->dev, &sti_hdmi_ops); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Gaignard4475.86%250.00%
Arnaud Pouliquen1322.41%125.00%
Vladimir Zapolskiy11.72%125.00%
Total58100.00%4100.00%

struct platform_driver sti_hdmi_driver = { .driver = { .name = "sti-hdmi", .owner = THIS_MODULE, .of_match_table = hdmi_of_match, }, .probe = sti_hdmi_probe, .remove = sti_hdmi_remove, }; MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>"); MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver"); MODULE_LICENSE("GPL");

Overall Contributors

PersonTokensPropCommitsCommitProp
Benjamin Gaignard254442.06%825.00%
Vincent Abriou183130.27%1031.25%
Arnaud Pouliquen156725.91%39.38%
Vladimir Zapolskiy721.19%26.25%
Dave Airlie160.26%13.12%
Ajay Kumar80.13%26.25%
Wei Yongjun30.05%13.12%
Laurent Pinchart30.05%13.12%
Ville Syrjälä20.03%13.12%
Kiran Padwal10.02%13.12%
Fabien Dessenne10.02%13.12%
Noralf Trönnes10.02%13.12%
Total6049100.00%32100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.