You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
drm: sti: add DVO output connector
Digital Video Out connector driver LCD panels. Like HDMI and HDA it create bridge, encoder and connector drm object. Add binding description. Signed-off-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
This commit is contained in:
@@ -83,6 +83,22 @@ sti-hda:
|
||||
- clock-names: names of the clocks listed in clocks property in the same
|
||||
order.
|
||||
|
||||
sti-dvo:
|
||||
Required properties:
|
||||
must be a child of sti-tvout
|
||||
- compatible: "st,stih<chip>-dvo"
|
||||
- reg: Physical base address of the IP registers and length of memory mapped region.
|
||||
- reg-names: names of the mapped memory regions listed in regs property in
|
||||
the same order.
|
||||
- clocks: from common clock binding: handle hardware IP needed clocks, the
|
||||
number of clocks may depend of the SoC type.
|
||||
See ../clocks/clock-bindings.txt for details.
|
||||
- clock-names: names of the clocks listed in clocks property in the same
|
||||
order.
|
||||
- pinctrl-0: pin control handle
|
||||
- pinctrl-name: names of the pin control to use
|
||||
- sti,panel: phandle of the panel connected to the DVO output
|
||||
|
||||
sti-hqvdp:
|
||||
must be a child of sti-display-subsystem
|
||||
Required properties:
|
||||
@@ -198,6 +214,19 @@ Example:
|
||||
clock-names = "pix", "hddac";
|
||||
clocks = <&clockgen_c_vcc CLK_S_PIX_HD>, <&clockgen_c_vcc CLK_S_HDDAC>;
|
||||
};
|
||||
|
||||
sti-dvo@8d00400 {
|
||||
compatible = "st,stih407-dvo";
|
||||
reg = <0x8d00400 0x200>;
|
||||
reg-names = "dvo-reg";
|
||||
clock-names = "dvo_pix", "dvo",
|
||||
"main_parent", "aux_parent";
|
||||
clocks = <&clk_s_d2_flexgen CLK_PIX_DVO>, <&clk_s_d2_flexgen CLK_DVO>,
|
||||
<&clk_s_d2_quadfs 0>, <&clk_s_d2_quadfs 1>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_dvo>;
|
||||
sti,panel = <&panel_dvo>;
|
||||
};
|
||||
};
|
||||
|
||||
sti-hqvdp@9c000000 {
|
||||
|
||||
@@ -12,6 +12,9 @@ stihdmi-y := sti_hdmi.o \
|
||||
sti_hdmi_tx3g0c55phy.o \
|
||||
sti_hdmi_tx3g4c28phy.o \
|
||||
|
||||
stidvo-y := sti_dvo.o \
|
||||
sti_awg_utils.o
|
||||
|
||||
obj-$(CONFIG_DRM_STI) = \
|
||||
sti_vtg.o \
|
||||
sti_vtac.o \
|
||||
@@ -20,4 +23,5 @@ obj-$(CONFIG_DRM_STI) = \
|
||||
sti_tvout.o \
|
||||
sticompositor.o \
|
||||
sti_hqvdp.o \
|
||||
stidvo.o \
|
||||
sti_drm_drv.o
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright (C) STMicroelectronics SA 2014
|
||||
* Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
|
||||
* License terms: GNU General Public License (GPL), version 2
|
||||
*/
|
||||
|
||||
#include "sti_awg_utils.h"
|
||||
|
||||
#define AWG_OPCODE_OFFSET 10
|
||||
|
||||
enum opcode {
|
||||
SET,
|
||||
RPTSET,
|
||||
RPLSET,
|
||||
SKIP,
|
||||
STOP,
|
||||
REPEAT,
|
||||
REPLAY,
|
||||
JUMP,
|
||||
HOLD,
|
||||
};
|
||||
|
||||
static int awg_generate_instr(enum opcode opcode,
|
||||
long int arg,
|
||||
long int mux_sel,
|
||||
long int data_en,
|
||||
struct awg_code_generation_params *fwparams)
|
||||
{
|
||||
u32 instruction = 0;
|
||||
u32 mux = (mux_sel << 8) & 0x1ff;
|
||||
u32 data_enable = (data_en << 9) & 0x2ff;
|
||||
long int arg_tmp = arg;
|
||||
|
||||
/* skip, repeat and replay arg should not exceed 1023.
|
||||
* If user wants to exceed this value, the instruction should be
|
||||
* duplicate and arg should be adjust for each duplicated instruction.
|
||||
*/
|
||||
|
||||
while (arg_tmp > 0) {
|
||||
arg = arg_tmp;
|
||||
if (fwparams->instruction_offset >= AWG_MAX_INST) {
|
||||
DRM_ERROR("too many number of instructions\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (opcode) {
|
||||
case SKIP:
|
||||
/* leave 'arg' + 1 pixel elapsing without changing
|
||||
* output bus */
|
||||
arg--; /* pixel adjustment */
|
||||
arg_tmp--;
|
||||
|
||||
if (arg < 0) {
|
||||
/* SKIP instruction not needed */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg == 0) {
|
||||
/* SKIP 0 not permitted but we want to skip 1
|
||||
* pixel. So we transform SKIP into SET
|
||||
* instruction */
|
||||
opcode = SET;
|
||||
arg = (arg << 24) >> 24;
|
||||
arg &= (0x0ff);
|
||||
break;
|
||||
}
|
||||
|
||||
mux = 0;
|
||||
data_enable = 0;
|
||||
arg = (arg << 22) >> 22;
|
||||
arg &= (0x3ff);
|
||||
break;
|
||||
case REPEAT:
|
||||
case REPLAY:
|
||||
if (arg == 0) {
|
||||
/* REPEAT or REPLAY instruction not needed */
|
||||
return 0;
|
||||
}
|
||||
|
||||
mux = 0;
|
||||
data_enable = 0;
|
||||
arg = (arg << 22) >> 22;
|
||||
arg &= (0x3ff);
|
||||
break;
|
||||
case JUMP:
|
||||
mux = 0;
|
||||
data_enable = 0;
|
||||
arg |= 0x40; /* for jump instruction 7th bit is 1 */
|
||||
arg = (arg << 22) >> 22;
|
||||
arg &= 0x3ff;
|
||||
break;
|
||||
case STOP:
|
||||
arg = 0;
|
||||
break;
|
||||
case SET:
|
||||
case RPTSET:
|
||||
case RPLSET:
|
||||
case HOLD:
|
||||
arg = (arg << 24) >> 24;
|
||||
arg &= (0x0ff);
|
||||
break;
|
||||
default:
|
||||
DRM_ERROR("instruction %d does not exist\n", opcode);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
arg_tmp = arg_tmp - arg;
|
||||
|
||||
arg = ((arg + mux) + data_enable);
|
||||
|
||||
instruction = ((opcode) << AWG_OPCODE_OFFSET) | arg;
|
||||
fwparams->ram_code[fwparams->instruction_offset] =
|
||||
instruction & (0x3fff);
|
||||
fwparams->instruction_offset++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sti_awg_generate_code_data_enable_mode(
|
||||
struct awg_code_generation_params *fwparams,
|
||||
struct awg_timing *timing)
|
||||
{
|
||||
long int val;
|
||||
long int data_en;
|
||||
int ret = 0;
|
||||
|
||||
if (timing->trailing_lines > 0) {
|
||||
/* skip trailing lines */
|
||||
val = timing->blanking_level;
|
||||
data_en = 0;
|
||||
ret |= awg_generate_instr(RPLSET, val, 0, data_en, fwparams);
|
||||
|
||||
val = timing->trailing_lines - 1;
|
||||
data_en = 0;
|
||||
ret |= awg_generate_instr(REPLAY, val, 0, data_en, fwparams);
|
||||
}
|
||||
|
||||
if (timing->trailing_pixels > 0) {
|
||||
/* skip trailing pixel */
|
||||
val = timing->blanking_level;
|
||||
data_en = 0;
|
||||
ret |= awg_generate_instr(RPLSET, val, 0, data_en, fwparams);
|
||||
|
||||
val = timing->trailing_pixels - 1;
|
||||
data_en = 0;
|
||||
ret |= awg_generate_instr(SKIP, val, 0, data_en, fwparams);
|
||||
}
|
||||
|
||||
/* set DE signal high */
|
||||
val = timing->blanking_level;
|
||||
data_en = 1;
|
||||
ret |= awg_generate_instr((timing->trailing_pixels > 0) ? SET : RPLSET,
|
||||
val, 0, data_en, fwparams);
|
||||
|
||||
if (timing->blanking_pixels > 0) {
|
||||
/* skip the number of active pixel */
|
||||
val = timing->active_pixels - 1;
|
||||
data_en = 1;
|
||||
ret |= awg_generate_instr(SKIP, val, 0, data_en, fwparams);
|
||||
|
||||
/* set DE signal low */
|
||||
val = timing->blanking_level;
|
||||
data_en = 0;
|
||||
ret |= awg_generate_instr(SET, val, 0, data_en, fwparams);
|
||||
}
|
||||
|
||||
/* replay the sequence as many active lines defined */
|
||||
val = timing->active_lines - 1;
|
||||
data_en = 0;
|
||||
ret |= awg_generate_instr(REPLAY, val, 0, data_en, fwparams);
|
||||
|
||||
if (timing->blanking_lines > 0) {
|
||||
/* skip blanking lines */
|
||||
val = timing->blanking_level;
|
||||
data_en = 0;
|
||||
ret |= awg_generate_instr(RPLSET, val, 0, data_en, fwparams);
|
||||
|
||||
val = timing->blanking_lines - 1;
|
||||
data_en = 0;
|
||||
ret |= awg_generate_instr(REPLAY, val, 0, data_en, fwparams);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) STMicroelectronics SA 2014
|
||||
* Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
|
||||
* License terms: GNU General Public License (GPL), version 2
|
||||
*/
|
||||
|
||||
#ifndef _STI_AWG_UTILS_H_
|
||||
#define _STI_AWG_UTILS_H_
|
||||
|
||||
#include <drm/drmP.h>
|
||||
|
||||
#define AWG_MAX_INST 64
|
||||
|
||||
struct awg_code_generation_params {
|
||||
u32 *ram_code;
|
||||
u8 instruction_offset;
|
||||
};
|
||||
|
||||
struct awg_timing {
|
||||
u32 total_lines;
|
||||
u32 active_lines;
|
||||
u32 blanking_lines;
|
||||
u32 trailing_lines;
|
||||
u32 total_pixels;
|
||||
u32 active_pixels;
|
||||
u32 blanking_pixels;
|
||||
u32 trailing_pixels;
|
||||
u32 blanking_level;
|
||||
};
|
||||
|
||||
int sti_awg_generate_code_data_enable_mode(
|
||||
struct awg_code_generation_params *fw_gen_params,
|
||||
struct awg_timing *timing);
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -48,6 +48,9 @@
|
||||
#define TVO_HDMI_CLIP_VALUE_R_CR 0x514
|
||||
#define TVO_HDMI_SYNC_SEL 0x518
|
||||
#define TVO_HDMI_DFV_OBS 0x540
|
||||
#define TVO_VIP_DVO 0x600
|
||||
#define TVO_DVO_SYNC_SEL 0x618
|
||||
#define TVO_DVO_CONFIG 0x620
|
||||
|
||||
#define TVO_IN_FMT_SIGNED BIT(0)
|
||||
#define TVO_SYNC_EXT BIT(4)
|
||||
@@ -98,6 +101,9 @@
|
||||
|
||||
#define TVO_SYNC_HD_DCS_SHIFT 8
|
||||
|
||||
#define TVO_SYNC_DVO_PAD_HSYNC_SHIFT 8
|
||||
#define TVO_SYNC_DVO_PAD_VSYNC_SHIFT 16
|
||||
|
||||
#define ENCODER_CRTC_MASK (BIT(0) | BIT(1))
|
||||
|
||||
/* enum listing the supported output data format */
|
||||
@@ -113,6 +119,7 @@ struct sti_tvout {
|
||||
struct reset_control *reset;
|
||||
struct drm_encoder *hdmi;
|
||||
struct drm_encoder *hda;
|
||||
struct drm_encoder *dvo;
|
||||
};
|
||||
|
||||
struct sti_tvout_encoder {
|
||||
@@ -261,6 +268,66 @@ static void tvout_vip_set_in_vid_fmt(struct sti_tvout *tvout,
|
||||
tvout_write(tvout, val, reg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start VIP block for DVO output
|
||||
*
|
||||
* @tvout: pointer on tvout structure
|
||||
* @main_path: true if main path has to be used in the vip configuration
|
||||
* else aux path is used.
|
||||
*/
|
||||
static void tvout_dvo_start(struct sti_tvout *tvout, bool main_path)
|
||||
{
|
||||
struct device_node *node = tvout->dev->of_node;
|
||||
bool sel_input_logic_inverted = false;
|
||||
u32 tvo_in_vid_format;
|
||||
int val;
|
||||
|
||||
dev_dbg(tvout->dev, "%s\n", __func__);
|
||||
|
||||
if (main_path) {
|
||||
DRM_DEBUG_DRIVER("main vip for DVO\n");
|
||||
/* Select the input sync for dvo = VTG set 4 */
|
||||
val = TVO_SYNC_MAIN_VTG_SET_4 << TVO_SYNC_DVO_PAD_VSYNC_SHIFT;
|
||||
val |= TVO_SYNC_MAIN_VTG_SET_4 << TVO_SYNC_DVO_PAD_HSYNC_SHIFT;
|
||||
val |= TVO_SYNC_MAIN_VTG_SET_4;
|
||||
tvout_write(tvout, val, TVO_DVO_SYNC_SEL);
|
||||
tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT;
|
||||
} else {
|
||||
DRM_DEBUG_DRIVER("aux vip for DVO\n");
|
||||
/* Select the input sync for dvo = VTG set 4 */
|
||||
val = TVO_SYNC_AUX_VTG_SET_4 << TVO_SYNC_DVO_PAD_VSYNC_SHIFT;
|
||||
val |= TVO_SYNC_AUX_VTG_SET_4 << TVO_SYNC_DVO_PAD_HSYNC_SHIFT;
|
||||
val |= TVO_SYNC_AUX_VTG_SET_4;
|
||||
tvout_write(tvout, val, TVO_DVO_SYNC_SEL);
|
||||
tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT;
|
||||
}
|
||||
|
||||
/* Set color channel order */
|
||||
tvout_vip_set_color_order(tvout, TVO_VIP_DVO,
|
||||
TVO_VIP_REORDER_CR_R_SEL,
|
||||
TVO_VIP_REORDER_Y_G_SEL,
|
||||
TVO_VIP_REORDER_CB_B_SEL);
|
||||
|
||||
/* Set clipping mode (Limited range RGB/Y) */
|
||||
tvout_vip_set_clip_mode(tvout, TVO_VIP_DVO,
|
||||
TVO_VIP_CLIP_LIMITED_RANGE_RGB_Y);
|
||||
|
||||
/* Set round mode (rounded to 8-bit per component) */
|
||||
tvout_vip_set_rnd(tvout, TVO_VIP_DVO, TVO_VIP_RND_8BIT_ROUNDED);
|
||||
|
||||
if (of_device_is_compatible(node, "st,stih407-tvout")) {
|
||||
/* Set input video format */
|
||||
tvout_vip_set_in_vid_fmt(tvout, tvo_in_vid_format,
|
||||
TVO_IN_FMT_SIGNED);
|
||||
sel_input_logic_inverted = true;
|
||||
}
|
||||
|
||||
/* Input selection */
|
||||
tvout_vip_set_sel_input(tvout, TVO_VIP_DVO, main_path,
|
||||
sel_input_logic_inverted,
|
||||
STI_TVOUT_VIDEO_OUT_RGB);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start VIP block for HDMI output
|
||||
*
|
||||
@@ -402,6 +469,56 @@ static const struct drm_encoder_funcs sti_tvout_encoder_funcs = {
|
||||
.destroy = sti_tvout_encoder_destroy,
|
||||
};
|
||||
|
||||
static void sti_dvo_encoder_commit(struct drm_encoder *encoder)
|
||||
{
|
||||
struct sti_tvout *tvout = to_sti_tvout(encoder);
|
||||
|
||||
tvout_dvo_start(tvout, sti_drm_crtc_is_main(encoder->crtc));
|
||||
}
|
||||
|
||||
static void sti_dvo_encoder_disable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct sti_tvout *tvout = to_sti_tvout(encoder);
|
||||
|
||||
/* Reset VIP register */
|
||||
tvout_write(tvout, 0x0, TVO_VIP_DVO);
|
||||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs sti_dvo_encoder_helper_funcs = {
|
||||
.dpms = sti_tvout_encoder_dpms,
|
||||
.mode_fixup = sti_tvout_encoder_mode_fixup,
|
||||
.mode_set = sti_tvout_encoder_mode_set,
|
||||
.prepare = sti_tvout_encoder_prepare,
|
||||
.commit = sti_dvo_encoder_commit,
|
||||
.disable = sti_dvo_encoder_disable,
|
||||
};
|
||||
|
||||
static struct drm_encoder *
|
||||
sti_tvout_create_dvo_encoder(struct drm_device *dev,
|
||||
struct sti_tvout *tvout)
|
||||
{
|
||||
struct sti_tvout_encoder *encoder;
|
||||
struct drm_encoder *drm_encoder;
|
||||
|
||||
encoder = devm_kzalloc(tvout->dev, sizeof(*encoder), GFP_KERNEL);
|
||||
if (!encoder)
|
||||
return NULL;
|
||||
|
||||
encoder->tvout = tvout;
|
||||
|
||||
drm_encoder = (struct drm_encoder *)encoder;
|
||||
|
||||
drm_encoder->possible_crtcs = ENCODER_CRTC_MASK;
|
||||
drm_encoder->possible_clones = 1 << 0;
|
||||
|
||||
drm_encoder_init(dev, drm_encoder,
|
||||
&sti_tvout_encoder_funcs, DRM_MODE_ENCODER_LVDS);
|
||||
|
||||
drm_encoder_helper_add(drm_encoder, &sti_dvo_encoder_helper_funcs);
|
||||
|
||||
return drm_encoder;
|
||||
}
|
||||
|
||||
static void sti_hda_encoder_commit(struct drm_encoder *encoder)
|
||||
{
|
||||
struct sti_tvout *tvout = to_sti_tvout(encoder);
|
||||
@@ -508,6 +625,7 @@ static void sti_tvout_create_encoders(struct drm_device *dev,
|
||||
{
|
||||
tvout->hdmi = sti_tvout_create_hdmi_encoder(dev, tvout);
|
||||
tvout->hda = sti_tvout_create_hda_encoder(dev, tvout);
|
||||
tvout->dvo = sti_tvout_create_dvo_encoder(dev, tvout);
|
||||
}
|
||||
|
||||
static void sti_tvout_destroy_encoders(struct sti_tvout *tvout)
|
||||
|
||||
Reference in New Issue
Block a user