blob: a0a94ce0f47d4bab9b726fd255ebd42d046843d0 [file] [log] [blame]
/*
* Copyright (C) 2016 Freescale Semiconductor, Inc.
* Copyright 2017 NXP
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include <linux/io.h>
#include <linux/media-bus-format.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <video/dpu.h>
#include "dpu-prv.h"
#define SSQCNTS 0
#define SSQCYCLE 0x8
#define SWRESET 0xC
#define TCON_CTRL 0x10
#define BYPASS BIT(3)
#define RSDSINVCTRL 0x14
#define MAPBIT3_0 0x18
#define MAPBIT7_4 0x1C
#define MAPBIT11_8 0x20
#define MAPBIT15_12 0x24
#define MAPBIT19_16 0x28
#define MAPBIT23_20 0x2C
#define MAPBIT27_24 0x30
#define MAPBIT31_28 0x34
#define MAPBIT34_32 0x38
#define MAPBIT3_0_DUAL 0x3C
#define MAPBIT7_4_DUAL 0x40
#define MAPBIT11_8_DUAL 0x44
#define MAPBIT15_12_DUAL 0x48
#define MAPBIT19_16_DUAL 0x4C
#define MAPBIT23_20_DUAL 0x50
#define MAPBIT27_24_DUAL 0x54
#define MAPBIT31_28_DUAL 0x58
#define MAPBIT34_32_DUAL 0x5C
#define SPGPOSON(n) (0x60 + (n) * 16)
#define X(n) (((n) & 0x7FFF) << 16)
#define Y(n) ((n) & 0x7FFF)
#define SPGMASKON(n) (0x64 + (n) * 16)
#define SPGPOSOFF(n) (0x68 + (n) * 16)
#define SPGMASKOFF(n) (0x6C + (n) * 16)
#define SMXSIGS(n) (0x120 + (n) * 8)
#define SMXFCTTABLE(n) (0x124 + (n) * 8)
#define RESET_OVER_UNFERFLOW 0x180
#define DUAL_DEBUG 0x184
struct dpu_tcon {
void __iomem *base;
struct mutex mutex;
int id;
bool inuse;
struct dpu_soc *dpu;
};
static inline u32 dpu_tcon_read(struct dpu_tcon *tcon, unsigned int offset)
{
return readl(tcon->base + offset);
}
static inline void dpu_tcon_write(struct dpu_tcon *tcon, u32 value,
unsigned int offset)
{
writel(value, tcon->base + offset);
}
int tcon_set_fmt(struct dpu_tcon *tcon, u32 bus_format)
{
mutex_lock(&tcon->mutex);
switch (bus_format) {
case MEDIA_BUS_FMT_RGB888_1X24:
dpu_tcon_write(tcon, 0x19181716, MAPBIT3_0);
dpu_tcon_write(tcon, 0x1d1c1b1a, MAPBIT7_4);
dpu_tcon_write(tcon, 0x0f0e0d0c, MAPBIT11_8);
dpu_tcon_write(tcon, 0x13121110, MAPBIT15_12);
dpu_tcon_write(tcon, 0x05040302, MAPBIT19_16);
dpu_tcon_write(tcon, 0x09080706, MAPBIT23_20);
break;
case MEDIA_BUS_FMT_RGB101010_1X30:
case MEDIA_BUS_FMT_RGB888_1X30_PADLO:
case MEDIA_BUS_FMT_RGB666_1X30_PADLO:
dpu_tcon_write(tcon, 0x17161514, MAPBIT3_0);
dpu_tcon_write(tcon, 0x1b1a1918, MAPBIT7_4);
dpu_tcon_write(tcon, 0x0b0a1d1c, MAPBIT11_8);
dpu_tcon_write(tcon, 0x0f0e0d0c, MAPBIT15_12);
dpu_tcon_write(tcon, 0x13121110, MAPBIT19_16);
dpu_tcon_write(tcon, 0x03020100, MAPBIT23_20);
dpu_tcon_write(tcon, 0x07060504, MAPBIT27_24);
dpu_tcon_write(tcon, 0x00000908, MAPBIT31_28);
break;
default:
mutex_unlock(&tcon->mutex);
return -EINVAL;
}
mutex_unlock(&tcon->mutex);
return 0;
}
EXPORT_SYMBOL_GPL(tcon_set_fmt);
/* This function is used to workaround TKT320590 which is related to DPR/PRG. */
void tcon_set_operation_mode(struct dpu_tcon *tcon)
{
u32 val;
mutex_lock(&tcon->mutex);
val = dpu_tcon_read(tcon, TCON_CTRL);
val &= ~BYPASS;
dpu_tcon_write(tcon, val, TCON_CTRL);
mutex_unlock(&tcon->mutex);
}
EXPORT_SYMBOL_GPL(tcon_set_operation_mode);
void tcon_cfg_videomode(struct dpu_tcon *tcon, struct drm_display_mode *m)
{
u32 val;
mutex_lock(&tcon->mutex);
/*
* TKT320590:
* Turn TCON into operation mode later after the first dumb frame is
* generated by DPU. This makes DPR/PRG be able to evade the frame.
*/
val = dpu_tcon_read(tcon, TCON_CTRL);
val |= BYPASS;
dpu_tcon_write(tcon, val, TCON_CTRL);
/* dsp_control[0]: hsync */
dpu_tcon_write(tcon, X(m->hsync_start), SPGPOSON(0));
dpu_tcon_write(tcon, 0xffff, SPGMASKON(0));
dpu_tcon_write(tcon, X(m->hsync_end), SPGPOSOFF(0));
dpu_tcon_write(tcon, 0xffff, SPGMASKOFF(0));
dpu_tcon_write(tcon, 0x2, SMXSIGS(0));
dpu_tcon_write(tcon, 0x1, SMXFCTTABLE(0));
/* dsp_control[1]: vsync */
dpu_tcon_write(tcon, X(m->hsync_start) | Y(m->vsync_start - 1),
SPGPOSON(1));
dpu_tcon_write(tcon, 0x0, SPGMASKON(1));
dpu_tcon_write(tcon, X(m->hsync_start) | Y(m->vsync_end - 1),
SPGPOSOFF(1));
dpu_tcon_write(tcon, 0x0, SPGMASKOFF(1));
dpu_tcon_write(tcon, 0x3, SMXSIGS(1));
dpu_tcon_write(tcon, 0x1, SMXFCTTABLE(1));
/* dsp_control[2]: data enable */
/* horizontal */
dpu_tcon_write(tcon, 0x0, SPGPOSON(2));
dpu_tcon_write(tcon, 0xffff, SPGMASKON(2));
dpu_tcon_write(tcon, X(m->hdisplay), SPGPOSOFF(2));
dpu_tcon_write(tcon, 0xffff, SPGMASKOFF(2));
/* vertical */
dpu_tcon_write(tcon, 0x0, SPGPOSON(3));
dpu_tcon_write(tcon, 0x7fff0000, SPGMASKON(3));
dpu_tcon_write(tcon, Y(m->vdisplay), SPGPOSOFF(3));
dpu_tcon_write(tcon, 0x7fff0000, SPGMASKOFF(3));
dpu_tcon_write(tcon, 0x2c, SMXSIGS(2));
dpu_tcon_write(tcon, 0x8, SMXFCTTABLE(2));
/* dsp_control[3]: kachuck */
dpu_tcon_write(tcon, X(0xa) | Y(m->vdisplay), SPGPOSON(4));
dpu_tcon_write(tcon, 0x0, SPGMASKON(4));
dpu_tcon_write(tcon, X(0x2a) | Y(m->vdisplay), SPGPOSOFF(4));
dpu_tcon_write(tcon, 0x0, SPGMASKOFF(4));
dpu_tcon_write(tcon, 0x6, SMXSIGS(3));
dpu_tcon_write(tcon, 0x2, SMXFCTTABLE(3));
mutex_unlock(&tcon->mutex);
}
EXPORT_SYMBOL_GPL(tcon_cfg_videomode);
struct dpu_tcon *dpu_tcon_get(struct dpu_soc *dpu, int id)
{
struct dpu_tcon *tcon;
int i;
for (i = 0; i < ARRAY_SIZE(tcon_ids); i++)
if (tcon_ids[i] == id)
break;
if (i == ARRAY_SIZE(tcon_ids))
return ERR_PTR(-EINVAL);
tcon = dpu->tcon_priv[i];
mutex_lock(&tcon->mutex);
if (tcon->inuse) {
tcon = ERR_PTR(-EBUSY);
goto out;
}
tcon->inuse = true;
out:
mutex_unlock(&tcon->mutex);
return tcon;
}
EXPORT_SYMBOL_GPL(dpu_tcon_get);
void dpu_tcon_put(struct dpu_tcon *tcon)
{
mutex_lock(&tcon->mutex);
tcon->inuse = false;
mutex_unlock(&tcon->mutex);
}
EXPORT_SYMBOL_GPL(dpu_tcon_put);
void _dpu_tcon_init(struct dpu_soc *dpu, unsigned int id)
{
}
int dpu_tcon_init(struct dpu_soc *dpu, unsigned int id,
unsigned long unused, unsigned long base)
{
struct dpu_tcon *tcon;
tcon = devm_kzalloc(dpu->dev, sizeof(*tcon), GFP_KERNEL);
if (!tcon)
return -ENOMEM;
dpu->tcon_priv[id] = tcon;
tcon->base = devm_ioremap(dpu->dev, base, SZ_512);
if (!tcon->base)
return -ENOMEM;
tcon->dpu = dpu;
mutex_init(&tcon->mutex);
return 0;
}