mirror of
https://github.com/torvalds/linux.git
synced 2024-11-16 00:52:01 +00:00
Merge branch '2016-02-26-st-drm-next' of http://git.linaro.org/people/benjamin.gaignard/kernel into drm-next
Here are sti patches for drm-next. It brings: - The support of the atomic_check for the planes and minor fixes for planes - The support of the vendor specific infoframe for HDMI and the support of 2 HDMI properties related to the connector - The support of the DVO solving panel detection issue and timing issue. - The support of debugfs for connectors, encoders, crtcs and planes. * '2016-02-26-st-drm-next' of http://git.linaro.org/people/benjamin.gaignard/kernel: (36 commits) drm/sti: use u32 to store DMA addresses drm: sti: remove sti_gem_prime_export hack drm/sti: add debugfs fps_show/fps_get mechanism for planes drm/sti: add debugfs entries for TVOUT encoders drm/sti: add debugfs entries for MIXER crtc drm/sti: add debugfs entries for VID plane drm/sti: add debugfs entries for HQVDP plane drm/sti: add debugfs entries for GDP planes drm/sti: add debugfs entries for CURSOR plane drm/sti: add debugfs entries for HDA connector drm/sti: add debugfs entries for DVO connector drm/sti: add debugfs entries for HDMI connector drm/sti: add hdmi_mode property for HDMI connector drm/sti: add colorspace property to the HDMI connector drm/sti: add HDMI vendor specific infoframe drm/sti: reset infoframe transmission when HDMI is stopped drm/sti: HDMI infoframe transmission mode not take into account drm/sti: reset HD DACS when HDA connector is created drm/sti: fix dvo data_enable signal drm/sti: adjust delay for DVO ...
This commit is contained in:
commit
cf481068cd
@ -7,6 +7,7 @@
|
||||
#include "sti_awg_utils.h"
|
||||
|
||||
#define AWG_OPCODE_OFFSET 10
|
||||
#define AWG_MAX_ARG 0x3ff
|
||||
|
||||
enum opcode {
|
||||
SET,
|
||||
@ -34,6 +35,8 @@ static int awg_generate_instr(enum opcode opcode,
|
||||
/* 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.
|
||||
*
|
||||
* mux_sel is used in case of SAV/EAV synchronization.
|
||||
*/
|
||||
|
||||
while (arg_tmp > 0) {
|
||||
@ -65,7 +68,7 @@ static int awg_generate_instr(enum opcode opcode,
|
||||
|
||||
mux = 0;
|
||||
data_enable = 0;
|
||||
arg &= (0x3ff);
|
||||
arg &= AWG_MAX_ARG;
|
||||
break;
|
||||
case REPEAT:
|
||||
case REPLAY:
|
||||
@ -76,13 +79,13 @@ static int awg_generate_instr(enum opcode opcode,
|
||||
|
||||
mux = 0;
|
||||
data_enable = 0;
|
||||
arg &= (0x3ff);
|
||||
arg &= AWG_MAX_ARG;
|
||||
break;
|
||||
case JUMP:
|
||||
mux = 0;
|
||||
data_enable = 0;
|
||||
arg |= 0x40; /* for jump instruction 7th bit is 1 */
|
||||
arg &= 0x3ff;
|
||||
arg &= AWG_MAX_ARG;
|
||||
break;
|
||||
case STOP:
|
||||
arg = 0;
|
||||
@ -110,68 +113,75 @@ static int awg_generate_instr(enum opcode opcode,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sti_awg_generate_code_data_enable_mode(
|
||||
static int awg_generate_line_signal(
|
||||
struct awg_code_generation_params *fwparams,
|
||||
struct awg_timing *timing)
|
||||
{
|
||||
long int val;
|
||||
long int data_en;
|
||||
int ret = 0;
|
||||
|
||||
if (timing->trailing_pixels > 0) {
|
||||
/* skip trailing pixel */
|
||||
val = timing->blanking_level;
|
||||
ret |= awg_generate_instr(RPLSET, val, 0, 0, fwparams);
|
||||
|
||||
val = timing->trailing_pixels - 1;
|
||||
ret |= awg_generate_instr(SKIP, val, 0, 0, fwparams);
|
||||
}
|
||||
|
||||
/* set DE signal high */
|
||||
val = timing->blanking_level;
|
||||
ret |= awg_generate_instr((timing->trailing_pixels > 0) ? SET : RPLSET,
|
||||
val, 0, 1, fwparams);
|
||||
|
||||
if (timing->blanking_pixels > 0) {
|
||||
/* skip the number of active pixel */
|
||||
val = timing->active_pixels - 1;
|
||||
ret |= awg_generate_instr(SKIP, val, 0, 1, fwparams);
|
||||
|
||||
/* set DE signal low */
|
||||
val = timing->blanking_level;
|
||||
ret |= awg_generate_instr(SET, val, 0, 0, fwparams);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sti_awg_generate_code_data_enable_mode(
|
||||
struct awg_code_generation_params *fwparams,
|
||||
struct awg_timing *timing)
|
||||
{
|
||||
long int val, tmp_val;
|
||||
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);
|
||||
ret |= awg_generate_instr(RPLSET, val, 0, 0, fwparams);
|
||||
|
||||
val = timing->trailing_lines - 1;
|
||||
data_en = 0;
|
||||
ret |= awg_generate_instr(REPLAY, val, 0, data_en, fwparams);
|
||||
ret |= awg_generate_instr(REPLAY, val, 0, 0, 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);
|
||||
tmp_val = timing->active_lines - 1;
|
||||
|
||||
val = timing->trailing_pixels - 1;
|
||||
data_en = 0;
|
||||
ret |= awg_generate_instr(SKIP, val, 0, data_en, fwparams);
|
||||
while (tmp_val > 0) {
|
||||
/* generate DE signal for each line */
|
||||
ret |= awg_generate_line_signal(fwparams, timing);
|
||||
/* replay the sequence as many active lines defined */
|
||||
ret |= awg_generate_instr(REPLAY,
|
||||
min_t(int, AWG_MAX_ARG, tmp_val),
|
||||
0, 0, fwparams);
|
||||
tmp_val -= AWG_MAX_ARG;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
ret |= awg_generate_instr(RPLSET, val, 0, 0, fwparams);
|
||||
|
||||
val = timing->blanking_lines - 1;
|
||||
data_en = 0;
|
||||
ret |= awg_generate_instr(REPLAY, val, 0, data_en, fwparams);
|
||||
ret |= awg_generate_instr(REPLAY, val, 0, 0, fwparams);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -75,13 +75,13 @@ static int sti_compositor_bind(struct device *dev,
|
||||
switch (desc[i].type) {
|
||||
case STI_VID_SUBDEV:
|
||||
compo->vid[vid_id++] =
|
||||
sti_vid_create(compo->dev, desc[i].id,
|
||||
sti_vid_create(compo->dev, drm_dev, desc[i].id,
|
||||
compo->regs + desc[i].offset);
|
||||
break;
|
||||
case STI_MIXER_MAIN_SUBDEV:
|
||||
case STI_MIXER_AUX_SUBDEV:
|
||||
compo->mixer[mixer_id++] =
|
||||
sti_mixer_create(compo->dev, desc[i].id,
|
||||
sti_mixer_create(compo->dev, drm_dev, desc[i].id,
|
||||
compo->regs + desc[i].offset);
|
||||
break;
|
||||
case STI_GPD_SUBDEV:
|
||||
|
@ -51,6 +51,15 @@ static void sti_crtc_disabling(struct drm_crtc *crtc)
|
||||
mixer->status = STI_MIXER_DISABLING;
|
||||
}
|
||||
|
||||
static bool sti_crtc_mode_fixup(struct drm_crtc *crtc,
|
||||
const struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
/* accept the provided drm_display_mode, do not fix it up */
|
||||
drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int
|
||||
sti_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode)
|
||||
{
|
||||
|
@ -5,12 +5,10 @@
|
||||
* for STMicroelectronics.
|
||||
* License terms: GNU General Public License (GPL), version 2
|
||||
*/
|
||||
#include <drm/drmP.h>
|
||||
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_plane_helper.h>
|
||||
|
||||
#include "sti_compositor.h"
|
||||
#include "sti_cursor.h"
|
||||
@ -74,6 +72,82 @@ static const uint32_t cursor_supported_formats[] = {
|
||||
|
||||
#define to_sti_cursor(x) container_of(x, struct sti_cursor, plane)
|
||||
|
||||
#define DBGFS_DUMP(reg) seq_printf(s, "\n %-25s 0x%08X", #reg, \
|
||||
readl(cursor->regs + reg))
|
||||
|
||||
static void cursor_dbg_vpo(struct seq_file *s, u32 val)
|
||||
{
|
||||
seq_printf(s, "\txdo:%4d\tydo:%4d", val & 0x0FFF, (val >> 16) & 0x0FFF);
|
||||
}
|
||||
|
||||
static void cursor_dbg_size(struct seq_file *s, u32 val)
|
||||
{
|
||||
seq_printf(s, "\t%d x %d", val & 0x07FF, (val >> 16) & 0x07FF);
|
||||
}
|
||||
|
||||
static void cursor_dbg_pml(struct seq_file *s,
|
||||
struct sti_cursor *cursor, u32 val)
|
||||
{
|
||||
if (cursor->pixmap.paddr == val)
|
||||
seq_printf(s, "\tVirt @: %p", cursor->pixmap.base);
|
||||
}
|
||||
|
||||
static void cursor_dbg_cml(struct seq_file *s,
|
||||
struct sti_cursor *cursor, u32 val)
|
||||
{
|
||||
if (cursor->clut_paddr == val)
|
||||
seq_printf(s, "\tVirt @: %p", cursor->clut);
|
||||
}
|
||||
|
||||
static int cursor_dbg_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct drm_info_node *node = s->private;
|
||||
struct sti_cursor *cursor = (struct sti_cursor *)node->info_ent->data;
|
||||
struct drm_device *dev = node->minor->dev;
|
||||
int ret;
|
||||
|
||||
ret = mutex_lock_interruptible(&dev->struct_mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
seq_printf(s, "%s: (vaddr = 0x%p)",
|
||||
sti_plane_to_str(&cursor->plane), cursor->regs);
|
||||
|
||||
DBGFS_DUMP(CUR_CTL);
|
||||
DBGFS_DUMP(CUR_VPO);
|
||||
cursor_dbg_vpo(s, readl(cursor->regs + CUR_VPO));
|
||||
DBGFS_DUMP(CUR_PML);
|
||||
cursor_dbg_pml(s, cursor, readl(cursor->regs + CUR_PML));
|
||||
DBGFS_DUMP(CUR_PMP);
|
||||
DBGFS_DUMP(CUR_SIZE);
|
||||
cursor_dbg_size(s, readl(cursor->regs + CUR_SIZE));
|
||||
DBGFS_DUMP(CUR_CML);
|
||||
cursor_dbg_cml(s, cursor, readl(cursor->regs + CUR_CML));
|
||||
DBGFS_DUMP(CUR_AWS);
|
||||
DBGFS_DUMP(CUR_AWE);
|
||||
seq_puts(s, "\n");
|
||||
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct drm_info_list cursor_debugfs_files[] = {
|
||||
{ "cursor", cursor_dbg_show, 0, NULL },
|
||||
};
|
||||
|
||||
static int cursor_debugfs_init(struct sti_cursor *cursor,
|
||||
struct drm_minor *minor)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(cursor_debugfs_files); i++)
|
||||
cursor_debugfs_files[i].data = cursor;
|
||||
|
||||
return drm_debugfs_create_files(cursor_debugfs_files,
|
||||
ARRAY_SIZE(cursor_debugfs_files),
|
||||
minor->debugfs_root, minor);
|
||||
}
|
||||
|
||||
static void sti_cursor_argb8888_to_clut8(struct sti_cursor *cursor, u32 *src)
|
||||
{
|
||||
u8 *dst = cursor->pixmap.base;
|
||||
@ -110,35 +184,31 @@ static void sti_cursor_init(struct sti_cursor *cursor)
|
||||
(b * 5);
|
||||
}
|
||||
|
||||
static void sti_cursor_atomic_update(struct drm_plane *drm_plane,
|
||||
struct drm_plane_state *oldstate)
|
||||
static int sti_cursor_atomic_check(struct drm_plane *drm_plane,
|
||||
struct drm_plane_state *state)
|
||||
{
|
||||
struct drm_plane_state *state = drm_plane->state;
|
||||
struct sti_plane *plane = to_sti_plane(drm_plane);
|
||||
struct sti_cursor *cursor = to_sti_cursor(plane);
|
||||
struct drm_crtc *crtc = state->crtc;
|
||||
struct sti_mixer *mixer = to_sti_mixer(crtc);
|
||||
struct drm_framebuffer *fb = state->fb;
|
||||
struct drm_display_mode *mode = &crtc->mode;
|
||||
int dst_x = state->crtc_x;
|
||||
int dst_y = state->crtc_y;
|
||||
int dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x);
|
||||
int dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y);
|
||||
struct drm_crtc_state *crtc_state;
|
||||
struct drm_display_mode *mode;
|
||||
int dst_x, dst_y, dst_w, dst_h;
|
||||
int src_w, src_h;
|
||||
|
||||
/* no need for further checks if the plane is being disabled */
|
||||
if (!crtc || !fb)
|
||||
return 0;
|
||||
|
||||
crtc_state = drm_atomic_get_crtc_state(state->state, crtc);
|
||||
mode = &crtc_state->mode;
|
||||
dst_x = state->crtc_x;
|
||||
dst_y = state->crtc_y;
|
||||
dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x);
|
||||
dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y);
|
||||
/* src_x are in 16.16 format */
|
||||
int src_w = state->src_w >> 16;
|
||||
int src_h = state->src_h >> 16;
|
||||
bool first_prepare = plane->status == STI_PLANE_DISABLED ? true : false;
|
||||
struct drm_gem_cma_object *cma_obj;
|
||||
u32 y, x;
|
||||
u32 val;
|
||||
|
||||
DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s)\n",
|
||||
crtc->base.id, sti_mixer_to_str(mixer),
|
||||
drm_plane->base.id, sti_plane_to_str(plane));
|
||||
DRM_DEBUG_KMS("(%dx%d)@(%d,%d)\n", dst_w, dst_h, dst_x, dst_y);
|
||||
|
||||
dev_dbg(cursor->dev, "%s %s\n", __func__,
|
||||
sti_plane_to_str(plane));
|
||||
src_w = state->src_w >> 16;
|
||||
src_h = state->src_h >> 16;
|
||||
|
||||
if (src_w < STI_CURS_MIN_SIZE ||
|
||||
src_h < STI_CURS_MIN_SIZE ||
|
||||
@ -146,7 +216,7 @@ static void sti_cursor_atomic_update(struct drm_plane *drm_plane,
|
||||
src_h > STI_CURS_MAX_SIZE) {
|
||||
DRM_ERROR("Invalid cursor size (%dx%d)\n",
|
||||
src_w, src_h);
|
||||
return;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* If the cursor size has changed, re-allocated the pixmap */
|
||||
@ -170,16 +240,46 @@ static void sti_cursor_atomic_update(struct drm_plane *drm_plane,
|
||||
GFP_KERNEL | GFP_DMA);
|
||||
if (!cursor->pixmap.base) {
|
||||
DRM_ERROR("Failed to allocate memory for pixmap\n");
|
||||
return;
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
|
||||
if (!cma_obj) {
|
||||
if (!drm_fb_cma_get_gem_obj(fb, 0)) {
|
||||
DRM_ERROR("Can't get CMA GEM object for fb\n");
|
||||
return;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s)\n",
|
||||
crtc->base.id, sti_mixer_to_str(to_sti_mixer(crtc)),
|
||||
drm_plane->base.id, sti_plane_to_str(plane));
|
||||
DRM_DEBUG_KMS("(%dx%d)@(%d,%d)\n", dst_w, dst_h, dst_x, dst_y);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sti_cursor_atomic_update(struct drm_plane *drm_plane,
|
||||
struct drm_plane_state *oldstate)
|
||||
{
|
||||
struct drm_plane_state *state = drm_plane->state;
|
||||
struct sti_plane *plane = to_sti_plane(drm_plane);
|
||||
struct sti_cursor *cursor = to_sti_cursor(plane);
|
||||
struct drm_crtc *crtc = state->crtc;
|
||||
struct drm_framebuffer *fb = state->fb;
|
||||
struct drm_display_mode *mode;
|
||||
int dst_x, dst_y;
|
||||
struct drm_gem_cma_object *cma_obj;
|
||||
u32 y, x;
|
||||
u32 val;
|
||||
|
||||
if (!crtc || !fb)
|
||||
return;
|
||||
|
||||
mode = &crtc->mode;
|
||||
dst_x = state->crtc_x;
|
||||
dst_y = state->crtc_y;
|
||||
|
||||
cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
|
||||
|
||||
/* Convert ARGB8888 to CLUT8 */
|
||||
sti_cursor_argb8888_to_clut8(cursor, (u32 *)cma_obj->vaddr);
|
||||
|
||||
@ -193,21 +293,21 @@ static void sti_cursor_atomic_update(struct drm_plane *drm_plane,
|
||||
val = y << 16 | x;
|
||||
writel(val, cursor->regs + CUR_AWE);
|
||||
|
||||
if (first_prepare) {
|
||||
/* Set and fetch CLUT */
|
||||
writel(cursor->clut_paddr, cursor->regs + CUR_CML);
|
||||
writel(CUR_CTL_CLUT_UPDATE, cursor->regs + CUR_CTL);
|
||||
}
|
||||
|
||||
/* Set memory location, size, and position */
|
||||
writel(cursor->pixmap.paddr, cursor->regs + CUR_PML);
|
||||
writel(cursor->width, cursor->regs + CUR_PMP);
|
||||
writel(cursor->height << 16 | cursor->width, cursor->regs + CUR_SIZE);
|
||||
|
||||
y = sti_vtg_get_line_number(*mode, dst_y);
|
||||
x = sti_vtg_get_pixel_number(*mode, dst_y);
|
||||
x = sti_vtg_get_pixel_number(*mode, dst_x);
|
||||
writel((y << 16) | x, cursor->regs + CUR_VPO);
|
||||
|
||||
/* Set and fetch CLUT */
|
||||
writel(cursor->clut_paddr, cursor->regs + CUR_CML);
|
||||
writel(CUR_CTL_CLUT_UPDATE, cursor->regs + CUR_CTL);
|
||||
|
||||
sti_plane_update_fps(plane, true, false);
|
||||
|
||||
plane->status = STI_PLANE_UPDATED;
|
||||
}
|
||||
|
||||
@ -215,7 +315,6 @@ static void sti_cursor_atomic_disable(struct drm_plane *drm_plane,
|
||||
struct drm_plane_state *oldstate)
|
||||
{
|
||||
struct sti_plane *plane = to_sti_plane(drm_plane);
|
||||
struct sti_mixer *mixer = to_sti_mixer(drm_plane->crtc);
|
||||
|
||||
if (!drm_plane->crtc) {
|
||||
DRM_DEBUG_DRIVER("drm plane:%d not enabled\n",
|
||||
@ -224,13 +323,15 @@ static void sti_cursor_atomic_disable(struct drm_plane *drm_plane,
|
||||
}
|
||||
|
||||
DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n",
|
||||
drm_plane->crtc->base.id, sti_mixer_to_str(mixer),
|
||||
drm_plane->crtc->base.id,
|
||||
sti_mixer_to_str(to_sti_mixer(drm_plane->crtc)),
|
||||
drm_plane->base.id, sti_plane_to_str(plane));
|
||||
|
||||
plane->status = STI_PLANE_DISABLING;
|
||||
}
|
||||
|
||||
static const struct drm_plane_helper_funcs sti_cursor_helpers_funcs = {
|
||||
.atomic_check = sti_cursor_atomic_check,
|
||||
.atomic_update = sti_cursor_atomic_update,
|
||||
.atomic_disable = sti_cursor_atomic_disable,
|
||||
};
|
||||
@ -283,6 +384,9 @@ struct drm_plane *sti_cursor_create(struct drm_device *drm_dev,
|
||||
|
||||
sti_plane_init_property(&cursor->plane, DRM_PLANE_TYPE_CURSOR);
|
||||
|
||||
if (cursor_debugfs_init(cursor, drm_dev->primary))
|
||||
DRM_ERROR("CURSOR debugfs setup failed\n");
|
||||
|
||||
return &cursor->plane.drm_plane;
|
||||
|
||||
err_plane:
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
#include "sti_crtc.h"
|
||||
#include "sti_drv.h"
|
||||
#include "sti_plane.h"
|
||||
|
||||
#define DRIVER_NAME "sti"
|
||||
#define DRIVER_DESC "STMicroelectronics SoC DRM"
|
||||
@ -30,6 +31,130 @@
|
||||
#define STI_MAX_FB_HEIGHT 4096
|
||||
#define STI_MAX_FB_WIDTH 4096
|
||||
|
||||
static int sti_drm_fps_get(void *data, u64 *val)
|
||||
{
|
||||
struct drm_device *drm_dev = data;
|
||||
struct drm_plane *p;
|
||||
unsigned int i = 0;
|
||||
|
||||
*val = 0;
|
||||
list_for_each_entry(p, &drm_dev->mode_config.plane_list, head) {
|
||||
struct sti_plane *plane = to_sti_plane(p);
|
||||
|
||||
*val |= plane->fps_info.output << i;
|
||||
i++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sti_drm_fps_set(void *data, u64 val)
|
||||
{
|
||||
struct drm_device *drm_dev = data;
|
||||
struct drm_plane *p;
|
||||
unsigned int i = 0;
|
||||
|
||||
list_for_each_entry(p, &drm_dev->mode_config.plane_list, head) {
|
||||
struct sti_plane *plane = to_sti_plane(p);
|
||||
|
||||
plane->fps_info.output = (val >> i) & 1;
|
||||
i++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_SIMPLE_ATTRIBUTE(sti_drm_fps_fops,
|
||||
sti_drm_fps_get, sti_drm_fps_set, "%llu\n");
|
||||
|
||||
static int sti_drm_fps_dbg_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct drm_info_node *node = s->private;
|
||||
struct drm_device *dev = node->minor->dev;
|
||||
struct drm_plane *p;
|
||||
int ret;
|
||||
|
||||
ret = mutex_lock_interruptible(&dev->struct_mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
list_for_each_entry(p, &dev->mode_config.plane_list, head) {
|
||||
struct sti_plane *plane = to_sti_plane(p);
|
||||
|
||||
seq_printf(s, "%s%s\n",
|
||||
plane->fps_info.fps_str,
|
||||
plane->fps_info.fips_str);
|
||||
}
|
||||
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct drm_info_list sti_drm_dbg_list[] = {
|
||||
{"fps_get", sti_drm_fps_dbg_show, 0},
|
||||
};
|
||||
|
||||
static int sti_drm_debugfs_create(struct dentry *root,
|
||||
struct drm_minor *minor,
|
||||
const char *name,
|
||||
const struct file_operations *fops)
|
||||
{
|
||||
struct drm_device *dev = minor->dev;
|
||||
struct drm_info_node *node;
|
||||
struct dentry *ent;
|
||||
|
||||
ent = debugfs_create_file(name, S_IRUGO | S_IWUSR, root, dev, fops);
|
||||
if (IS_ERR(ent))
|
||||
return PTR_ERR(ent);
|
||||
|
||||
node = kmalloc(sizeof(*node), GFP_KERNEL);
|
||||
if (!node) {
|
||||
debugfs_remove(ent);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
node->minor = minor;
|
||||
node->dent = ent;
|
||||
node->info_ent = (void *)fops;
|
||||
|
||||
mutex_lock(&minor->debugfs_lock);
|
||||
list_add(&node->list, &minor->debugfs_list);
|
||||
mutex_unlock(&minor->debugfs_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sti_drm_dbg_init(struct drm_minor *minor)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = drm_debugfs_create_files(sti_drm_dbg_list,
|
||||
ARRAY_SIZE(sti_drm_dbg_list),
|
||||
minor->debugfs_root, minor);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = sti_drm_debugfs_create(minor->debugfs_root, minor, "fps_show",
|
||||
&sti_drm_fps_fops);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
DRM_INFO("%s: debugfs installed\n", DRIVER_NAME);
|
||||
return 0;
|
||||
err:
|
||||
DRM_ERROR("%s: cannot install debugfs\n", DRIVER_NAME);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void sti_drm_dbg_cleanup(struct drm_minor *minor)
|
||||
{
|
||||
drm_debugfs_remove_files(sti_drm_dbg_list,
|
||||
ARRAY_SIZE(sti_drm_dbg_list), minor);
|
||||
|
||||
drm_debugfs_remove_files((struct drm_info_list *)&sti_drm_fps_fops,
|
||||
1, minor);
|
||||
}
|
||||
|
||||
static void sti_atomic_schedule(struct sti_private *private,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
@ -181,18 +306,9 @@ static const struct file_operations sti_driver_fops = {
|
||||
.release = drm_release,
|
||||
};
|
||||
|
||||
static struct dma_buf *sti_gem_prime_export(struct drm_device *dev,
|
||||
struct drm_gem_object *obj,
|
||||
int flags)
|
||||
{
|
||||
/* we want to be able to write in mmapped buffer */
|
||||
flags |= O_RDWR;
|
||||
return drm_gem_prime_export(dev, obj, flags);
|
||||
}
|
||||
|
||||
static struct drm_driver sti_driver = {
|
||||
.driver_features = DRIVER_HAVE_IRQ | DRIVER_MODESET |
|
||||
DRIVER_GEM | DRIVER_PRIME,
|
||||
DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC,
|
||||
.load = sti_load,
|
||||
.gem_free_object = drm_gem_cma_free_object,
|
||||
.gem_vm_ops = &drm_gem_cma_vm_ops,
|
||||
@ -207,7 +323,7 @@ static struct drm_driver sti_driver = {
|
||||
|
||||
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
|
||||
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
|
||||
.gem_prime_export = sti_gem_prime_export,
|
||||
.gem_prime_export = drm_gem_prime_export,
|
||||
.gem_prime_import = drm_gem_prime_import,
|
||||
.gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
|
||||
.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
|
||||
@ -215,6 +331,9 @@ static struct drm_driver sti_driver = {
|
||||
.gem_prime_vunmap = drm_gem_cma_prime_vunmap,
|
||||
.gem_prime_mmap = drm_gem_cma_prime_mmap,
|
||||
|
||||
.debugfs_init = sti_drm_dbg_init,
|
||||
.debugfs_cleanup = sti_drm_dbg_cleanup,
|
||||
|
||||
.name = DRIVER_NAME,
|
||||
.desc = DRIVER_DESC,
|
||||
.date = DRIVER_DATE,
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/component.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/platform_device.h>
|
||||
@ -156,6 +157,69 @@ static void dvo_awg_configure(struct sti_dvo *dvo, u32 *awg_ram_code, int nb)
|
||||
writel(DVO_AWG_CTRL_EN, dvo->regs + DVO_AWG_DIGSYNC_CTRL);
|
||||
}
|
||||
|
||||
#define DBGFS_DUMP(reg) seq_printf(s, "\n %-25s 0x%08X", #reg, \
|
||||
readl(dvo->regs + reg))
|
||||
|
||||
static void dvo_dbg_awg_microcode(struct seq_file *s, void __iomem *reg)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
seq_puts(s, "\n\n");
|
||||
seq_puts(s, " DVO AWG microcode:");
|
||||
for (i = 0; i < AWG_MAX_INST; i++) {
|
||||
if (i % 8 == 0)
|
||||
seq_printf(s, "\n %04X:", i);
|
||||
seq_printf(s, " %04X", readl(reg + i * 4));
|
||||
}
|
||||
}
|
||||
|
||||
static int dvo_dbg_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct drm_info_node *node = s->private;
|
||||
struct sti_dvo *dvo = (struct sti_dvo *)node->info_ent->data;
|
||||
struct drm_device *dev = node->minor->dev;
|
||||
int ret;
|
||||
|
||||
ret = mutex_lock_interruptible(&dev->struct_mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
seq_printf(s, "DVO: (vaddr = 0x%p)", dvo->regs);
|
||||
DBGFS_DUMP(DVO_AWG_DIGSYNC_CTRL);
|
||||
DBGFS_DUMP(DVO_DOF_CFG);
|
||||
DBGFS_DUMP(DVO_LUT_PROG_LOW);
|
||||
DBGFS_DUMP(DVO_LUT_PROG_MID);
|
||||
DBGFS_DUMP(DVO_LUT_PROG_HIGH);
|
||||
dvo_dbg_awg_microcode(s, dvo->regs + DVO_DIGSYNC_INSTR_I);
|
||||
seq_puts(s, "\n");
|
||||
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct drm_info_list dvo_debugfs_files[] = {
|
||||
{ "dvo", dvo_dbg_show, 0, NULL },
|
||||
};
|
||||
|
||||
static void dvo_debugfs_exit(struct sti_dvo *dvo, struct drm_minor *minor)
|
||||
{
|
||||
drm_debugfs_remove_files(dvo_debugfs_files,
|
||||
ARRAY_SIZE(dvo_debugfs_files),
|
||||
minor);
|
||||
}
|
||||
|
||||
static int dvo_debugfs_init(struct sti_dvo *dvo, struct drm_minor *minor)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dvo_debugfs_files); i++)
|
||||
dvo_debugfs_files[i].data = dvo;
|
||||
|
||||
return drm_debugfs_create_files(dvo_debugfs_files,
|
||||
ARRAY_SIZE(dvo_debugfs_files),
|
||||
minor->debugfs_root, minor);
|
||||
}
|
||||
|
||||
static void sti_dvo_disable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct sti_dvo *dvo = bridge->driver_private;
|
||||
@ -345,12 +409,14 @@ sti_dvo_connector_detect(struct drm_connector *connector, bool force)
|
||||
|
||||
DRM_DEBUG_DRIVER("\n");
|
||||
|
||||
if (!dvo->panel)
|
||||
if (!dvo->panel) {
|
||||
dvo->panel = of_drm_find_panel(dvo->panel_node);
|
||||
if (dvo->panel)
|
||||
drm_panel_attach(dvo->panel, connector);
|
||||
}
|
||||
|
||||
if (dvo->panel)
|
||||
if (!drm_panel_attach(dvo->panel, connector))
|
||||
return connector_status_connected;
|
||||
return connector_status_connected;
|
||||
|
||||
return connector_status_disconnected;
|
||||
}
|
||||
@ -453,6 +519,9 @@ static int sti_dvo_bind(struct device *dev, struct device *master, void *data)
|
||||
goto err_sysfs;
|
||||
}
|
||||
|
||||
if (dvo_debugfs_init(dvo, drm_dev->primary))
|
||||
DRM_ERROR("DVO debugfs setup failed\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err_sysfs:
|
||||
@ -467,6 +536,9 @@ static void sti_dvo_unbind(struct device *dev,
|
||||
struct device *master, void *data)
|
||||
{
|
||||
struct sti_dvo *dvo = dev_get_drvdata(dev);
|
||||
struct drm_device *drm_dev = data;
|
||||
|
||||
dvo_debugfs_exit(dvo, drm_dev->primary);
|
||||
|
||||
drm_bridge_remove(dvo->bridge);
|
||||
}
|
||||
|
@ -6,9 +6,7 @@
|
||||
* License terms: GNU General Public License (GPL), version 2
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
|
||||
@ -32,10 +30,23 @@
|
||||
#define GDP_ABGR8888 (GDP_ARGB8888 | BIGNOTLITTLE | ALPHASWITCH)
|
||||
#define GDP_ARGB1555 0x06
|
||||
#define GDP_ARGB4444 0x07
|
||||
#define GDP_CLUT8 0x0B
|
||||
#define GDP_YCBR888 0x10
|
||||
#define GDP_YCBR422R 0x12
|
||||
#define GDP_AYCBR8888 0x15
|
||||
|
||||
#define GDP2STR(fmt) { GDP_ ## fmt, #fmt }
|
||||
|
||||
static struct gdp_format_to_str {
|
||||
int format;
|
||||
char name[20];
|
||||
} gdp_format_to_str[] = {
|
||||
GDP2STR(RGB565),
|
||||
GDP2STR(RGB888),
|
||||
GDP2STR(RGB888_32),
|
||||
GDP2STR(XBGR8888),
|
||||
GDP2STR(ARGB8565),
|
||||
GDP2STR(ARGB8888),
|
||||
GDP2STR(ABGR8888),
|
||||
GDP2STR(ARGB1555),
|
||||
GDP2STR(ARGB4444)
|
||||
};
|
||||
|
||||
#define GAM_GDP_CTL_OFFSET 0x00
|
||||
#define GAM_GDP_AGC_OFFSET 0x04
|
||||
@ -97,6 +108,7 @@ struct sti_gdp_node_list {
|
||||
* @vtg_field_nb: callback for VTG FIELD (top or bottom) notification
|
||||
* @is_curr_top: true if the current node processed is the top field
|
||||
* @node_list: array of node list
|
||||
* @vtg: registered vtg
|
||||
*/
|
||||
struct sti_gdp {
|
||||
struct sti_plane plane;
|
||||
@ -108,6 +120,7 @@ struct sti_gdp {
|
||||
struct notifier_block vtg_field_nb;
|
||||
bool is_curr_top;
|
||||
struct sti_gdp_node_list node_list[GDP_NODE_NB_BANK];
|
||||
struct sti_vtg *vtg;
|
||||
};
|
||||
|
||||
#define to_sti_gdp(x) container_of(x, struct sti_gdp, plane)
|
||||
@ -121,12 +134,224 @@ static const uint32_t gdp_supported_formats[] = {
|
||||
DRM_FORMAT_ARGB1555,
|
||||
DRM_FORMAT_RGB565,
|
||||
DRM_FORMAT_RGB888,
|
||||
DRM_FORMAT_AYUV,
|
||||
DRM_FORMAT_YUV444,
|
||||
DRM_FORMAT_VYUY,
|
||||
DRM_FORMAT_C8,
|
||||
};
|
||||
|
||||
#define DBGFS_DUMP(reg) seq_printf(s, "\n %-25s 0x%08X", #reg, \
|
||||
readl(gdp->regs + reg ## _OFFSET))
|
||||
|
||||
static void gdp_dbg_ctl(struct seq_file *s, int val)
|
||||
{
|
||||
int i;
|
||||
|
||||
seq_puts(s, "\tColor:");
|
||||
for (i = 0; i < ARRAY_SIZE(gdp_format_to_str); i++) {
|
||||
if (gdp_format_to_str[i].format == (val & 0x1F)) {
|
||||
seq_printf(s, gdp_format_to_str[i].name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == ARRAY_SIZE(gdp_format_to_str))
|
||||
seq_puts(s, "<UNKNOWN>");
|
||||
|
||||
seq_printf(s, "\tWaitNextVsync:%d", val & WAIT_NEXT_VSYNC ? 1 : 0);
|
||||
}
|
||||
|
||||
static void gdp_dbg_vpo(struct seq_file *s, int val)
|
||||
{
|
||||
seq_printf(s, "\txdo:%4d\tydo:%4d", val & 0xFFFF, (val >> 16) & 0xFFFF);
|
||||
}
|
||||
|
||||
static void gdp_dbg_vps(struct seq_file *s, int val)
|
||||
{
|
||||
seq_printf(s, "\txds:%4d\tyds:%4d", val & 0xFFFF, (val >> 16) & 0xFFFF);
|
||||
}
|
||||
|
||||
static void gdp_dbg_size(struct seq_file *s, int val)
|
||||
{
|
||||
seq_printf(s, "\t%d x %d", val & 0xFFFF, (val >> 16) & 0xFFFF);
|
||||
}
|
||||
|
||||
static void gdp_dbg_nvn(struct seq_file *s, struct sti_gdp *gdp, int val)
|
||||
{
|
||||
void *base = NULL;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < GDP_NODE_NB_BANK; i++) {
|
||||
if (gdp->node_list[i].top_field_paddr == val) {
|
||||
base = gdp->node_list[i].top_field;
|
||||
break;
|
||||
}
|
||||
if (gdp->node_list[i].btm_field_paddr == val) {
|
||||
base = gdp->node_list[i].btm_field;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (base)
|
||||
seq_printf(s, "\tVirt @: %p", base);
|
||||
}
|
||||
|
||||
static void gdp_dbg_ppt(struct seq_file *s, int val)
|
||||
{
|
||||
if (val & GAM_GDP_PPT_IGNORE)
|
||||
seq_puts(s, "\tNot displayed on mixer!");
|
||||
}
|
||||
|
||||
static void gdp_dbg_mst(struct seq_file *s, int val)
|
||||
{
|
||||
if (val & 1)
|
||||
seq_puts(s, "\tBUFFER UNDERFLOW!");
|
||||
}
|
||||
|
||||
static int gdp_dbg_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct drm_info_node *node = s->private;
|
||||
struct sti_gdp *gdp = (struct sti_gdp *)node->info_ent->data;
|
||||
struct drm_device *dev = node->minor->dev;
|
||||
struct drm_plane *drm_plane = &gdp->plane.drm_plane;
|
||||
struct drm_crtc *crtc = drm_plane->crtc;
|
||||
int ret;
|
||||
|
||||
ret = mutex_lock_interruptible(&dev->struct_mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
seq_printf(s, "%s: (vaddr = 0x%p)",
|
||||
sti_plane_to_str(&gdp->plane), gdp->regs);
|
||||
|
||||
DBGFS_DUMP(GAM_GDP_CTL);
|
||||
gdp_dbg_ctl(s, readl(gdp->regs + GAM_GDP_CTL_OFFSET));
|
||||
DBGFS_DUMP(GAM_GDP_AGC);
|
||||
DBGFS_DUMP(GAM_GDP_VPO);
|
||||
gdp_dbg_vpo(s, readl(gdp->regs + GAM_GDP_VPO_OFFSET));
|
||||
DBGFS_DUMP(GAM_GDP_VPS);
|
||||
gdp_dbg_vps(s, readl(gdp->regs + GAM_GDP_VPS_OFFSET));
|
||||
DBGFS_DUMP(GAM_GDP_PML);
|
||||
DBGFS_DUMP(GAM_GDP_PMP);
|
||||
DBGFS_DUMP(GAM_GDP_SIZE);
|
||||
gdp_dbg_size(s, readl(gdp->regs + GAM_GDP_SIZE_OFFSET));
|
||||
DBGFS_DUMP(GAM_GDP_NVN);
|
||||
gdp_dbg_nvn(s, gdp, readl(gdp->regs + GAM_GDP_NVN_OFFSET));
|
||||
DBGFS_DUMP(GAM_GDP_KEY1);
|
||||
DBGFS_DUMP(GAM_GDP_KEY2);
|
||||
DBGFS_DUMP(GAM_GDP_PPT);
|
||||
gdp_dbg_ppt(s, readl(gdp->regs + GAM_GDP_PPT_OFFSET));
|
||||
DBGFS_DUMP(GAM_GDP_CML);
|
||||
DBGFS_DUMP(GAM_GDP_MST);
|
||||
gdp_dbg_mst(s, readl(gdp->regs + GAM_GDP_MST_OFFSET));
|
||||
|
||||
seq_puts(s, "\n\n");
|
||||
if (!crtc)
|
||||
seq_puts(s, " Not connected to any DRM CRTC\n");
|
||||
else
|
||||
seq_printf(s, " Connected to DRM CRTC #%d (%s)\n",
|
||||
crtc->base.id, sti_mixer_to_str(to_sti_mixer(crtc)));
|
||||
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gdp_node_dump_node(struct seq_file *s, struct sti_gdp_node *node)
|
||||
{
|
||||
seq_printf(s, "\t@:0x%p", node);
|
||||
seq_printf(s, "\n\tCTL 0x%08X", node->gam_gdp_ctl);
|
||||
gdp_dbg_ctl(s, node->gam_gdp_ctl);
|
||||
seq_printf(s, "\n\tAGC 0x%08X", node->gam_gdp_agc);
|
||||
seq_printf(s, "\n\tVPO 0x%08X", node->gam_gdp_vpo);
|
||||
gdp_dbg_vpo(s, node->gam_gdp_vpo);
|
||||
seq_printf(s, "\n\tVPS 0x%08X", node->gam_gdp_vps);
|
||||
gdp_dbg_vps(s, node->gam_gdp_vps);
|
||||
seq_printf(s, "\n\tPML 0x%08X", node->gam_gdp_pml);
|
||||
seq_printf(s, "\n\tPMP 0x%08X", node->gam_gdp_pmp);
|
||||
seq_printf(s, "\n\tSIZE 0x%08X", node->gam_gdp_size);
|
||||
gdp_dbg_size(s, node->gam_gdp_size);
|
||||
seq_printf(s, "\n\tNVN 0x%08X", node->gam_gdp_nvn);
|
||||
seq_printf(s, "\n\tKEY1 0x%08X", node->gam_gdp_key1);
|
||||
seq_printf(s, "\n\tKEY2 0x%08X", node->gam_gdp_key2);
|
||||
seq_printf(s, "\n\tPPT 0x%08X", node->gam_gdp_ppt);
|
||||
gdp_dbg_ppt(s, node->gam_gdp_ppt);
|
||||
seq_printf(s, "\n\tCML 0x%08X", node->gam_gdp_cml);
|
||||
seq_puts(s, "\n");
|
||||
}
|
||||
|
||||
static int gdp_node_dbg_show(struct seq_file *s, void *arg)
|
||||
{
|
||||
struct drm_info_node *node = s->private;
|
||||
struct sti_gdp *gdp = (struct sti_gdp *)node->info_ent->data;
|
||||
struct drm_device *dev = node->minor->dev;
|
||||
unsigned int b;
|
||||
int ret;
|
||||
|
||||
ret = mutex_lock_interruptible(&dev->struct_mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (b = 0; b < GDP_NODE_NB_BANK; b++) {
|
||||
seq_printf(s, "\n%s[%d].top", sti_plane_to_str(&gdp->plane), b);
|
||||
gdp_node_dump_node(s, gdp->node_list[b].top_field);
|
||||
seq_printf(s, "\n%s[%d].btm", sti_plane_to_str(&gdp->plane), b);
|
||||
gdp_node_dump_node(s, gdp->node_list[b].btm_field);
|
||||
}
|
||||
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct drm_info_list gdp0_debugfs_files[] = {
|
||||
{ "gdp0", gdp_dbg_show, 0, NULL },
|
||||
{ "gdp0_node", gdp_node_dbg_show, 0, NULL },
|
||||
};
|
||||
|
||||
static struct drm_info_list gdp1_debugfs_files[] = {
|
||||
{ "gdp1", gdp_dbg_show, 0, NULL },
|
||||
{ "gdp1_node", gdp_node_dbg_show, 0, NULL },
|
||||
};
|
||||
|
||||
static struct drm_info_list gdp2_debugfs_files[] = {
|
||||
{ "gdp2", gdp_dbg_show, 0, NULL },
|
||||
{ "gdp2_node", gdp_node_dbg_show, 0, NULL },
|
||||
};
|
||||
|
||||
static struct drm_info_list gdp3_debugfs_files[] = {
|
||||
{ "gdp3", gdp_dbg_show, 0, NULL },
|
||||
{ "gdp3_node", gdp_node_dbg_show, 0, NULL },
|
||||
};
|
||||
|
||||
static int gdp_debugfs_init(struct sti_gdp *gdp, struct drm_minor *minor)
|
||||
{
|
||||
unsigned int i;
|
||||
struct drm_info_list *gdp_debugfs_files;
|
||||
int nb_files;
|
||||
|
||||
switch (gdp->plane.desc) {
|
||||
case STI_GDP_0:
|
||||
gdp_debugfs_files = gdp0_debugfs_files;
|
||||
nb_files = ARRAY_SIZE(gdp0_debugfs_files);
|
||||
break;
|
||||
case STI_GDP_1:
|
||||
gdp_debugfs_files = gdp1_debugfs_files;
|
||||
nb_files = ARRAY_SIZE(gdp1_debugfs_files);
|
||||
break;
|
||||
case STI_GDP_2:
|
||||
gdp_debugfs_files = gdp2_debugfs_files;
|
||||
nb_files = ARRAY_SIZE(gdp2_debugfs_files);
|
||||
break;
|
||||
case STI_GDP_3:
|
||||
gdp_debugfs_files = gdp3_debugfs_files;
|
||||
nb_files = ARRAY_SIZE(gdp3_debugfs_files);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < nb_files; i++)
|
||||
gdp_debugfs_files[i].data = gdp;
|
||||
|
||||
return drm_debugfs_create_files(gdp_debugfs_files,
|
||||
nb_files,
|
||||
minor->debugfs_root, minor);
|
||||
}
|
||||
|
||||
static int sti_gdp_fourcc2format(int fourcc)
|
||||
{
|
||||
switch (fourcc) {
|
||||
@ -146,14 +371,6 @@ static int sti_gdp_fourcc2format(int fourcc)
|
||||
return GDP_RGB565;
|
||||
case DRM_FORMAT_RGB888:
|
||||
return GDP_RGB888;
|
||||
case DRM_FORMAT_AYUV:
|
||||
return GDP_AYCBR8888;
|
||||
case DRM_FORMAT_YUV444:
|
||||
return GDP_YCBR888;
|
||||
case DRM_FORMAT_VYUY:
|
||||
return GDP_YCBR422R;
|
||||
case DRM_FORMAT_C8:
|
||||
return GDP_CLUT8;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@ -163,7 +380,6 @@ static int sti_gdp_get_alpharange(int format)
|
||||
switch (format) {
|
||||
case GDP_ARGB8565:
|
||||
case GDP_ARGB8888:
|
||||
case GDP_AYCBR8888:
|
||||
case GDP_ABGR8888:
|
||||
return GAM_GDP_ALPHARANGE_255;
|
||||
}
|
||||
@ -240,9 +456,6 @@ end:
|
||||
*/
|
||||
static void sti_gdp_disable(struct sti_gdp *gdp)
|
||||
{
|
||||
struct drm_plane *drm_plane = &gdp->plane.drm_plane;
|
||||
struct sti_mixer *mixer = to_sti_mixer(drm_plane->crtc);
|
||||
struct sti_compositor *compo = dev_get_drvdata(gdp->dev);
|
||||
unsigned int i;
|
||||
|
||||
DRM_DEBUG_DRIVER("%s\n", sti_plane_to_str(&gdp->plane));
|
||||
@ -253,8 +466,7 @@ static void sti_gdp_disable(struct sti_gdp *gdp)
|
||||
gdp->node_list[i].btm_field->gam_gdp_ppt |= GAM_GDP_PPT_IGNORE;
|
||||
}
|
||||
|
||||
if (sti_vtg_unregister_client(mixer->id == STI_MIXER_MAIN ?
|
||||
compo->vtg_main : compo->vtg_aux, &gdp->vtg_field_nb))
|
||||
if (sti_vtg_unregister_client(gdp->vtg, &gdp->vtg_field_nb))
|
||||
DRM_DEBUG_DRIVER("Warning: cannot unregister VTG notifier\n");
|
||||
|
||||
if (gdp->clk_pix)
|
||||
@ -380,37 +592,54 @@ static void sti_gdp_init(struct sti_gdp *gdp)
|
||||
}
|
||||
}
|
||||
|
||||
static void sti_gdp_atomic_update(struct drm_plane *drm_plane,
|
||||
struct drm_plane_state *oldstate)
|
||||
/**
|
||||
* sti_gdp_get_dst
|
||||
* @dev: device
|
||||
* @dst: requested destination size
|
||||
* @src: source size
|
||||
*
|
||||
* Return the cropped / clamped destination size
|
||||
*
|
||||
* RETURNS:
|
||||
* cropped / clamped destination size
|
||||
*/
|
||||
static int sti_gdp_get_dst(struct device *dev, int dst, int src)
|
||||
{
|
||||
if (dst == src)
|
||||
return dst;
|
||||
|
||||
if (dst < src) {
|
||||
dev_dbg(dev, "WARNING: GDP scale not supported, will crop\n");
|
||||
return dst;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "WARNING: GDP scale not supported, will clamp\n");
|
||||
return src;
|
||||
}
|
||||
|
||||
static int sti_gdp_atomic_check(struct drm_plane *drm_plane,
|
||||
struct drm_plane_state *state)
|
||||
{
|
||||
struct drm_plane_state *state = drm_plane->state;
|
||||
struct sti_plane *plane = to_sti_plane(drm_plane);
|
||||
struct sti_gdp *gdp = to_sti_gdp(plane);
|
||||
struct drm_crtc *crtc = state->crtc;
|
||||
struct sti_compositor *compo = dev_get_drvdata(gdp->dev);
|
||||
struct drm_framebuffer *fb = state->fb;
|
||||
bool first_prepare = plane->status == STI_PLANE_DISABLED ? true : false;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
struct sti_mixer *mixer;
|
||||
struct drm_display_mode *mode;
|
||||
int dst_x, dst_y, dst_w, dst_h;
|
||||
int src_x, src_y, src_w, src_h;
|
||||
struct drm_gem_cma_object *cma_obj;
|
||||
struct sti_gdp_node_list *list;
|
||||
struct sti_gdp_node_list *curr_list;
|
||||
struct sti_gdp_node *top_field, *btm_field;
|
||||
u32 dma_updated_top;
|
||||
u32 dma_updated_btm;
|
||||
int format;
|
||||
unsigned int depth, bpp;
|
||||
u32 ydo, xdo, yds, xds;
|
||||
int res;
|
||||
|
||||
/* Manage the case where crtc is null (disabled) */
|
||||
if (!crtc)
|
||||
return;
|
||||
/* no need for further checks if the plane is being disabled */
|
||||
if (!crtc || !fb)
|
||||
return 0;
|
||||
|
||||
mixer = to_sti_mixer(crtc);
|
||||
mode = &crtc->mode;
|
||||
crtc_state = drm_atomic_get_crtc_state(state->state, crtc);
|
||||
mode = &crtc_state->mode;
|
||||
dst_x = state->crtc_x;
|
||||
dst_y = state->crtc_y;
|
||||
dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x);
|
||||
@ -418,92 +647,41 @@ static void sti_gdp_atomic_update(struct drm_plane *drm_plane,
|
||||
/* src_x are in 16.16 format */
|
||||
src_x = state->src_x >> 16;
|
||||
src_y = state->src_y >> 16;
|
||||
src_w = state->src_w >> 16;
|
||||
src_h = state->src_h >> 16;
|
||||
src_w = clamp_val(state->src_w >> 16, 0, GAM_GDP_SIZE_MAX);
|
||||
src_h = clamp_val(state->src_h >> 16, 0, GAM_GDP_SIZE_MAX);
|
||||
|
||||
DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s)\n",
|
||||
crtc->base.id, sti_mixer_to_str(mixer),
|
||||
drm_plane->base.id, sti_plane_to_str(plane));
|
||||
DRM_DEBUG_KMS("%s dst=(%dx%d)@(%d,%d) - src=(%dx%d)@(%d,%d)\n",
|
||||
sti_plane_to_str(plane),
|
||||
dst_w, dst_h, dst_x, dst_y,
|
||||
src_w, src_h, src_x, src_y);
|
||||
|
||||
list = sti_gdp_get_free_nodes(gdp);
|
||||
top_field = list->top_field;
|
||||
btm_field = list->btm_field;
|
||||
|
||||
dev_dbg(gdp->dev, "%s %s top_node:0x%p btm_node:0x%p\n", __func__,
|
||||
sti_plane_to_str(plane), top_field, btm_field);
|
||||
|
||||
/* build the top field */
|
||||
top_field->gam_gdp_agc = GAM_GDP_AGC_FULL_RANGE;
|
||||
top_field->gam_gdp_ctl = WAIT_NEXT_VSYNC;
|
||||
format = sti_gdp_fourcc2format(fb->pixel_format);
|
||||
if (format == -1) {
|
||||
DRM_ERROR("Format not supported by GDP %.4s\n",
|
||||
(char *)&fb->pixel_format);
|
||||
return;
|
||||
return -EINVAL;
|
||||
}
|
||||
top_field->gam_gdp_ctl |= format;
|
||||
top_field->gam_gdp_ctl |= sti_gdp_get_alpharange(format);
|
||||
top_field->gam_gdp_ppt &= ~GAM_GDP_PPT_IGNORE;
|
||||
|
||||
cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
|
||||
if (!cma_obj) {
|
||||
if (!drm_fb_cma_get_gem_obj(fb, 0)) {
|
||||
DRM_ERROR("Can't get CMA GEM object for fb\n");
|
||||
return;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
DRM_DEBUG_DRIVER("drm FB:%d format:%.4s phys@:0x%lx\n", fb->base.id,
|
||||
(char *)&fb->pixel_format,
|
||||
(unsigned long)cma_obj->paddr);
|
||||
|
||||
/* pixel memory location */
|
||||
drm_fb_get_bpp_depth(fb->pixel_format, &depth, &bpp);
|
||||
top_field->gam_gdp_pml = (u32)cma_obj->paddr + fb->offsets[0];
|
||||
top_field->gam_gdp_pml += src_x * (bpp >> 3);
|
||||
top_field->gam_gdp_pml += src_y * fb->pitches[0];
|
||||
|
||||
/* input parameters */
|
||||
top_field->gam_gdp_pmp = fb->pitches[0];
|
||||
top_field->gam_gdp_size = clamp_val(src_h, 0, GAM_GDP_SIZE_MAX) << 16 |
|
||||
clamp_val(src_w, 0, GAM_GDP_SIZE_MAX);
|
||||
|
||||
/* output parameters */
|
||||
ydo = sti_vtg_get_line_number(*mode, dst_y);
|
||||
yds = sti_vtg_get_line_number(*mode, dst_y + dst_h - 1);
|
||||
xdo = sti_vtg_get_pixel_number(*mode, dst_x);
|
||||
xds = sti_vtg_get_pixel_number(*mode, dst_x + dst_w - 1);
|
||||
top_field->gam_gdp_vpo = (ydo << 16) | xdo;
|
||||
top_field->gam_gdp_vps = (yds << 16) | xds;
|
||||
|
||||
/* Same content and chained together */
|
||||
memcpy(btm_field, top_field, sizeof(*btm_field));
|
||||
top_field->gam_gdp_nvn = list->btm_field_paddr;
|
||||
btm_field->gam_gdp_nvn = list->top_field_paddr;
|
||||
|
||||
/* Interlaced mode */
|
||||
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
|
||||
btm_field->gam_gdp_pml = top_field->gam_gdp_pml +
|
||||
fb->pitches[0];
|
||||
|
||||
if (first_prepare) {
|
||||
/* Register gdp callback */
|
||||
if (sti_vtg_register_client(mixer->id == STI_MIXER_MAIN ?
|
||||
compo->vtg_main : compo->vtg_aux,
|
||||
&gdp->vtg_field_nb, crtc)) {
|
||||
gdp->vtg = mixer->id == STI_MIXER_MAIN ?
|
||||
compo->vtg_main : compo->vtg_aux;
|
||||
if (sti_vtg_register_client(gdp->vtg,
|
||||
&gdp->vtg_field_nb, crtc)) {
|
||||
DRM_ERROR("Cannot register VTG notifier\n");
|
||||
return;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Set and enable gdp clock */
|
||||
if (gdp->clk_pix) {
|
||||
struct clk *clkp;
|
||||
int rate = mode->clock * 1000;
|
||||
int res;
|
||||
|
||||
/* According to the mixer used, the gdp pixel clock
|
||||
* should have a different parent clock. */
|
||||
/*
|
||||
* According to the mixer used, the gdp pixel clock
|
||||
* should have a different parent clock.
|
||||
*/
|
||||
if (mixer->id == STI_MIXER_MAIN)
|
||||
clkp = gdp->clk_main_parent;
|
||||
else
|
||||
@ -516,16 +694,114 @@ static void sti_gdp_atomic_update(struct drm_plane *drm_plane,
|
||||
if (res < 0) {
|
||||
DRM_ERROR("Cannot set rate (%dHz) for gdp\n",
|
||||
rate);
|
||||
return;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (clk_prepare_enable(gdp->clk_pix)) {
|
||||
DRM_ERROR("Failed to prepare/enable gdp\n");
|
||||
return;
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s)\n",
|
||||
crtc->base.id, sti_mixer_to_str(mixer),
|
||||
drm_plane->base.id, sti_plane_to_str(plane));
|
||||
DRM_DEBUG_KMS("%s dst=(%dx%d)@(%d,%d) - src=(%dx%d)@(%d,%d)\n",
|
||||
sti_plane_to_str(plane),
|
||||
dst_w, dst_h, dst_x, dst_y,
|
||||
src_w, src_h, src_x, src_y);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sti_gdp_atomic_update(struct drm_plane *drm_plane,
|
||||
struct drm_plane_state *oldstate)
|
||||
{
|
||||
struct drm_plane_state *state = drm_plane->state;
|
||||
struct sti_plane *plane = to_sti_plane(drm_plane);
|
||||
struct sti_gdp *gdp = to_sti_gdp(plane);
|
||||
struct drm_crtc *crtc = state->crtc;
|
||||
struct drm_framebuffer *fb = state->fb;
|
||||
struct drm_display_mode *mode;
|
||||
int dst_x, dst_y, dst_w, dst_h;
|
||||
int src_x, src_y, src_w, src_h;
|
||||
struct drm_gem_cma_object *cma_obj;
|
||||
struct sti_gdp_node_list *list;
|
||||
struct sti_gdp_node_list *curr_list;
|
||||
struct sti_gdp_node *top_field, *btm_field;
|
||||
u32 dma_updated_top;
|
||||
u32 dma_updated_btm;
|
||||
int format;
|
||||
unsigned int depth, bpp;
|
||||
u32 ydo, xdo, yds, xds;
|
||||
|
||||
if (!crtc || !fb)
|
||||
return;
|
||||
|
||||
mode = &crtc->mode;
|
||||
dst_x = state->crtc_x;
|
||||
dst_y = state->crtc_y;
|
||||
dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x);
|
||||
dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y);
|
||||
/* src_x are in 16.16 format */
|
||||
src_x = state->src_x >> 16;
|
||||
src_y = state->src_y >> 16;
|
||||
src_w = clamp_val(state->src_w >> 16, 0, GAM_GDP_SIZE_MAX);
|
||||
src_h = clamp_val(state->src_h >> 16, 0, GAM_GDP_SIZE_MAX);
|
||||
|
||||
list = sti_gdp_get_free_nodes(gdp);
|
||||
top_field = list->top_field;
|
||||
btm_field = list->btm_field;
|
||||
|
||||
dev_dbg(gdp->dev, "%s %s top_node:0x%p btm_node:0x%p\n", __func__,
|
||||
sti_plane_to_str(plane), top_field, btm_field);
|
||||
|
||||
/* build the top field */
|
||||
top_field->gam_gdp_agc = GAM_GDP_AGC_FULL_RANGE;
|
||||
top_field->gam_gdp_ctl = WAIT_NEXT_VSYNC;
|
||||
format = sti_gdp_fourcc2format(fb->pixel_format);
|
||||
top_field->gam_gdp_ctl |= format;
|
||||
top_field->gam_gdp_ctl |= sti_gdp_get_alpharange(format);
|
||||
top_field->gam_gdp_ppt &= ~GAM_GDP_PPT_IGNORE;
|
||||
|
||||
cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
|
||||
|
||||
DRM_DEBUG_DRIVER("drm FB:%d format:%.4s phys@:0x%lx\n", fb->base.id,
|
||||
(char *)&fb->pixel_format,
|
||||
(unsigned long)cma_obj->paddr);
|
||||
|
||||
/* pixel memory location */
|
||||
drm_fb_get_bpp_depth(fb->pixel_format, &depth, &bpp);
|
||||
top_field->gam_gdp_pml = (u32)cma_obj->paddr + fb->offsets[0];
|
||||
top_field->gam_gdp_pml += src_x * (bpp >> 3);
|
||||
top_field->gam_gdp_pml += src_y * fb->pitches[0];
|
||||
|
||||
/* output parameters (clamped / cropped) */
|
||||
dst_w = sti_gdp_get_dst(gdp->dev, dst_w, src_w);
|
||||
dst_h = sti_gdp_get_dst(gdp->dev, dst_h, src_h);
|
||||
ydo = sti_vtg_get_line_number(*mode, dst_y);
|
||||
yds = sti_vtg_get_line_number(*mode, dst_y + dst_h - 1);
|
||||
xdo = sti_vtg_get_pixel_number(*mode, dst_x);
|
||||
xds = sti_vtg_get_pixel_number(*mode, dst_x + dst_w - 1);
|
||||
top_field->gam_gdp_vpo = (ydo << 16) | xdo;
|
||||
top_field->gam_gdp_vps = (yds << 16) | xds;
|
||||
|
||||
/* input parameters */
|
||||
src_w = dst_w;
|
||||
top_field->gam_gdp_pmp = fb->pitches[0];
|
||||
top_field->gam_gdp_size = src_h << 16 | src_w;
|
||||
|
||||
/* Same content and chained together */
|
||||
memcpy(btm_field, top_field, sizeof(*btm_field));
|
||||
top_field->gam_gdp_nvn = list->btm_field_paddr;
|
||||
btm_field->gam_gdp_nvn = list->top_field_paddr;
|
||||
|
||||
/* Interlaced mode */
|
||||
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
|
||||
btm_field->gam_gdp_pml = top_field->gam_gdp_pml +
|
||||
fb->pitches[0];
|
||||
|
||||
/* Update the NVN field of the 'right' field of the current GDP node
|
||||
* (being used by the HW) with the address of the updated ('free') top
|
||||
* field GDP node.
|
||||
@ -574,6 +850,8 @@ static void sti_gdp_atomic_update(struct drm_plane *drm_plane,
|
||||
}
|
||||
|
||||
end:
|
||||
sti_plane_update_fps(plane, true, false);
|
||||
|
||||
plane->status = STI_PLANE_UPDATED;
|
||||
}
|
||||
|
||||
@ -581,7 +859,6 @@ static void sti_gdp_atomic_disable(struct drm_plane *drm_plane,
|
||||
struct drm_plane_state *oldstate)
|
||||
{
|
||||
struct sti_plane *plane = to_sti_plane(drm_plane);
|
||||
struct sti_mixer *mixer = to_sti_mixer(drm_plane->crtc);
|
||||
|
||||
if (!drm_plane->crtc) {
|
||||
DRM_DEBUG_DRIVER("drm plane:%d not enabled\n",
|
||||
@ -590,13 +867,15 @@ static void sti_gdp_atomic_disable(struct drm_plane *drm_plane,
|
||||
}
|
||||
|
||||
DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n",
|
||||
drm_plane->crtc->base.id, sti_mixer_to_str(mixer),
|
||||
drm_plane->crtc->base.id,
|
||||
sti_mixer_to_str(to_sti_mixer(drm_plane->crtc)),
|
||||
drm_plane->base.id, sti_plane_to_str(plane));
|
||||
|
||||
plane->status = STI_PLANE_DISABLING;
|
||||
}
|
||||
|
||||
static const struct drm_plane_helper_funcs sti_gdp_helpers_funcs = {
|
||||
.atomic_check = sti_gdp_atomic_check,
|
||||
.atomic_update = sti_gdp_atomic_update,
|
||||
.atomic_disable = sti_gdp_atomic_disable,
|
||||
};
|
||||
@ -640,6 +919,9 @@ struct drm_plane *sti_gdp_create(struct drm_device *drm_dev,
|
||||
|
||||
sti_plane_init_property(&gdp->plane, type);
|
||||
|
||||
if (gdp_debugfs_init(gdp, drm_dev->primary))
|
||||
DRM_ERROR("GDP debugfs setup failed\n");
|
||||
|
||||
return &gdp->plane.drm_plane;
|
||||
|
||||
err:
|
||||
|
@ -326,6 +326,103 @@ static void hda_enable_hd_dacs(struct sti_hda *hda, bool enable)
|
||||
}
|
||||
}
|
||||
|
||||
#define DBGFS_DUMP(reg) seq_printf(s, "\n %-25s 0x%08X", #reg, \
|
||||
readl(hda->regs + reg))
|
||||
|
||||
static void hda_dbg_cfg(struct seq_file *s, int val)
|
||||
{
|
||||
seq_puts(s, "\tAWG ");
|
||||
seq_puts(s, val & CFG_AWG_ASYNC_EN ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
static void hda_dbg_awg_microcode(struct seq_file *s, void __iomem *reg)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
seq_puts(s, "\n\n");
|
||||
seq_puts(s, " HDA AWG microcode:");
|
||||
for (i = 0; i < AWG_MAX_INST; i++) {
|
||||
if (i % 8 == 0)
|
||||
seq_printf(s, "\n %04X:", i);
|
||||
seq_printf(s, " %04X", readl(reg + i * 4));
|
||||
}
|
||||
}
|
||||
|
||||
static void hda_dbg_video_dacs_ctrl(struct seq_file *s, void __iomem *reg)
|
||||
{
|
||||
u32 val = readl(reg);
|
||||
u32 mask;
|
||||
|
||||
switch ((u32)reg & VIDEO_DACS_CONTROL_MASK) {
|
||||
case VIDEO_DACS_CONTROL_SYSCFG2535:
|
||||
mask = DAC_CFG_HD_OFF_MASK;
|
||||
break;
|
||||
case VIDEO_DACS_CONTROL_SYSCFG5072:
|
||||
mask = DAC_CFG_HD_HZUVW_OFF_MASK;
|
||||
break;
|
||||
default:
|
||||
DRM_DEBUG_DRIVER("Warning: DACS ctrl register not supported!");
|
||||
return;
|
||||
}
|
||||
|
||||
seq_puts(s, "\n");
|
||||
seq_printf(s, "\n %-25s 0x%08X", "VIDEO_DACS_CONTROL", val);
|
||||
seq_puts(s, "\tHD DACs ");
|
||||
seq_puts(s, val & mask ? "disabled" : "enabled");
|
||||
}
|
||||
|
||||
static int hda_dbg_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct drm_info_node *node = s->private;
|
||||
struct sti_hda *hda = (struct sti_hda *)node->info_ent->data;
|
||||
struct drm_device *dev = node->minor->dev;
|
||||
int ret;
|
||||
|
||||
ret = mutex_lock_interruptible(&dev->struct_mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
seq_printf(s, "HD Analog: (vaddr = 0x%p)", hda->regs);
|
||||
DBGFS_DUMP(HDA_ANA_CFG);
|
||||
hda_dbg_cfg(s, readl(hda->regs + HDA_ANA_CFG));
|
||||
DBGFS_DUMP(HDA_ANA_SCALE_CTRL_Y);
|
||||
DBGFS_DUMP(HDA_ANA_SCALE_CTRL_CB);
|
||||
DBGFS_DUMP(HDA_ANA_SCALE_CTRL_CR);
|
||||
DBGFS_DUMP(HDA_ANA_ANC_CTRL);
|
||||
DBGFS_DUMP(HDA_ANA_SRC_Y_CFG);
|
||||
DBGFS_DUMP(HDA_ANA_SRC_C_CFG);
|
||||
hda_dbg_awg_microcode(s, hda->regs + HDA_SYNC_AWGI);
|
||||
if (hda->video_dacs_ctrl)
|
||||
hda_dbg_video_dacs_ctrl(s, hda->video_dacs_ctrl);
|
||||
seq_puts(s, "\n");
|
||||
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct drm_info_list hda_debugfs_files[] = {
|
||||
{ "hda", hda_dbg_show, 0, NULL },
|
||||
};
|
||||
|
||||
static void hda_debugfs_exit(struct sti_hda *hda, struct drm_minor *minor)
|
||||
{
|
||||
drm_debugfs_remove_files(hda_debugfs_files,
|
||||
ARRAY_SIZE(hda_debugfs_files),
|
||||
minor);
|
||||
}
|
||||
|
||||
static int hda_debugfs_init(struct sti_hda *hda, struct drm_minor *minor)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hda_debugfs_files); i++)
|
||||
hda_debugfs_files[i].data = hda;
|
||||
|
||||
return drm_debugfs_create_files(hda_debugfs_files,
|
||||
ARRAY_SIZE(hda_debugfs_files),
|
||||
minor->debugfs_root, minor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure AWG, writing instructions
|
||||
*
|
||||
@ -685,6 +782,12 @@ static int sti_hda_bind(struct device *dev, struct device *master, void *data)
|
||||
goto err_sysfs;
|
||||
}
|
||||
|
||||
/* force to disable hd dacs at startup */
|
||||
hda_enable_hd_dacs(hda, false);
|
||||
|
||||
if (hda_debugfs_init(hda, drm_dev->primary))
|
||||
DRM_ERROR("HDA debugfs setup failed\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err_sysfs:
|
||||
@ -697,7 +800,10 @@ err_connector:
|
||||
static void sti_hda_unbind(struct device *dev,
|
||||
struct device *master, void *data)
|
||||
{
|
||||
/* do nothing */
|
||||
struct sti_hda *hda = dev_get_drvdata(dev);
|
||||
struct drm_device *drm_dev = data;
|
||||
|
||||
hda_debugfs_exit(hda, drm_dev->primary);
|
||||
}
|
||||
|
||||
static const struct component_ops sti_hda_ops = {
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#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>
|
||||
@ -51,9 +52,18 @@
|
||||
#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)
|
||||
@ -65,6 +75,8 @@
|
||||
#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
|
||||
@ -117,6 +129,8 @@ struct sti_hdmi_connector {
|
||||
struct drm_connector drm_connector;
|
||||
struct drm_encoder *encoder;
|
||||
struct sti_hdmi *hdmi;
|
||||
struct drm_property *colorspace_property;
|
||||
struct drm_property *hdmi_mode_property;
|
||||
};
|
||||
|
||||
#define to_sti_hdmi_connector(x) \
|
||||
@ -217,8 +231,10 @@ static void hdmi_config(struct sti_hdmi *hdmi)
|
||||
/* Clear overrun and underrun fifo */
|
||||
conf = HDMI_CFG_FIFO_OVERRUN_CLR | HDMI_CFG_FIFO_UNDERRUN_CLR;
|
||||
|
||||
/* Enable HDMI mode not DVI */
|
||||
conf |= HDMI_CFG_HDMI_NOT_DVI | HDMI_CFG_ESS_NOT_OESS;
|
||||
/* Select encryption type and the framing mode */
|
||||
conf |= HDMI_CFG_ESS_NOT_OESS;
|
||||
if (hdmi->hdmi_mode == HDMI_MODE_HDMI)
|
||||
conf |= HDMI_CFG_HDMI_NOT_DVI;
|
||||
|
||||
/* Enable sink term detection */
|
||||
conf |= HDMI_CFG_SINK_TERM_DET_EN;
|
||||
@ -241,6 +257,47 @@ static void hdmi_config(struct sti_hdmi *hdmi)
|
||||
hdmi_write(hdmi, conf, HDMI_CFG);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to concatenate infoframe in 32 bits word
|
||||
*
|
||||
@ -266,12 +323,13 @@ static inline unsigned int hdmi_infoframe_subpack(const u8 *ptr, size_t size)
|
||||
* @data: infoframe to write
|
||||
* @size: size to write
|
||||
*/
|
||||
static void hdmi_infoframe_write_infopack(struct sti_hdmi *hdmi, const u8 *data)
|
||||
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;
|
||||
size_t size;
|
||||
|
||||
switch (*ptr) {
|
||||
case HDMI_INFOFRAME_TYPE_AVI:
|
||||
@ -279,17 +337,19 @@ static void hdmi_infoframe_write_infopack(struct sti_hdmi *hdmi, const u8 *data)
|
||||
mode = HDMI_IFRAME_FIELD;
|
||||
head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AVI);
|
||||
pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AVI);
|
||||
size = HDMI_AVI_INFOFRAME_SIZE;
|
||||
break;
|
||||
|
||||
case HDMI_INFOFRAME_TYPE_AUDIO:
|
||||
slot = HDMI_IFRAME_SLOT_AUDIO;
|
||||
mode = HDMI_IFRAME_FRAME;
|
||||
head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AUDIO);
|
||||
pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AUDIO);
|
||||
size = HDMI_AUDIO_INFOFRAME_SIZE;
|
||||
break;
|
||||
|
||||
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;
|
||||
@ -308,8 +368,9 @@ static void hdmi_infoframe_write_infopack(struct sti_hdmi *hdmi, const u8 *data)
|
||||
/*
|
||||
* Each subpack contains 4 bytes
|
||||
* The First Bytes of the first subpacket must contain the checksum
|
||||
* Packet size in increase by one.
|
||||
* Packet size is increase by one.
|
||||
*/
|
||||
size = size - HDMI_INFOFRAME_HEADER_SIZE + 1;
|
||||
for (i = 0; i < size; i += sizeof(u32)) {
|
||||
size_t num;
|
||||
|
||||
@ -321,7 +382,7 @@ static void hdmi_infoframe_write_infopack(struct sti_hdmi *hdmi, const u8 *data)
|
||||
|
||||
/* Enable transmission slot for updated infoframe */
|
||||
val = hdmi_read(hdmi, HDMI_SW_DI_CFG);
|
||||
val |= HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_FIELD, slot);
|
||||
val |= HDMI_IFRAME_CFG_DI_N(mode, slot);
|
||||
hdmi_write(hdmi, val, HDMI_SW_DI_CFG);
|
||||
}
|
||||
|
||||
@ -352,7 +413,7 @@ static int hdmi_avi_infoframe_config(struct sti_hdmi *hdmi)
|
||||
}
|
||||
|
||||
/* fixed infoframe configuration not linked to the mode */
|
||||
infoframe.colorspace = HDMI_COLORSPACE_RGB;
|
||||
infoframe.colorspace = hdmi->colorspace;
|
||||
infoframe.quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT;
|
||||
infoframe.colorimetry = HDMI_COLORIMETRY_NONE;
|
||||
|
||||
@ -362,7 +423,7 @@ static int hdmi_avi_infoframe_config(struct sti_hdmi *hdmi)
|
||||
return ret;
|
||||
}
|
||||
|
||||
hdmi_infoframe_write_infopack(hdmi, buffer);
|
||||
hdmi_infoframe_write_infopack(hdmi, buffer, ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -398,7 +459,49 @@ static int hdmi_audio_infoframe_config(struct sti_hdmi *hdmi)
|
||||
return ret;
|
||||
}
|
||||
|
||||
hdmi_infoframe_write_infopack(hdmi, buffer);
|
||||
hdmi_infoframe_write_infopack(hdmi, buffer, ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
@ -448,6 +551,172 @@ static void hdmi_swreset(struct sti_hdmi *hdmi)
|
||||
clk_disable_unprepare(hdmi->clk_audio);
|
||||
}
|
||||
|
||||
#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_SINK_TERM_DET_EN;
|
||||
DBGFS_PRINT_STR("Sink term detection:", tmp ? "enable" : "disable");
|
||||
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");
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
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;
|
||||
struct drm_device *dev = node->minor->dev;
|
||||
int ret;
|
||||
|
||||
ret = mutex_lock_interruptible(&dev->struct_mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
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));
|
||||
|
||||
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");
|
||||
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct drm_info_list hdmi_debugfs_files[] = {
|
||||
{ "hdmi", hdmi_dbg_show, 0, NULL },
|
||||
};
|
||||
|
||||
static void hdmi_debugfs_exit(struct sti_hdmi *hdmi, struct drm_minor *minor)
|
||||
{
|
||||
drm_debugfs_remove_files(hdmi_debugfs_files,
|
||||
ARRAY_SIZE(hdmi_debugfs_files),
|
||||
minor);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static void sti_hdmi_disable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct sti_hdmi *hdmi = bridge->driver_private;
|
||||
@ -468,6 +737,11 @@ static void sti_hdmi_disable(struct drm_bridge *bridge)
|
||||
/* 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);
|
||||
@ -523,6 +797,10 @@ static void sti_hdmi_pre_enable(struct drm_bridge *bridge)
|
||||
if (hdmi_audio_infoframe_config(hdmi))
|
||||
DRM_ERROR("Unable to configure AUDIO infoframe\n");
|
||||
|
||||
/* Program VS infoframe */
|
||||
if (hdmi_vendor_infoframe_config(hdmi))
|
||||
DRM_ERROR("Unable to configure VS infoframe\n");
|
||||
|
||||
/* Sw reset */
|
||||
hdmi_swreset(hdmi);
|
||||
}
|
||||
@ -664,12 +942,97 @@ static void sti_hdmi_connector_destroy(struct drm_connector *connector)
|
||||
kfree(hdmi_connector);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
/* hdmi_mode property */
|
||||
hdmi->hdmi_mode = DEFAULT_HDMI_MODE;
|
||||
prop = drm_property_create_enum(drm_dev, 0, "hdmi_mode",
|
||||
hdmi_mode_names,
|
||||
ARRAY_SIZE(hdmi_mode_names));
|
||||
if (!prop) {
|
||||
DRM_ERROR("fails to create colorspace property\n");
|
||||
return;
|
||||
}
|
||||
hdmi_connector->hdmi_mode_property = prop;
|
||||
drm_object_attach_property(&connector->base, prop, hdmi->hdmi_mode);
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (property == hdmi_connector->hdmi_mode_property) {
|
||||
hdmi->hdmi_mode = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DRM_ERROR("failed to set hdmi connector property\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (property == hdmi_connector->hdmi_mode_property) {
|
||||
*val = hdmi->hdmi_mode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DRM_ERROR("failed to get hdmi connector property\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
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 = sti_hdmi_connector_destroy,
|
||||
.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,
|
||||
};
|
||||
@ -729,6 +1092,9 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data)
|
||||
drm_connector_helper_add(drm_connector,
|
||||
&sti_hdmi_connector_helper_funcs);
|
||||
|
||||
/* initialise property */
|
||||
sti_hdmi_connector_init_property(drm_dev, drm_connector);
|
||||
|
||||
err = drm_connector_register(drm_connector);
|
||||
if (err)
|
||||
goto err_connector;
|
||||
@ -742,6 +1108,9 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data)
|
||||
/* Enable default interrupts */
|
||||
hdmi_write(hdmi, HDMI_DEFAULT_INT, HDMI_INT_EN);
|
||||
|
||||
if (hdmi_debugfs_init(hdmi, drm_dev->primary))
|
||||
DRM_ERROR("HDMI debugfs setup failed\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err_sysfs:
|
||||
@ -755,7 +1124,10 @@ err_connector:
|
||||
static void sti_hdmi_unbind(struct device *dev,
|
||||
struct device *master, void *data)
|
||||
{
|
||||
/* do nothing */
|
||||
struct sti_hdmi *hdmi = dev_get_drvdata(dev);
|
||||
struct drm_device *drm_dev = data;
|
||||
|
||||
hdmi_debugfs_exit(hdmi, drm_dev->primary);
|
||||
}
|
||||
|
||||
static const struct component_ops sti_hdmi_ops = {
|
||||
|
@ -7,15 +7,14 @@
|
||||
#ifndef _STI_HDMI_H_
|
||||
#define _STI_HDMI_H_
|
||||
|
||||
#include <linux/hdmi.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
|
||||
#define HDMI_STA 0x0010
|
||||
#define HDMI_STA_DLL_LCK BIT(5)
|
||||
|
||||
#define HDMI_STA_HOT_PLUG_SHIFT 4
|
||||
#define HDMI_STA_HOT_PLUG (1 << HDMI_STA_HOT_PLUG_SHIFT)
|
||||
#define HDMI_STA_HOT_PLUG BIT(4)
|
||||
|
||||
struct sti_hdmi;
|
||||
|
||||
@ -24,6 +23,27 @@ struct hdmi_phy_ops {
|
||||
void (*stop)(struct sti_hdmi *hdmi);
|
||||
};
|
||||
|
||||
/* values for the framing mode property */
|
||||
enum sti_hdmi_modes {
|
||||
HDMI_MODE_HDMI,
|
||||
HDMI_MODE_DVI,
|
||||
};
|
||||
|
||||
static const struct drm_prop_enum_list hdmi_mode_names[] = {
|
||||
{ HDMI_MODE_HDMI, "hdmi" },
|
||||
{ HDMI_MODE_DVI, "dvi" },
|
||||
};
|
||||
|
||||
#define DEFAULT_HDMI_MODE HDMI_MODE_HDMI
|
||||
|
||||
static const struct drm_prop_enum_list colorspace_mode_names[] = {
|
||||
{ HDMI_COLORSPACE_RGB, "rgb" },
|
||||
{ HDMI_COLORSPACE_YUV422, "yuv422" },
|
||||
{ HDMI_COLORSPACE_YUV444, "yuv444" },
|
||||
};
|
||||
|
||||
#define DEFAULT_COLORSPACE_MODE HDMI_COLORSPACE_RGB
|
||||
|
||||
/**
|
||||
* STI hdmi structure
|
||||
*
|
||||
@ -44,6 +64,9 @@ struct hdmi_phy_ops {
|
||||
* @wait_event: wait event
|
||||
* @event_received: wait event status
|
||||
* @reset: reset control of the hdmi phy
|
||||
* @ddc_adapt: i2c ddc adapter
|
||||
* @colorspace: current colorspace selected
|
||||
* @hdmi_mode: select framing for HDMI or DVI
|
||||
*/
|
||||
struct sti_hdmi {
|
||||
struct device dev;
|
||||
@ -64,6 +87,8 @@ struct sti_hdmi {
|
||||
bool event_received;
|
||||
struct reset_control *reset;
|
||||
struct i2c_adapter *ddc_adapt;
|
||||
enum hdmi_colorspace colorspace;
|
||||
enum sti_hdmi_modes hdmi_mode;
|
||||
};
|
||||
|
||||
u32 hdmi_read(struct sti_hdmi *hdmi, int offset);
|
||||
|
@ -4,14 +4,11 @@
|
||||
* License terms: GNU General Public License (GPL), version 2
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/component.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
|
||||
@ -329,8 +326,6 @@ struct sti_hqvdp_cmd {
|
||||
* @reset: reset control
|
||||
* @vtg_nb: notifier to handle VTG Vsync
|
||||
* @btm_field_pending: is there any bottom field (interlaced frame) to display
|
||||
* @curr_field_count: number of field updates
|
||||
* @last_field_count: number of field updates since last fps measure
|
||||
* @hqvdp_cmd: buffer of commands
|
||||
* @hqvdp_cmd_paddr: physical address of hqvdp_cmd
|
||||
* @vtg: vtg for main data path
|
||||
@ -346,10 +341,8 @@ struct sti_hqvdp {
|
||||
struct reset_control *reset;
|
||||
struct notifier_block vtg_nb;
|
||||
bool btm_field_pending;
|
||||
unsigned int curr_field_count;
|
||||
unsigned int last_field_count;
|
||||
void *hqvdp_cmd;
|
||||
dma_addr_t hqvdp_cmd_paddr;
|
||||
u32 hqvdp_cmd_paddr;
|
||||
struct sti_vtg *vtg;
|
||||
bool xp70_initialized;
|
||||
};
|
||||
@ -372,8 +365,8 @@ static const uint32_t hqvdp_supported_formats[] = {
|
||||
*/
|
||||
static int sti_hqvdp_get_free_cmd(struct sti_hqvdp *hqvdp)
|
||||
{
|
||||
int curr_cmd, next_cmd;
|
||||
dma_addr_t cmd = hqvdp->hqvdp_cmd_paddr;
|
||||
u32 curr_cmd, next_cmd;
|
||||
u32 cmd = hqvdp->hqvdp_cmd_paddr;
|
||||
int i;
|
||||
|
||||
curr_cmd = readl(hqvdp->regs + HQVDP_MBX_CURRENT_CMD);
|
||||
@ -400,8 +393,8 @@ static int sti_hqvdp_get_free_cmd(struct sti_hqvdp *hqvdp)
|
||||
*/
|
||||
static int sti_hqvdp_get_curr_cmd(struct sti_hqvdp *hqvdp)
|
||||
{
|
||||
int curr_cmd;
|
||||
dma_addr_t cmd = hqvdp->hqvdp_cmd_paddr;
|
||||
u32 curr_cmd;
|
||||
u32 cmd = hqvdp->hqvdp_cmd_paddr;
|
||||
unsigned int i;
|
||||
|
||||
curr_cmd = readl(hqvdp->regs + HQVDP_MBX_CURRENT_CMD);
|
||||
@ -416,6 +409,246 @@ static int sti_hqvdp_get_curr_cmd(struct sti_hqvdp *hqvdp)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* sti_hqvdp_get_next_cmd
|
||||
* @hqvdp: hqvdp structure
|
||||
*
|
||||
* Look for the next hqvdp_cmd that will be used by the FW.
|
||||
*
|
||||
* RETURNS:
|
||||
* the offset of the next command that will be used.
|
||||
* -1 in error cases
|
||||
*/
|
||||
static int sti_hqvdp_get_next_cmd(struct sti_hqvdp *hqvdp)
|
||||
{
|
||||
int next_cmd;
|
||||
dma_addr_t cmd = hqvdp->hqvdp_cmd_paddr;
|
||||
unsigned int i;
|
||||
|
||||
next_cmd = readl(hqvdp->regs + HQVDP_MBX_NEXT_CMD);
|
||||
|
||||
for (i = 0; i < NB_VDP_CMD; i++) {
|
||||
if (cmd == next_cmd)
|
||||
return i * sizeof(struct sti_hqvdp_cmd);
|
||||
|
||||
cmd += sizeof(struct sti_hqvdp_cmd);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define DBGFS_DUMP(reg) seq_printf(s, "\n %-25s 0x%08X", #reg, \
|
||||
readl(hqvdp->regs + reg))
|
||||
|
||||
static const char *hqvdp_dbg_get_lut(u32 *coef)
|
||||
{
|
||||
if (!memcmp(coef, coef_lut_a_legacy, 16))
|
||||
return "LUT A";
|
||||
if (!memcmp(coef, coef_lut_b, 16))
|
||||
return "LUT B";
|
||||
if (!memcmp(coef, coef_lut_c_y_legacy, 16))
|
||||
return "LUT C Y";
|
||||
if (!memcmp(coef, coef_lut_c_c_legacy, 16))
|
||||
return "LUT C C";
|
||||
if (!memcmp(coef, coef_lut_d_y_legacy, 16))
|
||||
return "LUT D Y";
|
||||
if (!memcmp(coef, coef_lut_d_c_legacy, 16))
|
||||
return "LUT D C";
|
||||
if (!memcmp(coef, coef_lut_e_y_legacy, 16))
|
||||
return "LUT E Y";
|
||||
if (!memcmp(coef, coef_lut_e_c_legacy, 16))
|
||||
return "LUT E C";
|
||||
if (!memcmp(coef, coef_lut_f_y_legacy, 16))
|
||||
return "LUT F Y";
|
||||
if (!memcmp(coef, coef_lut_f_c_legacy, 16))
|
||||
return "LUT F C";
|
||||
return "<UNKNOWN>";
|
||||
}
|
||||
|
||||
static void hqvdp_dbg_dump_cmd(struct seq_file *s, struct sti_hqvdp_cmd *c)
|
||||
{
|
||||
int src_w, src_h, dst_w, dst_h;
|
||||
|
||||
seq_puts(s, "\n\tTOP:");
|
||||
seq_printf(s, "\n\t %-20s 0x%08X", "Config", c->top.config);
|
||||
switch (c->top.config) {
|
||||
case TOP_CONFIG_PROGRESSIVE:
|
||||
seq_puts(s, "\tProgressive");
|
||||
break;
|
||||
case TOP_CONFIG_INTER_TOP:
|
||||
seq_puts(s, "\tInterlaced, top field");
|
||||
break;
|
||||
case TOP_CONFIG_INTER_BTM:
|
||||
seq_puts(s, "\tInterlaced, bottom field");
|
||||
break;
|
||||
default:
|
||||
seq_puts(s, "\t<UNKNOWN>");
|
||||
break;
|
||||
}
|
||||
|
||||
seq_printf(s, "\n\t %-20s 0x%08X", "MemFormat", c->top.mem_format);
|
||||
seq_printf(s, "\n\t %-20s 0x%08X", "CurrentY", c->top.current_luma);
|
||||
seq_printf(s, "\n\t %-20s 0x%08X", "CurrentC", c->top.current_chroma);
|
||||
seq_printf(s, "\n\t %-20s 0x%08X", "YSrcPitch", c->top.luma_src_pitch);
|
||||
seq_printf(s, "\n\t %-20s 0x%08X", "CSrcPitch",
|
||||
c->top.chroma_src_pitch);
|
||||
seq_printf(s, "\n\t %-20s 0x%08X", "InputFrameSize",
|
||||
c->top.input_frame_size);
|
||||
seq_printf(s, "\t%dx%d",
|
||||
c->top.input_frame_size & 0x0000FFFF,
|
||||
c->top.input_frame_size >> 16);
|
||||
seq_printf(s, "\n\t %-20s 0x%08X", "InputViewportSize",
|
||||
c->top.input_viewport_size);
|
||||
src_w = c->top.input_viewport_size & 0x0000FFFF;
|
||||
src_h = c->top.input_viewport_size >> 16;
|
||||
seq_printf(s, "\t%dx%d", src_w, src_h);
|
||||
|
||||
seq_puts(s, "\n\tHVSRC:");
|
||||
seq_printf(s, "\n\t %-20s 0x%08X", "OutputPictureSize",
|
||||
c->hvsrc.output_picture_size);
|
||||
dst_w = c->hvsrc.output_picture_size & 0x0000FFFF;
|
||||
dst_h = c->hvsrc.output_picture_size >> 16;
|
||||
seq_printf(s, "\t%dx%d", dst_w, dst_h);
|
||||
seq_printf(s, "\n\t %-20s 0x%08X", "ParamCtrl", c->hvsrc.param_ctrl);
|
||||
|
||||
seq_printf(s, "\n\t %-20s %s", "yh_coef",
|
||||
hqvdp_dbg_get_lut(c->hvsrc.yh_coef));
|
||||
seq_printf(s, "\n\t %-20s %s", "ch_coef",
|
||||
hqvdp_dbg_get_lut(c->hvsrc.ch_coef));
|
||||
seq_printf(s, "\n\t %-20s %s", "yv_coef",
|
||||
hqvdp_dbg_get_lut(c->hvsrc.yv_coef));
|
||||
seq_printf(s, "\n\t %-20s %s", "cv_coef",
|
||||
hqvdp_dbg_get_lut(c->hvsrc.cv_coef));
|
||||
|
||||
seq_printf(s, "\n\t %-20s", "ScaleH");
|
||||
if (dst_w > src_w)
|
||||
seq_printf(s, " %d/1", dst_w / src_w);
|
||||
else
|
||||
seq_printf(s, " 1/%d", src_w / dst_w);
|
||||
|
||||
seq_printf(s, "\n\t %-20s", "tScaleV");
|
||||
if (dst_h > src_h)
|
||||
seq_printf(s, " %d/1", dst_h / src_h);
|
||||
else
|
||||
seq_printf(s, " 1/%d", src_h / dst_h);
|
||||
|
||||
seq_puts(s, "\n\tCSDI:");
|
||||
seq_printf(s, "\n\t %-20s 0x%08X\t", "Config", c->csdi.config);
|
||||
switch (c->csdi.config) {
|
||||
case CSDI_CONFIG_PROG:
|
||||
seq_puts(s, "Bypass");
|
||||
break;
|
||||
case CSDI_CONFIG_INTER_DIR:
|
||||
seq_puts(s, "Deinterlace, directional");
|
||||
break;
|
||||
default:
|
||||
seq_puts(s, "<UNKNOWN>");
|
||||
break;
|
||||
}
|
||||
|
||||
seq_printf(s, "\n\t %-20s 0x%08X", "Config2", c->csdi.config2);
|
||||
seq_printf(s, "\n\t %-20s 0x%08X", "DcdiConfig", c->csdi.dcdi_config);
|
||||
}
|
||||
|
||||
static int hqvdp_dbg_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct drm_info_node *node = s->private;
|
||||
struct sti_hqvdp *hqvdp = (struct sti_hqvdp *)node->info_ent->data;
|
||||
struct drm_device *dev = node->minor->dev;
|
||||
int cmd, cmd_offset, infoxp70;
|
||||
void *virt;
|
||||
int ret;
|
||||
|
||||
ret = mutex_lock_interruptible(&dev->struct_mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
seq_printf(s, "%s: (vaddr = 0x%p)",
|
||||
sti_plane_to_str(&hqvdp->plane), hqvdp->regs);
|
||||
|
||||
DBGFS_DUMP(HQVDP_MBX_IRQ_TO_XP70);
|
||||
DBGFS_DUMP(HQVDP_MBX_INFO_HOST);
|
||||
DBGFS_DUMP(HQVDP_MBX_IRQ_TO_HOST);
|
||||
DBGFS_DUMP(HQVDP_MBX_INFO_XP70);
|
||||
infoxp70 = readl(hqvdp->regs + HQVDP_MBX_INFO_XP70);
|
||||
seq_puts(s, "\tFirmware state: ");
|
||||
if (infoxp70 & INFO_XP70_FW_READY)
|
||||
seq_puts(s, "idle and ready");
|
||||
else if (infoxp70 & INFO_XP70_FW_PROCESSING)
|
||||
seq_puts(s, "processing a picture");
|
||||
else if (infoxp70 & INFO_XP70_FW_INITQUEUES)
|
||||
seq_puts(s, "programming queues");
|
||||
else
|
||||
seq_puts(s, "NOT READY");
|
||||
|
||||
DBGFS_DUMP(HQVDP_MBX_SW_RESET_CTRL);
|
||||
DBGFS_DUMP(HQVDP_MBX_STARTUP_CTRL1);
|
||||
if (readl(hqvdp->regs + HQVDP_MBX_STARTUP_CTRL1)
|
||||
& STARTUP_CTRL1_RST_DONE)
|
||||
seq_puts(s, "\tReset is done");
|
||||
else
|
||||
seq_puts(s, "\tReset is NOT done");
|
||||
DBGFS_DUMP(HQVDP_MBX_STARTUP_CTRL2);
|
||||
if (readl(hqvdp->regs + HQVDP_MBX_STARTUP_CTRL2)
|
||||
& STARTUP_CTRL2_FETCH_EN)
|
||||
seq_puts(s, "\tFetch is enabled");
|
||||
else
|
||||
seq_puts(s, "\tFetch is NOT enabled");
|
||||
DBGFS_DUMP(HQVDP_MBX_GP_STATUS);
|
||||
DBGFS_DUMP(HQVDP_MBX_NEXT_CMD);
|
||||
DBGFS_DUMP(HQVDP_MBX_CURRENT_CMD);
|
||||
DBGFS_DUMP(HQVDP_MBX_SOFT_VSYNC);
|
||||
if (!(readl(hqvdp->regs + HQVDP_MBX_SOFT_VSYNC) & 3))
|
||||
seq_puts(s, "\tHW Vsync");
|
||||
else
|
||||
seq_puts(s, "\tSW Vsync ?!?!");
|
||||
|
||||
/* Last command */
|
||||
cmd = readl(hqvdp->regs + HQVDP_MBX_CURRENT_CMD);
|
||||
cmd_offset = sti_hqvdp_get_curr_cmd(hqvdp);
|
||||
if (cmd_offset == -1) {
|
||||
seq_puts(s, "\n\n Last command: unknown");
|
||||
} else {
|
||||
virt = hqvdp->hqvdp_cmd + cmd_offset;
|
||||
seq_printf(s, "\n\n Last command: address @ 0x%x (0x%p)",
|
||||
cmd, virt);
|
||||
hqvdp_dbg_dump_cmd(s, (struct sti_hqvdp_cmd *)virt);
|
||||
}
|
||||
|
||||
/* Next command */
|
||||
cmd = readl(hqvdp->regs + HQVDP_MBX_NEXT_CMD);
|
||||
cmd_offset = sti_hqvdp_get_next_cmd(hqvdp);
|
||||
if (cmd_offset == -1) {
|
||||
seq_puts(s, "\n\n Next command: unknown");
|
||||
} else {
|
||||
virt = hqvdp->hqvdp_cmd + cmd_offset;
|
||||
seq_printf(s, "\n\n Next command address: @ 0x%x (0x%p)",
|
||||
cmd, virt);
|
||||
hqvdp_dbg_dump_cmd(s, (struct sti_hqvdp_cmd *)virt);
|
||||
}
|
||||
|
||||
seq_puts(s, "\n");
|
||||
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct drm_info_list hqvdp_debugfs_files[] = {
|
||||
{ "hqvdp", hqvdp_dbg_show, 0, NULL },
|
||||
};
|
||||
|
||||
static int hqvdp_debugfs_init(struct sti_hqvdp *hqvdp, struct drm_minor *minor)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hqvdp_debugfs_files); i++)
|
||||
hqvdp_debugfs_files[i].data = hqvdp;
|
||||
|
||||
return drm_debugfs_create_files(hqvdp_debugfs_files,
|
||||
ARRAY_SIZE(hqvdp_debugfs_files),
|
||||
minor->debugfs_root, minor);
|
||||
}
|
||||
|
||||
/**
|
||||
* sti_hqvdp_update_hvsrc
|
||||
* @orient: horizontal or vertical
|
||||
@ -580,7 +813,7 @@ int sti_hqvdp_vtg_cb(struct notifier_block *nb, unsigned long evt, void *data)
|
||||
btm_cmd_offset = sti_hqvdp_get_free_cmd(hqvdp);
|
||||
top_cmd_offest = sti_hqvdp_get_curr_cmd(hqvdp);
|
||||
if ((btm_cmd_offset == -1) || (top_cmd_offest == -1)) {
|
||||
DRM_ERROR("Cannot get cmds, skip btm field\n");
|
||||
DRM_DEBUG_DRIVER("Warning: no cmd, will skip field\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
@ -599,11 +832,12 @@ int sti_hqvdp_vtg_cb(struct notifier_block *nb, unsigned long evt, void *data)
|
||||
writel(hqvdp->hqvdp_cmd_paddr + btm_cmd_offset,
|
||||
hqvdp->regs + HQVDP_MBX_NEXT_CMD);
|
||||
|
||||
hqvdp->curr_field_count++;
|
||||
hqvdp->btm_field_pending = false;
|
||||
|
||||
dev_dbg(hqvdp->dev, "%s Posted command:0x%x\n",
|
||||
__func__, hqvdp->hqvdp_cmd_paddr);
|
||||
|
||||
sti_plane_update_fps(&hqvdp->plane, false, true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -612,19 +846,21 @@ int sti_hqvdp_vtg_cb(struct notifier_block *nb, unsigned long evt, void *data)
|
||||
static void sti_hqvdp_init(struct sti_hqvdp *hqvdp)
|
||||
{
|
||||
int size;
|
||||
dma_addr_t dma_addr;
|
||||
|
||||
hqvdp->vtg_nb.notifier_call = sti_hqvdp_vtg_cb;
|
||||
|
||||
/* Allocate memory for the VDP commands */
|
||||
size = NB_VDP_CMD * sizeof(struct sti_hqvdp_cmd);
|
||||
hqvdp->hqvdp_cmd = dma_alloc_writecombine(hqvdp->dev, size,
|
||||
&hqvdp->hqvdp_cmd_paddr,
|
||||
&dma_addr,
|
||||
GFP_KERNEL | GFP_DMA);
|
||||
if (!hqvdp->hqvdp_cmd) {
|
||||
DRM_ERROR("Failed to allocate memory for VDP cmd\n");
|
||||
return;
|
||||
}
|
||||
|
||||
hqvdp->hqvdp_cmd_paddr = (u32)dma_addr;
|
||||
memset(hqvdp->hqvdp_cmd, 0, size);
|
||||
}
|
||||
|
||||
@ -670,7 +906,7 @@ static void sti_hqvdp_start_xp70(struct sti_hqvdp *hqvdp)
|
||||
DRM_DEBUG_DRIVER("\n");
|
||||
|
||||
if (hqvdp->xp70_initialized) {
|
||||
DRM_INFO("HQVDP XP70 already initialized\n");
|
||||
DRM_DEBUG_DRIVER("HQVDP XP70 already initialized\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -775,6 +1011,94 @@ out:
|
||||
release_firmware(firmware);
|
||||
}
|
||||
|
||||
static int sti_hqvdp_atomic_check(struct drm_plane *drm_plane,
|
||||
struct drm_plane_state *state)
|
||||
{
|
||||
struct sti_plane *plane = to_sti_plane(drm_plane);
|
||||
struct sti_hqvdp *hqvdp = to_sti_hqvdp(plane);
|
||||
struct drm_crtc *crtc = state->crtc;
|
||||
struct drm_framebuffer *fb = state->fb;
|
||||
bool first_prepare = plane->status == STI_PLANE_DISABLED ? true : false;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
struct drm_display_mode *mode;
|
||||
int dst_x, dst_y, dst_w, dst_h;
|
||||
int src_x, src_y, src_w, src_h;
|
||||
|
||||
/* no need for further checks if the plane is being disabled */
|
||||
if (!crtc || !fb)
|
||||
return 0;
|
||||
|
||||
crtc_state = drm_atomic_get_crtc_state(state->state, crtc);
|
||||
mode = &crtc_state->mode;
|
||||
dst_x = state->crtc_x;
|
||||
dst_y = state->crtc_y;
|
||||
dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x);
|
||||
dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y);
|
||||
/* src_x are in 16.16 format */
|
||||
src_x = state->src_x >> 16;
|
||||
src_y = state->src_y >> 16;
|
||||
src_w = state->src_w >> 16;
|
||||
src_h = state->src_h >> 16;
|
||||
|
||||
if (!sti_hqvdp_check_hw_scaling(hqvdp, mode,
|
||||
src_w, src_h,
|
||||
dst_w, dst_h)) {
|
||||
DRM_ERROR("Scaling beyond HW capabilities\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!drm_fb_cma_get_gem_obj(fb, 0)) {
|
||||
DRM_ERROR("Can't get CMA GEM object for fb\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Input / output size
|
||||
* Align to upper even value
|
||||
*/
|
||||
dst_w = ALIGN(dst_w, 2);
|
||||
dst_h = ALIGN(dst_h, 2);
|
||||
|
||||
if ((src_w > MAX_WIDTH) || (src_w < MIN_WIDTH) ||
|
||||
(src_h > MAX_HEIGHT) || (src_h < MIN_HEIGHT) ||
|
||||
(dst_w > MAX_WIDTH) || (dst_w < MIN_WIDTH) ||
|
||||
(dst_h > MAX_HEIGHT) || (dst_h < MIN_HEIGHT)) {
|
||||
DRM_ERROR("Invalid in/out size %dx%d -> %dx%d\n",
|
||||
src_w, src_h,
|
||||
dst_w, dst_h);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (first_prepare) {
|
||||
/* Start HQVDP XP70 coprocessor */
|
||||
sti_hqvdp_start_xp70(hqvdp);
|
||||
|
||||
/* Prevent VTG shutdown */
|
||||
if (clk_prepare_enable(hqvdp->clk_pix_main)) {
|
||||
DRM_ERROR("Failed to prepare/enable pix main clk\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Register VTG Vsync callback to handle bottom fields */
|
||||
if (sti_vtg_register_client(hqvdp->vtg,
|
||||
&hqvdp->vtg_nb,
|
||||
crtc)) {
|
||||
DRM_ERROR("Cannot register VTG notifier\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s)\n",
|
||||
crtc->base.id, sti_mixer_to_str(to_sti_mixer(crtc)),
|
||||
drm_plane->base.id, sti_plane_to_str(plane));
|
||||
DRM_DEBUG_KMS("%s dst=(%dx%d)@(%d,%d) - src=(%dx%d)@(%d,%d)\n",
|
||||
sti_plane_to_str(plane),
|
||||
dst_w, dst_h, dst_x, dst_y,
|
||||
src_w, src_h, src_x, src_y);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sti_hqvdp_atomic_update(struct drm_plane *drm_plane,
|
||||
struct drm_plane_state *oldstate)
|
||||
{
|
||||
@ -782,46 +1106,36 @@ static void sti_hqvdp_atomic_update(struct drm_plane *drm_plane,
|
||||
struct sti_plane *plane = to_sti_plane(drm_plane);
|
||||
struct sti_hqvdp *hqvdp = to_sti_hqvdp(plane);
|
||||
struct drm_crtc *crtc = state->crtc;
|
||||
struct sti_mixer *mixer = to_sti_mixer(crtc);
|
||||
struct drm_framebuffer *fb = state->fb;
|
||||
struct drm_display_mode *mode = &crtc->mode;
|
||||
int dst_x = state->crtc_x;
|
||||
int dst_y = state->crtc_y;
|
||||
int dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x);
|
||||
int dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y);
|
||||
/* src_x are in 16.16 format */
|
||||
int src_x = state->src_x >> 16;
|
||||
int src_y = state->src_y >> 16;
|
||||
int src_w = state->src_w >> 16;
|
||||
int src_h = state->src_h >> 16;
|
||||
bool first_prepare = plane->status == STI_PLANE_DISABLED ? true : false;
|
||||
struct drm_display_mode *mode;
|
||||
int dst_x, dst_y, dst_w, dst_h;
|
||||
int src_x, src_y, src_w, src_h;
|
||||
struct drm_gem_cma_object *cma_obj;
|
||||
struct sti_hqvdp_cmd *cmd;
|
||||
int scale_h, scale_v;
|
||||
int cmd_offset;
|
||||
|
||||
DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s)\n",
|
||||
crtc->base.id, sti_mixer_to_str(mixer),
|
||||
drm_plane->base.id, sti_plane_to_str(plane));
|
||||
DRM_DEBUG_KMS("%s dst=(%dx%d)@(%d,%d) - src=(%dx%d)@(%d,%d)\n",
|
||||
sti_plane_to_str(plane),
|
||||
dst_w, dst_h, dst_x, dst_y,
|
||||
src_w, src_h, src_x, src_y);
|
||||
if (!crtc || !fb)
|
||||
return;
|
||||
|
||||
mode = &crtc->mode;
|
||||
dst_x = state->crtc_x;
|
||||
dst_y = state->crtc_y;
|
||||
dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x);
|
||||
dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y);
|
||||
/* src_x are in 16.16 format */
|
||||
src_x = state->src_x >> 16;
|
||||
src_y = state->src_y >> 16;
|
||||
src_w = state->src_w >> 16;
|
||||
src_h = state->src_h >> 16;
|
||||
|
||||
cmd_offset = sti_hqvdp_get_free_cmd(hqvdp);
|
||||
if (cmd_offset == -1) {
|
||||
DRM_ERROR("No available hqvdp_cmd now\n");
|
||||
DRM_DEBUG_DRIVER("Warning: no cmd, will skip frame\n");
|
||||
return;
|
||||
}
|
||||
cmd = hqvdp->hqvdp_cmd + cmd_offset;
|
||||
|
||||
if (!sti_hqvdp_check_hw_scaling(hqvdp, mode,
|
||||
src_w, src_h,
|
||||
dst_w, dst_h)) {
|
||||
DRM_ERROR("Scaling beyond HW capabilities\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Static parameters, defaulting to progressive mode */
|
||||
cmd->top.config = TOP_CONFIG_PROGRESSIVE;
|
||||
cmd->top.mem_format = TOP_MEM_FORMAT_DFLT;
|
||||
@ -836,10 +1150,6 @@ static void sti_hqvdp_atomic_update(struct drm_plane *drm_plane,
|
||||
cmd->iqi.pxf_conf = IQI_PXF_CONF_DFLT;
|
||||
|
||||
cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
|
||||
if (!cma_obj) {
|
||||
DRM_ERROR("Can't get CMA GEM object for fb\n");
|
||||
return;
|
||||
}
|
||||
|
||||
DRM_DEBUG_DRIVER("drm FB:%d format:%.4s phys@:0x%lx\n", fb->base.id,
|
||||
(char *)&fb->pixel_format,
|
||||
@ -860,16 +1170,6 @@ static void sti_hqvdp_atomic_update(struct drm_plane *drm_plane,
|
||||
dst_w = ALIGN(dst_w, 2);
|
||||
dst_h = ALIGN(dst_h, 2);
|
||||
|
||||
if ((src_w > MAX_WIDTH) || (src_w < MIN_WIDTH) ||
|
||||
(src_h > MAX_HEIGHT) || (src_h < MIN_HEIGHT) ||
|
||||
(dst_w > MAX_WIDTH) || (dst_w < MIN_WIDTH) ||
|
||||
(dst_h > MAX_HEIGHT) || (dst_h < MIN_HEIGHT)) {
|
||||
DRM_ERROR("Invalid in/out size %dx%d -> %dx%d\n",
|
||||
src_w, src_h,
|
||||
dst_w, dst_h);
|
||||
return;
|
||||
}
|
||||
|
||||
cmd->top.input_viewport_size = src_h << 16 | src_w;
|
||||
cmd->top.input_frame_size = src_h << 16 | src_w;
|
||||
cmd->hvsrc.output_picture_size = dst_h << 16 | dst_w;
|
||||
@ -900,30 +1200,9 @@ static void sti_hqvdp_atomic_update(struct drm_plane *drm_plane,
|
||||
scale_v = SCALE_FACTOR * dst_h / src_h;
|
||||
sti_hqvdp_update_hvsrc(HVSRC_VERT, scale_v, &cmd->hvsrc);
|
||||
|
||||
if (first_prepare) {
|
||||
/* Start HQVDP XP70 coprocessor */
|
||||
sti_hqvdp_start_xp70(hqvdp);
|
||||
|
||||
/* Prevent VTG shutdown */
|
||||
if (clk_prepare_enable(hqvdp->clk_pix_main)) {
|
||||
DRM_ERROR("Failed to prepare/enable pix main clk\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Register VTG Vsync callback to handle bottom fields */
|
||||
if (sti_vtg_register_client(hqvdp->vtg,
|
||||
&hqvdp->vtg_nb,
|
||||
crtc)) {
|
||||
DRM_ERROR("Cannot register VTG notifier\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
writel(hqvdp->hqvdp_cmd_paddr + cmd_offset,
|
||||
hqvdp->regs + HQVDP_MBX_NEXT_CMD);
|
||||
|
||||
hqvdp->curr_field_count++;
|
||||
|
||||
/* Interlaced : get ready to display the bottom field at next Vsync */
|
||||
if (fb->flags & DRM_MODE_FB_INTERLACED)
|
||||
hqvdp->btm_field_pending = true;
|
||||
@ -931,6 +1210,8 @@ static void sti_hqvdp_atomic_update(struct drm_plane *drm_plane,
|
||||
dev_dbg(hqvdp->dev, "%s Posted command:0x%x\n",
|
||||
__func__, hqvdp->hqvdp_cmd_paddr + cmd_offset);
|
||||
|
||||
sti_plane_update_fps(plane, true, true);
|
||||
|
||||
plane->status = STI_PLANE_UPDATED;
|
||||
}
|
||||
|
||||
@ -938,7 +1219,6 @@ static void sti_hqvdp_atomic_disable(struct drm_plane *drm_plane,
|
||||
struct drm_plane_state *oldstate)
|
||||
{
|
||||
struct sti_plane *plane = to_sti_plane(drm_plane);
|
||||
struct sti_mixer *mixer = to_sti_mixer(drm_plane->crtc);
|
||||
|
||||
if (!drm_plane->crtc) {
|
||||
DRM_DEBUG_DRIVER("drm plane:%d not enabled\n",
|
||||
@ -947,13 +1227,15 @@ static void sti_hqvdp_atomic_disable(struct drm_plane *drm_plane,
|
||||
}
|
||||
|
||||
DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n",
|
||||
drm_plane->crtc->base.id, sti_mixer_to_str(mixer),
|
||||
drm_plane->crtc->base.id,
|
||||
sti_mixer_to_str(to_sti_mixer(drm_plane->crtc)),
|
||||
drm_plane->base.id, sti_plane_to_str(plane));
|
||||
|
||||
plane->status = STI_PLANE_DISABLING;
|
||||
}
|
||||
|
||||
static const struct drm_plane_helper_funcs sti_hqvdp_helpers_funcs = {
|
||||
.atomic_check = sti_hqvdp_atomic_check,
|
||||
.atomic_update = sti_hqvdp_atomic_update,
|
||||
.atomic_disable = sti_hqvdp_atomic_disable,
|
||||
};
|
||||
@ -983,6 +1265,9 @@ static struct drm_plane *sti_hqvdp_create(struct drm_device *drm_dev,
|
||||
|
||||
sti_plane_init_property(&hqvdp->plane, DRM_PLANE_TYPE_OVERLAY);
|
||||
|
||||
if (hqvdp_debugfs_init(hqvdp, drm_dev->primary))
|
||||
DRM_ERROR("HQVDP debugfs setup failed\n");
|
||||
|
||||
return &hqvdp->plane.drm_plane;
|
||||
}
|
||||
|
||||
|
@ -75,6 +75,145 @@ static inline void sti_mixer_reg_write(struct sti_mixer *mixer,
|
||||
writel(val, mixer->regs + reg_id);
|
||||
}
|
||||
|
||||
#define DBGFS_DUMP(reg) seq_printf(s, "\n %-25s 0x%08X", #reg, \
|
||||
sti_mixer_reg_read(mixer, reg))
|
||||
|
||||
static void mixer_dbg_ctl(struct seq_file *s, int val)
|
||||
{
|
||||
unsigned int i;
|
||||
int count = 0;
|
||||
char *const disp_layer[] = {"BKG", "VID0", "VID1", "GDP0",
|
||||
"GDP1", "GDP2", "GDP3"};
|
||||
|
||||
seq_puts(s, "\tEnabled: ");
|
||||
for (i = 0; i < 7; i++) {
|
||||
if (val & 1) {
|
||||
seq_printf(s, "%s ", disp_layer[i]);
|
||||
count++;
|
||||
}
|
||||
val = val >> 1;
|
||||
}
|
||||
|
||||
val = val >> 2;
|
||||
if (val & 1) {
|
||||
seq_puts(s, "CURS ");
|
||||
count++;
|
||||
}
|
||||
if (!count)
|
||||
seq_puts(s, "Nothing");
|
||||
}
|
||||
|
||||
static void mixer_dbg_crb(struct seq_file *s, int val)
|
||||
{
|
||||
int i;
|
||||
|
||||
seq_puts(s, "\tDepth: ");
|
||||
for (i = 0; i < GAM_MIXER_NB_DEPTH_LEVEL; i++) {
|
||||
switch (val & GAM_DEPTH_MASK_ID) {
|
||||
case GAM_DEPTH_VID0_ID:
|
||||
seq_puts(s, "VID0");
|
||||
break;
|
||||
case GAM_DEPTH_VID1_ID:
|
||||
seq_puts(s, "VID1");
|
||||
break;
|
||||
case GAM_DEPTH_GDP0_ID:
|
||||
seq_puts(s, "GDP0");
|
||||
break;
|
||||
case GAM_DEPTH_GDP1_ID:
|
||||
seq_puts(s, "GDP1");
|
||||
break;
|
||||
case GAM_DEPTH_GDP2_ID:
|
||||
seq_puts(s, "GDP2");
|
||||
break;
|
||||
case GAM_DEPTH_GDP3_ID:
|
||||
seq_puts(s, "GDP3");
|
||||
break;
|
||||
default:
|
||||
seq_puts(s, "---");
|
||||
}
|
||||
|
||||
if (i < GAM_MIXER_NB_DEPTH_LEVEL - 1)
|
||||
seq_puts(s, " < ");
|
||||
val = val >> 3;
|
||||
}
|
||||
}
|
||||
|
||||
static void mixer_dbg_mxn(struct seq_file *s, void *addr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 1; i < 8; i++)
|
||||
seq_printf(s, "-0x%08X", (int)readl(addr + i * 4));
|
||||
}
|
||||
|
||||
static int mixer_dbg_show(struct seq_file *s, void *arg)
|
||||
{
|
||||
struct drm_info_node *node = s->private;
|
||||
struct sti_mixer *mixer = (struct sti_mixer *)node->info_ent->data;
|
||||
struct drm_device *dev = node->minor->dev;
|
||||
int ret;
|
||||
|
||||
ret = mutex_lock_interruptible(&dev->struct_mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
seq_printf(s, "%s: (vaddr = 0x%p)",
|
||||
sti_mixer_to_str(mixer), mixer->regs);
|
||||
|
||||
DBGFS_DUMP(GAM_MIXER_CTL);
|
||||
mixer_dbg_ctl(s, sti_mixer_reg_read(mixer, GAM_MIXER_CTL));
|
||||
DBGFS_DUMP(GAM_MIXER_BKC);
|
||||
DBGFS_DUMP(GAM_MIXER_BCO);
|
||||
DBGFS_DUMP(GAM_MIXER_BCS);
|
||||
DBGFS_DUMP(GAM_MIXER_AVO);
|
||||
DBGFS_DUMP(GAM_MIXER_AVS);
|
||||
DBGFS_DUMP(GAM_MIXER_CRB);
|
||||
mixer_dbg_crb(s, sti_mixer_reg_read(mixer, GAM_MIXER_CRB));
|
||||
DBGFS_DUMP(GAM_MIXER_ACT);
|
||||
DBGFS_DUMP(GAM_MIXER_MBP);
|
||||
DBGFS_DUMP(GAM_MIXER_MX0);
|
||||
mixer_dbg_mxn(s, mixer->regs + GAM_MIXER_MX0);
|
||||
seq_puts(s, "\n");
|
||||
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct drm_info_list mixer0_debugfs_files[] = {
|
||||
{ "mixer_main", mixer_dbg_show, 0, NULL },
|
||||
};
|
||||
|
||||
static struct drm_info_list mixer1_debugfs_files[] = {
|
||||
{ "mixer_aux", mixer_dbg_show, 0, NULL },
|
||||
};
|
||||
|
||||
static int mixer_debugfs_init(struct sti_mixer *mixer, struct drm_minor *minor)
|
||||
{
|
||||
unsigned int i;
|
||||
struct drm_info_list *mixer_debugfs_files;
|
||||
int nb_files;
|
||||
|
||||
switch (mixer->id) {
|
||||
case STI_MIXER_MAIN:
|
||||
mixer_debugfs_files = mixer0_debugfs_files;
|
||||
nb_files = ARRAY_SIZE(mixer0_debugfs_files);
|
||||
break;
|
||||
case STI_MIXER_AUX:
|
||||
mixer_debugfs_files = mixer1_debugfs_files;
|
||||
nb_files = ARRAY_SIZE(mixer1_debugfs_files);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < nb_files; i++)
|
||||
mixer_debugfs_files[i].data = mixer;
|
||||
|
||||
return drm_debugfs_create_files(mixer_debugfs_files,
|
||||
nb_files,
|
||||
minor->debugfs_root, minor);
|
||||
}
|
||||
|
||||
void sti_mixer_set_background_status(struct sti_mixer *mixer, bool enable)
|
||||
{
|
||||
u32 val = sti_mixer_reg_read(mixer, GAM_MIXER_CTL);
|
||||
@ -237,7 +376,9 @@ void sti_mixer_set_matrix(struct sti_mixer *mixer)
|
||||
mixerColorSpaceMatIdentity[i]);
|
||||
}
|
||||
|
||||
struct sti_mixer *sti_mixer_create(struct device *dev, int id,
|
||||
struct sti_mixer *sti_mixer_create(struct device *dev,
|
||||
struct drm_device *drm_dev,
|
||||
int id,
|
||||
void __iomem *baseaddr)
|
||||
{
|
||||
struct sti_mixer *mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
|
||||
@ -258,5 +399,8 @@ struct sti_mixer *sti_mixer_create(struct device *dev, int id,
|
||||
DRM_DEBUG_DRIVER("%s created. Regs=%p\n",
|
||||
sti_mixer_to_str(mixer), mixer->regs);
|
||||
|
||||
if (mixer_debugfs_init(mixer, drm_dev->primary))
|
||||
DRM_ERROR("MIXER debugfs setup failed\n");
|
||||
|
||||
return mixer;
|
||||
}
|
||||
|
@ -42,7 +42,9 @@ struct sti_mixer {
|
||||
|
||||
const char *sti_mixer_to_str(struct sti_mixer *mixer);
|
||||
|
||||
struct sti_mixer *sti_mixer_create(struct device *dev, int id,
|
||||
struct sti_mixer *sti_mixer_create(struct device *dev,
|
||||
struct drm_device *drm_dev,
|
||||
int id,
|
||||
void __iomem *baseaddr);
|
||||
|
||||
int sti_mixer_set_plane_status(struct sti_mixer *mixer,
|
||||
|
@ -43,6 +43,69 @@ const char *sti_plane_to_str(struct sti_plane *plane)
|
||||
}
|
||||
}
|
||||
|
||||
#define STI_FPS_INTERVAL_MS 3000
|
||||
|
||||
static int sti_plane_timespec_ms_diff(struct timespec lhs, struct timespec rhs)
|
||||
{
|
||||
struct timespec tmp_ts = timespec_sub(lhs, rhs);
|
||||
u64 tmp_ns = (u64)timespec_to_ns(&tmp_ts);
|
||||
|
||||
do_div(tmp_ns, NSEC_PER_MSEC);
|
||||
|
||||
return (u32)tmp_ns;
|
||||
}
|
||||
|
||||
void sti_plane_update_fps(struct sti_plane *plane,
|
||||
bool new_frame,
|
||||
bool new_field)
|
||||
{
|
||||
struct timespec now;
|
||||
struct sti_fps_info *fps;
|
||||
int fpks, fipks, ms_since_last, num_frames, num_fields;
|
||||
|
||||
getrawmonotonic(&now);
|
||||
|
||||
/* Compute number of frame updates */
|
||||
fps = &plane->fps_info;
|
||||
|
||||
if (new_field)
|
||||
fps->curr_field_counter++;
|
||||
|
||||
/* do not perform fps calcul if new_frame is false */
|
||||
if (!new_frame)
|
||||
return;
|
||||
|
||||
fps->curr_frame_counter++;
|
||||
ms_since_last = sti_plane_timespec_ms_diff(now, fps->last_timestamp);
|
||||
num_frames = fps->curr_frame_counter - fps->last_frame_counter;
|
||||
|
||||
if (num_frames <= 0 || ms_since_last < STI_FPS_INTERVAL_MS)
|
||||
return;
|
||||
|
||||
fps->last_timestamp = now;
|
||||
fps->last_frame_counter = fps->curr_frame_counter;
|
||||
fpks = (num_frames * 1000000) / ms_since_last;
|
||||
snprintf(plane->fps_info.fps_str, FPS_LENGTH, "%-6s @ %d.%.3d fps",
|
||||
sti_plane_to_str(plane), fpks / 1000, fpks % 1000);
|
||||
|
||||
if (fps->curr_field_counter) {
|
||||
/* Compute number of field updates */
|
||||
num_fields = fps->curr_field_counter - fps->last_field_counter;
|
||||
fps->last_field_counter = fps->curr_field_counter;
|
||||
fipks = (num_fields * 1000000) / ms_since_last;
|
||||
snprintf(plane->fps_info.fips_str,
|
||||
FPS_LENGTH, " - %d.%.3d field/sec",
|
||||
fipks / 1000, fipks % 1000);
|
||||
} else {
|
||||
plane->fps_info.fips_str[0] = '\0';
|
||||
}
|
||||
|
||||
if (fps->output)
|
||||
DRM_INFO("%s%s\n",
|
||||
plane->fps_info.fps_str,
|
||||
plane->fps_info.fips_str);
|
||||
}
|
||||
|
||||
static void sti_plane_destroy(struct drm_plane *drm_plane)
|
||||
{
|
||||
DRM_DEBUG_DRIVER("\n");
|
||||
|
@ -50,6 +50,18 @@ enum sti_plane_status {
|
||||
STI_PLANE_DISABLED,
|
||||
};
|
||||
|
||||
#define FPS_LENGTH 64
|
||||
struct sti_fps_info {
|
||||
bool output;
|
||||
unsigned int curr_frame_counter;
|
||||
unsigned int last_frame_counter;
|
||||
unsigned int curr_field_counter;
|
||||
unsigned int last_field_counter;
|
||||
struct timespec last_timestamp;
|
||||
char fps_str[FPS_LENGTH];
|
||||
char fips_str[FPS_LENGTH];
|
||||
};
|
||||
|
||||
/**
|
||||
* STI plane structure
|
||||
*
|
||||
@ -57,15 +69,20 @@ enum sti_plane_status {
|
||||
* @desc: plane type & id
|
||||
* @status: to know the status of the plane
|
||||
* @zorder: plane z-order
|
||||
* @fps_info: frame per second info
|
||||
*/
|
||||
struct sti_plane {
|
||||
struct drm_plane drm_plane;
|
||||
enum sti_plane_desc desc;
|
||||
enum sti_plane_status status;
|
||||
int zorder;
|
||||
struct sti_fps_info fps_info;
|
||||
};
|
||||
|
||||
const char *sti_plane_to_str(struct sti_plane *plane);
|
||||
void sti_plane_update_fps(struct sti_plane *plane,
|
||||
bool new_frame,
|
||||
bool new_field);
|
||||
void sti_plane_init_property(struct sti_plane *plane,
|
||||
enum drm_plane_type type);
|
||||
#endif
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
|
||||
#include "sti_crtc.h"
|
||||
#include "sti_vtg.h"
|
||||
|
||||
/* glue registers */
|
||||
#define TVO_CSC_MAIN_M0 0x000
|
||||
@ -85,19 +86,7 @@
|
||||
#define TVO_VIP_SEL_INPUT_BYPASSED 1
|
||||
|
||||
#define TVO_SYNC_MAIN_VTG_SET_REF 0x00
|
||||
#define TVO_SYNC_MAIN_VTG_SET_1 0x01
|
||||
#define TVO_SYNC_MAIN_VTG_SET_2 0x02
|
||||
#define TVO_SYNC_MAIN_VTG_SET_3 0x03
|
||||
#define TVO_SYNC_MAIN_VTG_SET_4 0x04
|
||||
#define TVO_SYNC_MAIN_VTG_SET_5 0x05
|
||||
#define TVO_SYNC_MAIN_VTG_SET_6 0x06
|
||||
#define TVO_SYNC_AUX_VTG_SET_REF 0x10
|
||||
#define TVO_SYNC_AUX_VTG_SET_1 0x11
|
||||
#define TVO_SYNC_AUX_VTG_SET_2 0x12
|
||||
#define TVO_SYNC_AUX_VTG_SET_3 0x13
|
||||
#define TVO_SYNC_AUX_VTG_SET_4 0x14
|
||||
#define TVO_SYNC_AUX_VTG_SET_5 0x15
|
||||
#define TVO_SYNC_AUX_VTG_SET_6 0x16
|
||||
|
||||
#define TVO_SYNC_HD_DCS_SHIFT 8
|
||||
|
||||
@ -106,6 +95,8 @@
|
||||
|
||||
#define ENCODER_CRTC_MASK (BIT(0) | BIT(1))
|
||||
|
||||
#define TVO_MIN_HD_HEIGHT 720
|
||||
|
||||
/* enum listing the supported output data format */
|
||||
enum sti_tvout_video_out_type {
|
||||
STI_TVOUT_VIDEO_OUT_RGB,
|
||||
@ -268,6 +259,31 @@ static void tvout_vip_set_in_vid_fmt(struct sti_tvout *tvout,
|
||||
tvout_write(tvout, val, reg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set preformatter matrix
|
||||
*
|
||||
* @tvout: tvout structure
|
||||
* @mode: display mode structure
|
||||
*/
|
||||
static void tvout_preformatter_set_matrix(struct sti_tvout *tvout,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
unsigned int i;
|
||||
const u32 *pf_matrix;
|
||||
|
||||
if (mode->vdisplay >= TVO_MIN_HD_HEIGHT)
|
||||
pf_matrix = rgb_to_ycbcr_709;
|
||||
else
|
||||
pf_matrix = rgb_to_ycbcr_601;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
tvout_write(tvout, *(pf_matrix + i),
|
||||
TVO_CSC_MAIN_M0 + (i * 4));
|
||||
tvout_write(tvout, *(pf_matrix + i),
|
||||
TVO_CSC_AUX_M0 + (i * 4));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start VIP block for DVO output
|
||||
*
|
||||
@ -280,24 +296,26 @@ 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;
|
||||
int val, tmp;
|
||||
|
||||
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;
|
||||
/* Select the input sync for dvo */
|
||||
tmp = TVO_SYNC_MAIN_VTG_SET_REF | VTG_SYNC_ID_DVO;
|
||||
val = tmp << TVO_SYNC_DVO_PAD_VSYNC_SHIFT;
|
||||
val |= tmp << TVO_SYNC_DVO_PAD_HSYNC_SHIFT;
|
||||
val |= tmp;
|
||||
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;
|
||||
/* Select the input sync for dvo */
|
||||
tmp = TVO_SYNC_AUX_VTG_SET_REF | VTG_SYNC_ID_DVO;
|
||||
val = tmp << TVO_SYNC_DVO_PAD_VSYNC_SHIFT;
|
||||
val |= tmp << TVO_SYNC_DVO_PAD_HSYNC_SHIFT;
|
||||
val |= tmp;
|
||||
tvout_write(tvout, val, TVO_DVO_SYNC_SEL);
|
||||
tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT;
|
||||
}
|
||||
@ -308,9 +326,8 @@ static void tvout_dvo_start(struct sti_tvout *tvout, bool main_path)
|
||||
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 clipping mode */
|
||||
tvout_vip_set_clip_mode(tvout, TVO_VIP_DVO, TVO_VIP_CLIP_DISABLED);
|
||||
|
||||
/* Set round mode (rounded to 8-bit per component) */
|
||||
tvout_vip_set_rnd(tvout, TVO_VIP_DVO, TVO_VIP_RND_8BIT_ROUNDED);
|
||||
@ -345,13 +362,17 @@ static void tvout_hdmi_start(struct sti_tvout *tvout, bool main_path)
|
||||
|
||||
if (main_path) {
|
||||
DRM_DEBUG_DRIVER("main vip for hdmi\n");
|
||||
/* select the input sync for hdmi = VTG set 1 */
|
||||
tvout_write(tvout, TVO_SYNC_MAIN_VTG_SET_1, TVO_HDMI_SYNC_SEL);
|
||||
/* select the input sync for hdmi */
|
||||
tvout_write(tvout,
|
||||
TVO_SYNC_MAIN_VTG_SET_REF | VTG_SYNC_ID_HDMI,
|
||||
TVO_HDMI_SYNC_SEL);
|
||||
tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT;
|
||||
} else {
|
||||
DRM_DEBUG_DRIVER("aux vip for hdmi\n");
|
||||
/* select the input sync for hdmi = VTG set 1 */
|
||||
tvout_write(tvout, TVO_SYNC_AUX_VTG_SET_1, TVO_HDMI_SYNC_SEL);
|
||||
/* select the input sync for hdmi */
|
||||
tvout_write(tvout,
|
||||
TVO_SYNC_AUX_VTG_SET_REF | VTG_SYNC_ID_HDMI,
|
||||
TVO_HDMI_SYNC_SEL);
|
||||
tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT;
|
||||
}
|
||||
|
||||
@ -361,9 +382,8 @@ static void tvout_hdmi_start(struct sti_tvout *tvout, bool main_path)
|
||||
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_HDMI,
|
||||
TVO_VIP_CLIP_LIMITED_RANGE_RGB_Y);
|
||||
/* set clipping mode */
|
||||
tvout_vip_set_clip_mode(tvout, TVO_VIP_HDMI, TVO_VIP_CLIP_DISABLED);
|
||||
|
||||
/* set round mode (rounded to 8-bit per component) */
|
||||
tvout_vip_set_rnd(tvout, TVO_VIP_HDMI, TVO_VIP_RND_8BIT_ROUNDED);
|
||||
@ -397,13 +417,19 @@ static void tvout_hda_start(struct sti_tvout *tvout, bool main_path)
|
||||
dev_dbg(tvout->dev, "%s\n", __func__);
|
||||
|
||||
if (main_path) {
|
||||
val = TVO_SYNC_MAIN_VTG_SET_2 << TVO_SYNC_HD_DCS_SHIFT;
|
||||
val |= TVO_SYNC_MAIN_VTG_SET_3;
|
||||
DRM_DEBUG_DRIVER("main vip for HDF\n");
|
||||
/* Select the input sync for HD analog and HD DCS */
|
||||
val = TVO_SYNC_MAIN_VTG_SET_REF | VTG_SYNC_ID_HDDCS;
|
||||
val = val << TVO_SYNC_HD_DCS_SHIFT;
|
||||
val |= TVO_SYNC_MAIN_VTG_SET_REF | VTG_SYNC_ID_HDF;
|
||||
tvout_write(tvout, val, TVO_HD_SYNC_SEL);
|
||||
tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT;
|
||||
} else {
|
||||
val = TVO_SYNC_AUX_VTG_SET_2 << TVO_SYNC_HD_DCS_SHIFT;
|
||||
val |= TVO_SYNC_AUX_VTG_SET_3;
|
||||
DRM_DEBUG_DRIVER("aux vip for HDF\n");
|
||||
/* Select the input sync for HD analog and HD DCS */
|
||||
val = TVO_SYNC_AUX_VTG_SET_REF | VTG_SYNC_ID_HDDCS;
|
||||
val = val << TVO_SYNC_HD_DCS_SHIFT;
|
||||
val |= TVO_SYNC_AUX_VTG_SET_REF | VTG_SYNC_ID_HDF;
|
||||
tvout_write(tvout, val, TVO_HD_SYNC_SEL);
|
||||
tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT;
|
||||
}
|
||||
@ -414,8 +440,8 @@ static void tvout_hda_start(struct sti_tvout *tvout, bool main_path)
|
||||
TVO_VIP_REORDER_Y_G_SEL,
|
||||
TVO_VIP_REORDER_CB_B_SEL);
|
||||
|
||||
/* set clipping mode (EAV/SAV clipping) */
|
||||
tvout_vip_set_clip_mode(tvout, TVO_VIP_HDF, TVO_VIP_CLIP_EAV_SAV);
|
||||
/* set clipping mode */
|
||||
tvout_vip_set_clip_mode(tvout, TVO_VIP_HDF, TVO_VIP_CLIP_DISABLED);
|
||||
|
||||
/* set round mode (rounded to 10-bit per component) */
|
||||
tvout_vip_set_rnd(tvout, TVO_VIP_HDF, TVO_VIP_RND_10BIT_ROUNDED);
|
||||
@ -436,6 +462,157 @@ static void tvout_hda_start(struct sti_tvout *tvout, bool main_path)
|
||||
tvout_write(tvout, 0, TVO_HD_DAC_CFG_OFF);
|
||||
}
|
||||
|
||||
#define DBGFS_DUMP(reg) seq_printf(s, "\n %-25s 0x%08X", #reg, \
|
||||
readl(tvout->regs + reg))
|
||||
|
||||
static void tvout_dbg_vip(struct seq_file *s, int val)
|
||||
{
|
||||
int r, g, b, tmp, mask;
|
||||
char *const reorder[] = {"Y_G", "Cb_B", "Cr_R"};
|
||||
char *const clipping[] = {"No", "EAV/SAV", "Limited range RGB/Y",
|
||||
"Limited range Cb/Cr", "decided by register"};
|
||||
char *const round[] = {"8-bit", "10-bit", "12-bit"};
|
||||
char *const input_sel[] = {"Main (color matrix enabled)",
|
||||
"Main (color matrix by-passed)",
|
||||
"", "", "", "", "", "",
|
||||
"Aux (color matrix enabled)",
|
||||
"Aux (color matrix by-passed)",
|
||||
"", "", "", "", "", "Force value"};
|
||||
|
||||
seq_puts(s, "\t");
|
||||
mask = TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_R_SHIFT;
|
||||
r = (val & mask) >> TVO_VIP_REORDER_R_SHIFT;
|
||||
mask = TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_G_SHIFT;
|
||||
g = (val & mask) >> TVO_VIP_REORDER_G_SHIFT;
|
||||
mask = TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_B_SHIFT;
|
||||
b = (val & mask) >> TVO_VIP_REORDER_B_SHIFT;
|
||||
seq_printf(s, "%-24s %s->%s %s->%s %s->%s\n", "Reorder:",
|
||||
reorder[r], reorder[TVO_VIP_REORDER_CR_R_SEL],
|
||||
reorder[g], reorder[TVO_VIP_REORDER_Y_G_SEL],
|
||||
reorder[b], reorder[TVO_VIP_REORDER_CB_B_SEL]);
|
||||
seq_puts(s, "\t\t\t\t\t");
|
||||
mask = TVO_VIP_CLIP_MASK << TVO_VIP_CLIP_SHIFT;
|
||||
tmp = (val & mask) >> TVO_VIP_CLIP_SHIFT;
|
||||
seq_printf(s, "%-24s %s\n", "Clipping:", clipping[tmp]);
|
||||
seq_puts(s, "\t\t\t\t\t");
|
||||
mask = TVO_VIP_RND_MASK << TVO_VIP_RND_SHIFT;
|
||||
tmp = (val & mask) >> TVO_VIP_RND_SHIFT;
|
||||
seq_printf(s, "%-24s input data rounded to %s per component\n",
|
||||
"Round:", round[tmp]);
|
||||
seq_puts(s, "\t\t\t\t\t");
|
||||
tmp = (val & TVO_VIP_SEL_INPUT_MASK);
|
||||
seq_printf(s, "%-24s %s", "Input selection:", input_sel[tmp]);
|
||||
}
|
||||
|
||||
static void tvout_dbg_hd_dac_cfg(struct seq_file *s, int val)
|
||||
{
|
||||
seq_printf(s, "\t%-24s %s", "HD DAC:",
|
||||
val & 1 ? "disabled" : "enabled");
|
||||
}
|
||||
|
||||
static int tvout_dbg_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct drm_info_node *node = s->private;
|
||||
struct sti_tvout *tvout = (struct sti_tvout *)node->info_ent->data;
|
||||
struct drm_device *dev = node->minor->dev;
|
||||
struct drm_crtc *crtc;
|
||||
int ret;
|
||||
|
||||
ret = mutex_lock_interruptible(&dev->struct_mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
seq_printf(s, "TVOUT: (vaddr = 0x%p)", tvout->regs);
|
||||
|
||||
seq_puts(s, "\n\n HDMI encoder: ");
|
||||
crtc = tvout->hdmi->crtc;
|
||||
if (crtc) {
|
||||
seq_printf(s, "connected to %s path",
|
||||
sti_crtc_is_main(crtc) ? "main" : "aux");
|
||||
DBGFS_DUMP(TVO_HDMI_SYNC_SEL);
|
||||
DBGFS_DUMP(TVO_VIP_HDMI);
|
||||
tvout_dbg_vip(s, readl(tvout->regs + TVO_VIP_HDMI));
|
||||
} else {
|
||||
seq_puts(s, "disabled");
|
||||
}
|
||||
|
||||
seq_puts(s, "\n\n DVO encoder: ");
|
||||
crtc = tvout->dvo->crtc;
|
||||
if (crtc) {
|
||||
seq_printf(s, "connected to %s path",
|
||||
sti_crtc_is_main(crtc) ? "main" : "aux");
|
||||
DBGFS_DUMP(TVO_DVO_SYNC_SEL);
|
||||
DBGFS_DUMP(TVO_DVO_CONFIG);
|
||||
DBGFS_DUMP(TVO_VIP_DVO);
|
||||
tvout_dbg_vip(s, readl(tvout->regs + TVO_VIP_DVO));
|
||||
} else {
|
||||
seq_puts(s, "disabled");
|
||||
}
|
||||
|
||||
seq_puts(s, "\n\n HDA encoder: ");
|
||||
crtc = tvout->hda->crtc;
|
||||
if (crtc) {
|
||||
seq_printf(s, "connected to %s path",
|
||||
sti_crtc_is_main(crtc) ? "main" : "aux");
|
||||
DBGFS_DUMP(TVO_HD_SYNC_SEL);
|
||||
DBGFS_DUMP(TVO_HD_DAC_CFG_OFF);
|
||||
tvout_dbg_hd_dac_cfg(s,
|
||||
readl(tvout->regs + TVO_HD_DAC_CFG_OFF));
|
||||
DBGFS_DUMP(TVO_VIP_HDF);
|
||||
tvout_dbg_vip(s, readl(tvout->regs + TVO_VIP_HDF));
|
||||
} else {
|
||||
seq_puts(s, "disabled");
|
||||
}
|
||||
|
||||
seq_puts(s, "\n\n main path configuration");
|
||||
DBGFS_DUMP(TVO_CSC_MAIN_M0);
|
||||
DBGFS_DUMP(TVO_CSC_MAIN_M1);
|
||||
DBGFS_DUMP(TVO_CSC_MAIN_M2);
|
||||
DBGFS_DUMP(TVO_CSC_MAIN_M3);
|
||||
DBGFS_DUMP(TVO_CSC_MAIN_M4);
|
||||
DBGFS_DUMP(TVO_CSC_MAIN_M5);
|
||||
DBGFS_DUMP(TVO_CSC_MAIN_M6);
|
||||
DBGFS_DUMP(TVO_CSC_MAIN_M7);
|
||||
DBGFS_DUMP(TVO_MAIN_IN_VID_FORMAT);
|
||||
|
||||
seq_puts(s, "\n\n auxiliary path configuration");
|
||||
DBGFS_DUMP(TVO_CSC_AUX_M0);
|
||||
DBGFS_DUMP(TVO_CSC_AUX_M2);
|
||||
DBGFS_DUMP(TVO_CSC_AUX_M3);
|
||||
DBGFS_DUMP(TVO_CSC_AUX_M4);
|
||||
DBGFS_DUMP(TVO_CSC_AUX_M5);
|
||||
DBGFS_DUMP(TVO_CSC_AUX_M6);
|
||||
DBGFS_DUMP(TVO_CSC_AUX_M7);
|
||||
DBGFS_DUMP(TVO_AUX_IN_VID_FORMAT);
|
||||
seq_puts(s, "\n");
|
||||
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct drm_info_list tvout_debugfs_files[] = {
|
||||
{ "tvout", tvout_dbg_show, 0, NULL },
|
||||
};
|
||||
|
||||
static void tvout_debugfs_exit(struct sti_tvout *tvout, struct drm_minor *minor)
|
||||
{
|
||||
drm_debugfs_remove_files(tvout_debugfs_files,
|
||||
ARRAY_SIZE(tvout_debugfs_files),
|
||||
minor);
|
||||
}
|
||||
|
||||
static int tvout_debugfs_init(struct sti_tvout *tvout, struct drm_minor *minor)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tvout_debugfs_files); i++)
|
||||
tvout_debugfs_files[i].data = tvout;
|
||||
|
||||
return drm_debugfs_create_files(tvout_debugfs_files,
|
||||
ARRAY_SIZE(tvout_debugfs_files),
|
||||
minor->debugfs_root, minor);
|
||||
}
|
||||
|
||||
static void sti_tvout_encoder_dpms(struct drm_encoder *encoder, int mode)
|
||||
{
|
||||
}
|
||||
@ -446,10 +623,6 @@ static void sti_tvout_encoder_mode_set(struct drm_encoder *encoder,
|
||||
{
|
||||
}
|
||||
|
||||
static void sti_tvout_encoder_prepare(struct drm_encoder *encoder)
|
||||
{
|
||||
}
|
||||
|
||||
static void sti_tvout_encoder_destroy(struct drm_encoder *encoder)
|
||||
{
|
||||
struct sti_tvout_encoder *sti_encoder = to_sti_tvout_encoder(encoder);
|
||||
@ -462,10 +635,12 @@ 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)
|
||||
static void sti_dvo_encoder_enable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct sti_tvout *tvout = to_sti_tvout(encoder);
|
||||
|
||||
tvout_preformatter_set_matrix(tvout, &encoder->crtc->mode);
|
||||
|
||||
tvout_dvo_start(tvout, sti_crtc_is_main(encoder->crtc));
|
||||
}
|
||||
|
||||
@ -480,8 +655,7 @@ static void sti_dvo_encoder_disable(struct drm_encoder *encoder)
|
||||
static const struct drm_encoder_helper_funcs sti_dvo_encoder_helper_funcs = {
|
||||
.dpms = sti_tvout_encoder_dpms,
|
||||
.mode_set = sti_tvout_encoder_mode_set,
|
||||
.prepare = sti_tvout_encoder_prepare,
|
||||
.commit = sti_dvo_encoder_commit,
|
||||
.enable = sti_dvo_encoder_enable,
|
||||
.disable = sti_dvo_encoder_disable,
|
||||
};
|
||||
|
||||
@ -512,10 +686,12 @@ sti_tvout_create_dvo_encoder(struct drm_device *dev,
|
||||
return drm_encoder;
|
||||
}
|
||||
|
||||
static void sti_hda_encoder_commit(struct drm_encoder *encoder)
|
||||
static void sti_hda_encoder_enable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct sti_tvout *tvout = to_sti_tvout(encoder);
|
||||
|
||||
tvout_preformatter_set_matrix(tvout, &encoder->crtc->mode);
|
||||
|
||||
tvout_hda_start(tvout, sti_crtc_is_main(encoder->crtc));
|
||||
}
|
||||
|
||||
@ -533,8 +709,7 @@ static void sti_hda_encoder_disable(struct drm_encoder *encoder)
|
||||
static const struct drm_encoder_helper_funcs sti_hda_encoder_helper_funcs = {
|
||||
.dpms = sti_tvout_encoder_dpms,
|
||||
.mode_set = sti_tvout_encoder_mode_set,
|
||||
.prepare = sti_tvout_encoder_prepare,
|
||||
.commit = sti_hda_encoder_commit,
|
||||
.commit = sti_hda_encoder_enable,
|
||||
.disable = sti_hda_encoder_disable,
|
||||
};
|
||||
|
||||
@ -563,10 +738,12 @@ static struct drm_encoder *sti_tvout_create_hda_encoder(struct drm_device *dev,
|
||||
return drm_encoder;
|
||||
}
|
||||
|
||||
static void sti_hdmi_encoder_commit(struct drm_encoder *encoder)
|
||||
static void sti_hdmi_encoder_enable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct sti_tvout *tvout = to_sti_tvout(encoder);
|
||||
|
||||
tvout_preformatter_set_matrix(tvout, &encoder->crtc->mode);
|
||||
|
||||
tvout_hdmi_start(tvout, sti_crtc_is_main(encoder->crtc));
|
||||
}
|
||||
|
||||
@ -581,8 +758,7 @@ static void sti_hdmi_encoder_disable(struct drm_encoder *encoder)
|
||||
static const struct drm_encoder_helper_funcs sti_hdmi_encoder_helper_funcs = {
|
||||
.dpms = sti_tvout_encoder_dpms,
|
||||
.mode_set = sti_tvout_encoder_mode_set,
|
||||
.prepare = sti_tvout_encoder_prepare,
|
||||
.commit = sti_hdmi_encoder_commit,
|
||||
.commit = sti_hdmi_encoder_enable,
|
||||
.disable = sti_hdmi_encoder_disable,
|
||||
};
|
||||
|
||||
@ -628,26 +804,24 @@ static void sti_tvout_destroy_encoders(struct sti_tvout *tvout)
|
||||
if (tvout->hda)
|
||||
drm_encoder_cleanup(tvout->hda);
|
||||
tvout->hda = NULL;
|
||||
|
||||
if (tvout->dvo)
|
||||
drm_encoder_cleanup(tvout->dvo);
|
||||
tvout->dvo = NULL;
|
||||
}
|
||||
|
||||
static int sti_tvout_bind(struct device *dev, struct device *master, void *data)
|
||||
{
|
||||
struct sti_tvout *tvout = dev_get_drvdata(dev);
|
||||
struct drm_device *drm_dev = data;
|
||||
unsigned int i;
|
||||
|
||||
tvout->drm_dev = drm_dev;
|
||||
|
||||
/* set preformatter matrix */
|
||||
for (i = 0; i < 8; i++) {
|
||||
tvout_write(tvout, rgb_to_ycbcr_601[i],
|
||||
TVO_CSC_MAIN_M0 + (i * 4));
|
||||
tvout_write(tvout, rgb_to_ycbcr_601[i],
|
||||
TVO_CSC_AUX_M0 + (i * 4));
|
||||
}
|
||||
|
||||
sti_tvout_create_encoders(drm_dev, tvout);
|
||||
|
||||
if (tvout_debugfs_init(tvout, drm_dev->primary))
|
||||
DRM_ERROR("TVOUT debugfs setup failed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -655,8 +829,11 @@ static void sti_tvout_unbind(struct device *dev, struct device *master,
|
||||
void *data)
|
||||
{
|
||||
struct sti_tvout *tvout = dev_get_drvdata(dev);
|
||||
struct drm_device *drm_dev = data;
|
||||
|
||||
sti_tvout_destroy_encoders(tvout);
|
||||
|
||||
tvout_debugfs_exit(tvout, drm_dev->primary);
|
||||
}
|
||||
|
||||
static const struct component_ops sti_tvout_ops = {
|
||||
|
@ -42,6 +42,104 @@
|
||||
#define VID_MPR1_BT709 0x0AC50000
|
||||
#define VID_MPR2_BT709 0x07150545
|
||||
#define VID_MPR3_BT709 0x00000AE8
|
||||
/* YCbCr to RGB BT709:
|
||||
* R = Y+1.3711Cr
|
||||
* G = Y-0.6992Cr-0.3359Cb
|
||||
* B = Y+1.7344Cb
|
||||
*/
|
||||
#define VID_MPR0_BT601 0x0A800000
|
||||
#define VID_MPR1_BT601 0x0AAF0000
|
||||
#define VID_MPR2_BT601 0x094E0754
|
||||
#define VID_MPR3_BT601 0x00000ADD
|
||||
|
||||
#define VID_MIN_HD_HEIGHT 720
|
||||
|
||||
#define DBGFS_DUMP(reg) seq_printf(s, "\n %-25s 0x%08X", #reg, \
|
||||
readl(vid->regs + reg))
|
||||
|
||||
static void vid_dbg_ctl(struct seq_file *s, int val)
|
||||
{
|
||||
val = val >> 30;
|
||||
seq_puts(s, "\t");
|
||||
|
||||
if (!(val & 1))
|
||||
seq_puts(s, "NOT ");
|
||||
seq_puts(s, "ignored on main mixer - ");
|
||||
|
||||
if (!(val & 2))
|
||||
seq_puts(s, "NOT ");
|
||||
seq_puts(s, "ignored on aux mixer");
|
||||
}
|
||||
|
||||
static void vid_dbg_vpo(struct seq_file *s, int val)
|
||||
{
|
||||
seq_printf(s, "\txdo:%4d\tydo:%4d", val & 0x0FFF, (val >> 16) & 0x0FFF);
|
||||
}
|
||||
|
||||
static void vid_dbg_vps(struct seq_file *s, int val)
|
||||
{
|
||||
seq_printf(s, "\txds:%4d\tyds:%4d", val & 0x0FFF, (val >> 16) & 0x0FFF);
|
||||
}
|
||||
|
||||
static void vid_dbg_mst(struct seq_file *s, int val)
|
||||
{
|
||||
if (val & 1)
|
||||
seq_puts(s, "\tBUFFER UNDERFLOW!");
|
||||
}
|
||||
|
||||
static int vid_dbg_show(struct seq_file *s, void *arg)
|
||||
{
|
||||
struct drm_info_node *node = s->private;
|
||||
struct sti_vid *vid = (struct sti_vid *)node->info_ent->data;
|
||||
struct drm_device *dev = node->minor->dev;
|
||||
int ret;
|
||||
|
||||
ret = mutex_lock_interruptible(&dev->struct_mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
seq_printf(s, "VID: (vaddr= 0x%p)", vid->regs);
|
||||
|
||||
DBGFS_DUMP(VID_CTL);
|
||||
vid_dbg_ctl(s, readl(vid->regs + VID_CTL));
|
||||
DBGFS_DUMP(VID_ALP);
|
||||
DBGFS_DUMP(VID_CLF);
|
||||
DBGFS_DUMP(VID_VPO);
|
||||
vid_dbg_vpo(s, readl(vid->regs + VID_VPO));
|
||||
DBGFS_DUMP(VID_VPS);
|
||||
vid_dbg_vps(s, readl(vid->regs + VID_VPS));
|
||||
DBGFS_DUMP(VID_KEY1);
|
||||
DBGFS_DUMP(VID_KEY2);
|
||||
DBGFS_DUMP(VID_MPR0);
|
||||
DBGFS_DUMP(VID_MPR1);
|
||||
DBGFS_DUMP(VID_MPR2);
|
||||
DBGFS_DUMP(VID_MPR3);
|
||||
DBGFS_DUMP(VID_MST);
|
||||
vid_dbg_mst(s, readl(vid->regs + VID_MST));
|
||||
DBGFS_DUMP(VID_BC);
|
||||
DBGFS_DUMP(VID_TINT);
|
||||
DBGFS_DUMP(VID_CSAT);
|
||||
seq_puts(s, "\n");
|
||||
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct drm_info_list vid_debugfs_files[] = {
|
||||
{ "vid", vid_dbg_show, 0, NULL },
|
||||
};
|
||||
|
||||
static int vid_debugfs_init(struct sti_vid *vid, struct drm_minor *minor)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(vid_debugfs_files); i++)
|
||||
vid_debugfs_files[i].data = vid;
|
||||
|
||||
return drm_debugfs_create_files(vid_debugfs_files,
|
||||
ARRAY_SIZE(vid_debugfs_files),
|
||||
minor->debugfs_root, minor);
|
||||
}
|
||||
|
||||
void sti_vid_commit(struct sti_vid *vid,
|
||||
struct drm_plane_state *state)
|
||||
@ -52,6 +150,7 @@ void sti_vid_commit(struct sti_vid *vid,
|
||||
int dst_y = state->crtc_y;
|
||||
int dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x);
|
||||
int dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y);
|
||||
int src_h = state->src_h >> 16;
|
||||
u32 val, ydo, xdo, yds, xds;
|
||||
|
||||
/* Input / output size
|
||||
@ -71,6 +170,19 @@ void sti_vid_commit(struct sti_vid *vid,
|
||||
|
||||
writel((ydo << 16) | xdo, vid->regs + VID_VPO);
|
||||
writel((yds << 16) | xds, vid->regs + VID_VPS);
|
||||
|
||||
/* Color conversion parameters */
|
||||
if (src_h >= VID_MIN_HD_HEIGHT) {
|
||||
writel(VID_MPR0_BT709, vid->regs + VID_MPR0);
|
||||
writel(VID_MPR1_BT709, vid->regs + VID_MPR1);
|
||||
writel(VID_MPR2_BT709, vid->regs + VID_MPR2);
|
||||
writel(VID_MPR3_BT709, vid->regs + VID_MPR3);
|
||||
} else {
|
||||
writel(VID_MPR0_BT601, vid->regs + VID_MPR0);
|
||||
writel(VID_MPR1_BT601, vid->regs + VID_MPR1);
|
||||
writel(VID_MPR2_BT601, vid->regs + VID_MPR2);
|
||||
writel(VID_MPR3_BT601, vid->regs + VID_MPR3);
|
||||
}
|
||||
}
|
||||
|
||||
void sti_vid_disable(struct sti_vid *vid)
|
||||
@ -91,20 +203,14 @@ static void sti_vid_init(struct sti_vid *vid)
|
||||
/* Opaque */
|
||||
writel(VID_ALP_OPAQUE, vid->regs + VID_ALP);
|
||||
|
||||
/* Color conversion parameters */
|
||||
writel(VID_MPR0_BT709, vid->regs + VID_MPR0);
|
||||
writel(VID_MPR1_BT709, vid->regs + VID_MPR1);
|
||||
writel(VID_MPR2_BT709, vid->regs + VID_MPR2);
|
||||
writel(VID_MPR3_BT709, vid->regs + VID_MPR3);
|
||||
|
||||
/* Brightness, contrast, tint, saturation */
|
||||
writel(VID_BC_DFLT, vid->regs + VID_BC);
|
||||
writel(VID_TINT_DFLT, vid->regs + VID_TINT);
|
||||
writel(VID_CSAT_DFLT, vid->regs + VID_CSAT);
|
||||
}
|
||||
|
||||
struct sti_vid *sti_vid_create(struct device *dev, int id,
|
||||
void __iomem *baseaddr)
|
||||
struct sti_vid *sti_vid_create(struct device *dev, struct drm_device *drm_dev,
|
||||
int id, void __iomem *baseaddr)
|
||||
{
|
||||
struct sti_vid *vid;
|
||||
|
||||
@ -120,5 +226,8 @@ struct sti_vid *sti_vid_create(struct device *dev, int id,
|
||||
|
||||
sti_vid_init(vid);
|
||||
|
||||
if (vid_debugfs_init(vid, drm_dev->primary))
|
||||
DRM_ERROR("VID debugfs setup failed\n");
|
||||
|
||||
return vid;
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ struct sti_vid {
|
||||
void sti_vid_commit(struct sti_vid *vid,
|
||||
struct drm_plane_state *state);
|
||||
void sti_vid_disable(struct sti_vid *vid);
|
||||
struct sti_vid *sti_vid_create(struct device *dev, int id,
|
||||
void __iomem *baseaddr);
|
||||
struct sti_vid *sti_vid_create(struct device *dev, struct drm_device *drm_dev,
|
||||
int id, void __iomem *baseaddr);
|
||||
|
||||
#endif
|
||||
|
@ -15,8 +15,8 @@
|
||||
|
||||
#include "sti_vtg.h"
|
||||
|
||||
#define VTG_TYPE_MASTER 0
|
||||
#define VTG_TYPE_SLAVE_BY_EXT0 1
|
||||
#define VTG_MODE_MASTER 0
|
||||
#define VTG_MODE_SLAVE_BY_EXT0 1
|
||||
|
||||
/* registers offset */
|
||||
#define VTG_MODE 0x0000
|
||||
@ -64,6 +64,9 @@
|
||||
/* Delay introduced by the HDMI in nb of pixel */
|
||||
#define HDMI_DELAY (5)
|
||||
|
||||
/* Delay introduced by the DVO in nb of pixel */
|
||||
#define DVO_DELAY (2)
|
||||
|
||||
/* delay introduced by the Arbitrary Waveform Generator in nb of pixels */
|
||||
#define AWG_DELAY_HD (-9)
|
||||
#define AWG_DELAY_ED (-8)
|
||||
@ -71,13 +74,61 @@
|
||||
|
||||
LIST_HEAD(vtg_lookup);
|
||||
|
||||
/*
|
||||
* STI VTG register offset structure
|
||||
*
|
||||
*@h_hd: stores the VTG_H_HD_x register offset
|
||||
*@top_v_vd: stores the VTG_TOP_V_VD_x register offset
|
||||
*@bot_v_vd: stores the VTG_BOT_V_VD_x register offset
|
||||
*@top_v_hd: stores the VTG_TOP_V_HD_x register offset
|
||||
*@bot_v_hd: stores the VTG_BOT_V_HD_x register offset
|
||||
*/
|
||||
struct sti_vtg_regs_offs {
|
||||
u32 h_hd;
|
||||
u32 top_v_vd;
|
||||
u32 bot_v_vd;
|
||||
u32 top_v_hd;
|
||||
u32 bot_v_hd;
|
||||
};
|
||||
|
||||
#define VTG_MAX_SYNC_OUTPUT 4
|
||||
static const struct sti_vtg_regs_offs vtg_regs_offs[VTG_MAX_SYNC_OUTPUT] = {
|
||||
{ VTG_H_HD_1,
|
||||
VTG_TOP_V_VD_1, VTG_BOT_V_VD_1, VTG_TOP_V_HD_1, VTG_BOT_V_HD_1 },
|
||||
{ VTG_H_HD_2,
|
||||
VTG_TOP_V_VD_2, VTG_BOT_V_VD_2, VTG_TOP_V_HD_2, VTG_BOT_V_HD_2 },
|
||||
{ VTG_H_HD_3,
|
||||
VTG_TOP_V_VD_3, VTG_BOT_V_VD_3, VTG_TOP_V_HD_3, VTG_BOT_V_HD_3 },
|
||||
{ VTG_H_HD_4,
|
||||
VTG_TOP_V_VD_4, VTG_BOT_V_VD_4, VTG_TOP_V_HD_4, VTG_BOT_V_HD_4 }
|
||||
};
|
||||
|
||||
/*
|
||||
* STI VTG synchronisation parameters structure
|
||||
*
|
||||
*@hsync: sample number falling and rising edge
|
||||
*@vsync_line_top: vertical top field line number falling and rising edge
|
||||
*@vsync_line_bot: vertical bottom field line number falling and rising edge
|
||||
*@vsync_off_top: vertical top field sample number rising and falling edge
|
||||
*@vsync_off_bot: vertical bottom field sample number rising and falling edge
|
||||
*/
|
||||
struct sti_vtg_sync_params {
|
||||
u32 hsync;
|
||||
u32 vsync_line_top;
|
||||
u32 vsync_line_bot;
|
||||
u32 vsync_off_top;
|
||||
u32 vsync_off_bot;
|
||||
};
|
||||
|
||||
/**
|
||||
* STI VTG structure
|
||||
*
|
||||
* @dev: pointer to device driver
|
||||
* @data: data associated to the device
|
||||
* @np: device node
|
||||
* @regs: register mapping
|
||||
* @sync_params: synchronisation parameters used to generate timings
|
||||
* @irq: VTG irq
|
||||
* @type: VTG type (main or aux)
|
||||
* @irq_status: store the IRQ status value
|
||||
* @notifier_list: notifier callback
|
||||
* @crtc: the CRTC for vblank event
|
||||
* @slave: slave vtg
|
||||
@ -87,6 +138,7 @@ struct sti_vtg {
|
||||
struct device *dev;
|
||||
struct device_node *np;
|
||||
void __iomem *regs;
|
||||
struct sti_vtg_sync_params sync_params[VTG_MAX_SYNC_OUTPUT];
|
||||
int irq;
|
||||
u32 irq_status;
|
||||
struct raw_notifier_head notifier_list;
|
||||
@ -146,13 +198,69 @@ static void vtg_set_output_window(void __iomem *regs,
|
||||
writel(video_bottom_field_stop, regs + VTG_VID_BFS);
|
||||
}
|
||||
|
||||
static void vtg_set_mode(struct sti_vtg *vtg,
|
||||
int type, const struct drm_display_mode *mode)
|
||||
static void vtg_set_hsync_vsync_pos(struct sti_vtg_sync_params *sync,
|
||||
int delay,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
u32 tmp;
|
||||
long clocksperline, start, stop;
|
||||
u32 risesync_top, fallsync_top;
|
||||
u32 risesync_offs_top, fallsync_offs_top;
|
||||
|
||||
clocksperline = mode->htotal;
|
||||
|
||||
/* Get the hsync position */
|
||||
start = 0;
|
||||
stop = mode->hsync_end - mode->hsync_start;
|
||||
|
||||
start += delay;
|
||||
stop += delay;
|
||||
|
||||
if (start < 0)
|
||||
start += clocksperline;
|
||||
else if (start >= clocksperline)
|
||||
start -= clocksperline;
|
||||
|
||||
if (stop < 0)
|
||||
stop += clocksperline;
|
||||
else if (stop >= clocksperline)
|
||||
stop -= clocksperline;
|
||||
|
||||
sync->hsync = (stop << 16) | start;
|
||||
|
||||
/* Get the vsync position */
|
||||
if (delay >= 0) {
|
||||
risesync_top = 1;
|
||||
fallsync_top = risesync_top;
|
||||
fallsync_top += mode->vsync_end - mode->vsync_start;
|
||||
|
||||
fallsync_offs_top = (u32)delay;
|
||||
risesync_offs_top = (u32)delay;
|
||||
} else {
|
||||
risesync_top = mode->vtotal;
|
||||
fallsync_top = mode->vsync_end - mode->vsync_start;
|
||||
|
||||
fallsync_offs_top = clocksperline + delay;
|
||||
risesync_offs_top = clocksperline + delay;
|
||||
}
|
||||
|
||||
sync->vsync_line_top = (fallsync_top << 16) | risesync_top;
|
||||
sync->vsync_off_top = (fallsync_offs_top << 16) | risesync_offs_top;
|
||||
|
||||
/* Only progressive supported for now */
|
||||
sync->vsync_line_bot = sync->vsync_line_top;
|
||||
sync->vsync_off_bot = sync->vsync_off_top;
|
||||
}
|
||||
|
||||
static void vtg_set_mode(struct sti_vtg *vtg,
|
||||
int type,
|
||||
struct sti_vtg_sync_params *sync,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (vtg->slave)
|
||||
vtg_set_mode(vtg->slave, VTG_TYPE_SLAVE_BY_EXT0, mode);
|
||||
vtg_set_mode(vtg->slave, VTG_MODE_SLAVE_BY_EXT0,
|
||||
vtg->sync_params, mode);
|
||||
|
||||
/* Set the number of clock cycles per line */
|
||||
writel(mode->htotal, vtg->regs + VTG_CLKLN);
|
||||
@ -163,57 +271,31 @@ static void vtg_set_mode(struct sti_vtg *vtg,
|
||||
/* Program output window */
|
||||
vtg_set_output_window(vtg->regs, mode);
|
||||
|
||||
/* prepare VTG set 1 for HDMI */
|
||||
tmp = (mode->hsync_end - mode->hsync_start + HDMI_DELAY) << 16;
|
||||
tmp |= HDMI_DELAY;
|
||||
writel(tmp, vtg->regs + VTG_H_HD_1);
|
||||
/* Set hsync and vsync position for HDMI */
|
||||
vtg_set_hsync_vsync_pos(&sync[VTG_SYNC_ID_HDMI - 1], HDMI_DELAY, mode);
|
||||
|
||||
tmp = (mode->vsync_end - mode->vsync_start + 1) << 16;
|
||||
tmp |= 1;
|
||||
writel(tmp, vtg->regs + VTG_TOP_V_VD_1);
|
||||
writel(tmp, vtg->regs + VTG_BOT_V_VD_1);
|
||||
/* Set hsync and vsync position for HD DCS */
|
||||
vtg_set_hsync_vsync_pos(&sync[VTG_SYNC_ID_HDDCS - 1], 0, mode);
|
||||
|
||||
tmp = HDMI_DELAY << 16;
|
||||
tmp |= HDMI_DELAY;
|
||||
writel(tmp, vtg->regs + VTG_TOP_V_HD_1);
|
||||
writel(tmp, vtg->regs + VTG_BOT_V_HD_1);
|
||||
/* Set hsync and vsync position for HDF */
|
||||
vtg_set_hsync_vsync_pos(&sync[VTG_SYNC_ID_HDF - 1], AWG_DELAY_HD, mode);
|
||||
|
||||
/* prepare VTG set 2 for for HD DCS */
|
||||
tmp = (mode->hsync_end - mode->hsync_start) << 16;
|
||||
writel(tmp, vtg->regs + VTG_H_HD_2);
|
||||
/* Set hsync and vsync position for DVO */
|
||||
vtg_set_hsync_vsync_pos(&sync[VTG_SYNC_ID_DVO - 1], DVO_DELAY, mode);
|
||||
|
||||
tmp = (mode->vsync_end - mode->vsync_start + 1) << 16;
|
||||
tmp |= 1;
|
||||
writel(tmp, vtg->regs + VTG_TOP_V_VD_2);
|
||||
writel(tmp, vtg->regs + VTG_BOT_V_VD_2);
|
||||
writel(0, vtg->regs + VTG_TOP_V_HD_2);
|
||||
writel(0, vtg->regs + VTG_BOT_V_HD_2);
|
||||
|
||||
/* prepare VTG set 3 for HD Analog in HD mode */
|
||||
tmp = (mode->hsync_end - mode->hsync_start + AWG_DELAY_HD) << 16;
|
||||
tmp |= mode->htotal + AWG_DELAY_HD;
|
||||
writel(tmp, vtg->regs + VTG_H_HD_3);
|
||||
|
||||
tmp = (mode->vsync_end - mode->vsync_start) << 16;
|
||||
tmp |= mode->vtotal;
|
||||
writel(tmp, vtg->regs + VTG_TOP_V_VD_3);
|
||||
writel(tmp, vtg->regs + VTG_BOT_V_VD_3);
|
||||
|
||||
tmp = (mode->htotal + AWG_DELAY_HD) << 16;
|
||||
tmp |= mode->htotal + AWG_DELAY_HD;
|
||||
writel(tmp, vtg->regs + VTG_TOP_V_HD_3);
|
||||
writel(tmp, vtg->regs + VTG_BOT_V_HD_3);
|
||||
|
||||
/* Prepare VTG set 4 for DVO */
|
||||
tmp = (mode->hsync_end - mode->hsync_start) << 16;
|
||||
writel(tmp, vtg->regs + VTG_H_HD_4);
|
||||
|
||||
tmp = (mode->vsync_end - mode->vsync_start + 1) << 16;
|
||||
tmp |= 1;
|
||||
writel(tmp, vtg->regs + VTG_TOP_V_VD_4);
|
||||
writel(tmp, vtg->regs + VTG_BOT_V_VD_4);
|
||||
writel(0, vtg->regs + VTG_TOP_V_HD_4);
|
||||
writel(0, vtg->regs + VTG_BOT_V_HD_4);
|
||||
/* Progam the syncs outputs */
|
||||
for (i = 0; i < VTG_MAX_SYNC_OUTPUT ; i++) {
|
||||
writel(sync[i].hsync,
|
||||
vtg->regs + vtg_regs_offs[i].h_hd);
|
||||
writel(sync[i].vsync_line_top,
|
||||
vtg->regs + vtg_regs_offs[i].top_v_vd);
|
||||
writel(sync[i].vsync_line_bot,
|
||||
vtg->regs + vtg_regs_offs[i].bot_v_vd);
|
||||
writel(sync[i].vsync_off_top,
|
||||
vtg->regs + vtg_regs_offs[i].top_v_hd);
|
||||
writel(sync[i].vsync_off_bot,
|
||||
vtg->regs + vtg_regs_offs[i].bot_v_hd);
|
||||
}
|
||||
|
||||
/* mode */
|
||||
writel(type, vtg->regs + VTG_MODE);
|
||||
@ -231,7 +313,7 @@ void sti_vtg_set_config(struct sti_vtg *vtg,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
/* write configuration */
|
||||
vtg_set_mode(vtg, VTG_TYPE_MASTER, mode);
|
||||
vtg_set_mode(vtg, VTG_MODE_MASTER, vtg->sync_params, mode);
|
||||
|
||||
vtg_reset(vtg);
|
||||
|
||||
|
@ -10,6 +10,11 @@
|
||||
#define VTG_TOP_FIELD_EVENT 1
|
||||
#define VTG_BOTTOM_FIELD_EVENT 2
|
||||
|
||||
#define VTG_SYNC_ID_HDMI 1
|
||||
#define VTG_SYNC_ID_HDDCS 2
|
||||
#define VTG_SYNC_ID_HDF 3
|
||||
#define VTG_SYNC_ID_DVO 4
|
||||
|
||||
struct sti_vtg;
|
||||
struct drm_display_mode;
|
||||
struct notifier_block;
|
||||
|
Loading…
Reference in New Issue
Block a user