- Rework vblank handling
   . This patch series adds frame counter callback and removes
     unnecessary pipe relevnt fields and simplifies event handling.
 - clean up and fix up sw-trigger relevant code
   . This patch series moves TE relevant code from Panel and HDMI
     to DECON driver to fix a race between interrupt handlers and
     DECON disable, and to fix timeout issue at wait-for-vblank.
   . It removes unnecessary flags and check code specific to Exynos driver.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJZNPyAAAoJEFc4NIkMQxK4haQP/jqsPKffmy54xOmTssR587cA
 e6pCCJVDnRXmCSf504pMpHJNPq+7k5dCbv5r4eEqyFmJd9g4+B94+/4MOXSoy/bS
 6GMcWFRPxmmfdCZSUJKBQS+OoGIT0S+AFLkME+PouIXSDX9uE59cQwMeRzP0oflB
 5wUrTPFIYygt6hQp1JH+ofQUEj5d/gu7O7xJWe4MPZ2Xd+j7K97J4wpVKykUiAG0
 BS+ELCUJhSFO/LZMb9/sPCdhhIjQCQ61+8H0Ep0ciRDjIFb31jmLrMSvPI+c7C2r
 EEEkvFKVLoMJzI4+sOuD7O56mOiGZW2RQXXZXTTPVPv+2EeREv4ATUb0fd4Ep2SY
 +4uKbKJFgkaIN8Rq4P43TeznzHJpM671AJoOhb4KiJqC97IKZ7w779WP/oEkyqeJ
 ZbIV9neG+W+185oC9Lwxcuw4HO4Ub7LF2w/AuwlKTS0v7+673QUpCrcYkJluZIj2
 M6bcQiv0txouzUbnnuIf/cU20JAy1FieqjEsPFyaY+CJdfJSiYsGc++ZNl7DH6xH
 AEUu/XYis+TyzfWkI7HcM2aYihhBjQMkPNXwBW9BcFRAwplhpreLJBO5Q6DwhJDs
 8uVlsZQdV/1SxxMkT0JnqYEWv5lUvecGe4eGVeopKsms4DcDytEfEYxvrJVtRr5S
 hp8o4iNfvIGFSbIXvN7V
 =rgX/
 -----END PGP SIGNATURE-----

Merge tag 'exynos-drm-next-for-v4.13' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos into drm-next

Summary:
- Rework vblank handling
  . This patch series adds frame counter callback and removes
    unnecessary pipe relevnt fields and simplifies event handling.
- clean up and fix up sw-trigger relevant code
  . This patch series moves TE relevant code from Panel and HDMI
    to DECON driver to fix a race between interrupt handlers and
    DECON disable, and to fix timeout issue at wait-for-vblank.
  . It removes unnecessary flags and check code specific to Exynos driver.

* tag 'exynos-drm-next-for-v4.13' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos: (27 commits)
  drm/exynos/decon5433: remove useless check
  drm/exynos/decon5433: kill BIT_SUSPENDED flag
  drm/exynos/decon5433: kill BIT_WIN_UPDATED flag
  drm/exynos/decon5433: kill BIT_CLKS_ENABLED flag
  drm/exynos/decon5433: kill BIT_IRQS_ENABLED flag
  drm/exynos/decon5433: move TE handling to DECON
  dt-bindings: exynos5433-decon: add TE interrupt binding
  dt-bindings: exynos5433-decon: fix interrupts bindings
  drm/exynos/decon5433: always do sw-trigger when vblanks enabled
  drm/exynos: mixer: document YCbCr magic numbers
  drm/exynos: mixer: simplify mixer_cfg_rgb_fmt()
  drm/exynos/dsi: fix bridge_node DT parsing
  drm/exynos/hdmi: fix pipeline disable order
  drm/exynos/decon5433: simplify shadow protect code
  drm/exynos/decon5433: kill BIT_IRQS_ENABLED
  drm/exynos/decon5433: kill DECON_UPDATE workaround
  drm/exynos: kill mode_set_nofb callback
  drm/exynos: kill pipe field from drivers contexts
  drm/exynos: set plane possible_crtcs in exynos_plane_init
  drm/exynos: kill exynos_drm_private::pipe
  ...
This commit is contained in:
Dave Airlie 2017-06-06 16:54:27 +10:00
commit c9f0726ff3
15 changed files with 195 additions and 302 deletions

View File

@ -8,12 +8,13 @@ Required properties:
- compatible: value should be one of:
"samsung,exynos5433-decon", "samsung,exynos5433-decon-tv";
- reg: physical base address and length of the DECON registers set.
- interrupts: should contain a list of all DECON IP block interrupts in the
order: VSYNC, LCD_SYSTEM. The interrupt specifier format
depends on the interrupt controller used.
- interrupt-names: should contain the interrupt names: "vsync", "lcd_sys"
in the same order as they were listed in the interrupts
property.
- interrupt-names: should contain the interrupt names depending on mode of work:
video mode: "vsync",
command mode: "lcd_sys",
command mode with software trigger: "lcd_sys", "te".
- interrupts or interrupts-extended: list of interrupt specifiers corresponding
to names privided in interrupt-names, as described in
interrupt-controller/interrupts.txt
- clocks: must include clock specifiers corresponding to entries in the
clock-names property.
- clock-names: list of clock names sorted in the same order as the clocks

View File

@ -47,14 +47,6 @@ static const char * const decon_clks_name[] = {
"sclk_decon_eclk",
};
enum decon_flag_bits {
BIT_CLKS_ENABLED,
BIT_IRQS_ENABLED,
BIT_WIN_UPDATED,
BIT_SUSPENDED,
BIT_REQUEST_UPDATE
};
struct decon_context {
struct device *dev;
struct drm_device *drm_dev;
@ -64,8 +56,8 @@ struct decon_context {
void __iomem *addr;
struct regmap *sysreg;
struct clk *clks[ARRAY_SIZE(decon_clks_name)];
int pipe;
unsigned long flags;
unsigned int irq;
unsigned int te_irq;
unsigned long out_type;
int first_win;
spinlock_t vblank_lock;
@ -97,18 +89,17 @@ static int decon_enable_vblank(struct exynos_drm_crtc *crtc)
struct decon_context *ctx = crtc->ctx;
u32 val;
if (test_bit(BIT_SUSPENDED, &ctx->flags))
return -EPERM;
val = VIDINTCON0_INTEN;
if (ctx->out_type & IFTYPE_I80)
val |= VIDINTCON0_FRAMEDONE;
else
val |= VIDINTCON0_INTFRMEN | VIDINTCON0_FRAMESEL_FP;
if (!test_and_set_bit(BIT_IRQS_ENABLED, &ctx->flags)) {
val = VIDINTCON0_INTEN;
if (ctx->out_type & IFTYPE_I80)
val |= VIDINTCON0_FRAMEDONE;
else
val |= VIDINTCON0_INTFRMEN | VIDINTCON0_FRAMESEL_FP;
writel(val, ctx->addr + DECON_VIDINTCON0);
writel(val, ctx->addr + DECON_VIDINTCON0);
}
enable_irq(ctx->irq);
if (!(ctx->out_type & I80_HW_TRG))
enable_irq(ctx->te_irq);
return 0;
}
@ -117,11 +108,11 @@ static void decon_disable_vblank(struct exynos_drm_crtc *crtc)
{
struct decon_context *ctx = crtc->ctx;
if (test_bit(BIT_SUSPENDED, &ctx->flags))
return;
if (!(ctx->out_type & I80_HW_TRG))
disable_irq_nosync(ctx->te_irq);
disable_irq_nosync(ctx->irq);
if (test_and_clear_bit(BIT_IRQS_ENABLED, &ctx->flags))
writel(0, ctx->addr + DECON_VIDINTCON0);
writel(0, ctx->addr + DECON_VIDINTCON0);
}
/* return number of starts/ends of frame transmissions since reset */
@ -166,6 +157,13 @@ static u32 decon_get_frame_count(struct decon_context *ctx, bool end)
return frm;
}
static u32 decon_get_vblank_counter(struct exynos_drm_crtc *crtc)
{
struct decon_context *ctx = crtc->ctx;
return decon_get_frame_count(ctx, false);
}
static void decon_setup_trigger(struct decon_context *ctx)
{
if (!(ctx->out_type & (IFTYPE_I80 | I80_HW_TRG)))
@ -193,9 +191,6 @@ static void decon_commit(struct exynos_drm_crtc *crtc)
bool interlaced = false;
u32 val;
if (test_bit(BIT_SUSPENDED, &ctx->flags))
return;
if (ctx->out_type & IFTYPE_HDMI) {
m->crtc_hsync_start = m->crtc_hdisplay + 10;
m->crtc_hsync_end = m->crtc_htotal - 92;
@ -309,23 +304,17 @@ static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win,
writel(val, ctx->addr + DECON_WINCONx(win));
}
static void decon_shadow_protect_win(struct decon_context *ctx, int win,
bool protect)
static void decon_shadow_protect(struct decon_context *ctx, bool protect)
{
decon_set_bits(ctx, DECON_SHADOWCON, SHADOWCON_Wx_PROTECT(win),
decon_set_bits(ctx, DECON_SHADOWCON, SHADOWCON_PROTECT_MASK,
protect ? ~0 : 0);
}
static void decon_atomic_begin(struct exynos_drm_crtc *crtc)
{
struct decon_context *ctx = crtc->ctx;
int i;
if (test_bit(BIT_SUSPENDED, &ctx->flags))
return;
for (i = ctx->first_win; i < WINDOWS_NR; i++)
decon_shadow_protect_win(ctx, i, true);
decon_shadow_protect(ctx, true);
}
#define BIT_VAL(x, e, s) (((x) & ((1 << ((e) - (s) + 1)) - 1)) << (s))
@ -345,9 +334,6 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc,
dma_addr_t dma_addr = exynos_drm_fb_dma_addr(fb, 0);
u32 val;
if (test_bit(BIT_SUSPENDED, &ctx->flags))
return;
if (crtc->base.mode.flags & DRM_MODE_FLAG_INTERLACE) {
val = COORDINATE_X(state->crtc.x) |
COORDINATE_Y(state->crtc.y / 2);
@ -390,7 +376,6 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc,
/* window enable */
decon_set_bits(ctx, DECON_WINCONx(win), WINCONx_ENWIN_F, ~0);
set_bit(BIT_REQUEST_UPDATE, &ctx->flags);
}
static void decon_disable_plane(struct exynos_drm_crtc *crtc,
@ -399,32 +384,19 @@ static void decon_disable_plane(struct exynos_drm_crtc *crtc,
struct decon_context *ctx = crtc->ctx;
unsigned int win = plane->index;
if (test_bit(BIT_SUSPENDED, &ctx->flags))
return;
decon_set_bits(ctx, DECON_WINCONx(win), WINCONx_ENWIN_F, 0);
set_bit(BIT_REQUEST_UPDATE, &ctx->flags);
}
static void decon_atomic_flush(struct exynos_drm_crtc *crtc)
{
struct decon_context *ctx = crtc->ctx;
unsigned long flags;
int i;
if (test_bit(BIT_SUSPENDED, &ctx->flags))
return;
spin_lock_irqsave(&ctx->vblank_lock, flags);
for (i = ctx->first_win; i < WINDOWS_NR; i++)
decon_shadow_protect_win(ctx, i, false);
decon_shadow_protect(ctx, false);
if (test_and_clear_bit(BIT_REQUEST_UPDATE, &ctx->flags))
decon_set_bits(ctx, DECON_UPDATE, STANDALONE_UPDATE_F, ~0);
if (ctx->out_type & IFTYPE_I80)
set_bit(BIT_WIN_UPDATED, &ctx->flags);
decon_set_bits(ctx, DECON_UPDATE, STANDALONE_UPDATE_F, ~0);
ctx->frame_id = decon_get_frame_count(ctx, true);
@ -473,21 +445,12 @@ static void decon_enable(struct exynos_drm_crtc *crtc)
{
struct decon_context *ctx = crtc->ctx;
if (!test_and_clear_bit(BIT_SUSPENDED, &ctx->flags))
return;
pm_runtime_get_sync(ctx->dev);
exynos_drm_pipe_clk_enable(crtc, true);
set_bit(BIT_CLKS_ENABLED, &ctx->flags);
decon_swreset(ctx);
/* if vblank was enabled status, enable it again. */
if (test_and_clear_bit(BIT_IRQS_ENABLED, &ctx->flags))
decon_enable_vblank(ctx->crtc);
decon_commit(ctx->crtc);
}
@ -496,8 +459,9 @@ static void decon_disable(struct exynos_drm_crtc *crtc)
struct decon_context *ctx = crtc->ctx;
int i;
if (test_bit(BIT_SUSPENDED, &ctx->flags))
return;
if (!(ctx->out_type & I80_HW_TRG))
synchronize_irq(ctx->te_irq);
synchronize_irq(ctx->irq);
/*
* We need to make sure that all windows are disabled before we
@ -509,25 +473,18 @@ static void decon_disable(struct exynos_drm_crtc *crtc)
decon_swreset(ctx);
clear_bit(BIT_CLKS_ENABLED, &ctx->flags);
exynos_drm_pipe_clk_enable(crtc, false);
pm_runtime_put_sync(ctx->dev);
set_bit(BIT_SUSPENDED, &ctx->flags);
}
static void decon_te_irq_handler(struct exynos_drm_crtc *crtc)
static irqreturn_t decon_te_irq_handler(int irq, void *dev_id)
{
struct decon_context *ctx = crtc->ctx;
struct decon_context *ctx = dev_id;
if (!test_bit(BIT_CLKS_ENABLED, &ctx->flags) ||
(ctx->out_type & I80_HW_TRG))
return;
decon_set_bits(ctx, DECON_TRIGCON, TRIGCON_SWTRIGCMD, ~0);
if (test_and_clear_bit(BIT_WIN_UPDATED, &ctx->flags))
decon_set_bits(ctx, DECON_TRIGCON, TRIGCON_SWTRIGCMD, ~0);
return IRQ_HANDLED;
}
static void decon_clear_channels(struct exynos_drm_crtc *crtc)
@ -543,11 +500,10 @@ static void decon_clear_channels(struct exynos_drm_crtc *crtc)
goto err;
}
for (win = 0; win < WINDOWS_NR; win++) {
decon_shadow_protect_win(ctx, win, true);
decon_shadow_protect(ctx, true);
for (win = 0; win < WINDOWS_NR; win++)
decon_set_bits(ctx, DECON_WINCONx(win), WINCONx_ENWIN_F, 0);
decon_shadow_protect_win(ctx, win, false);
}
decon_shadow_protect(ctx, false);
decon_set_bits(ctx, DECON_UPDATE, STANDALONE_UPDATE_F, ~0);
@ -564,25 +520,24 @@ static const struct exynos_drm_crtc_ops decon_crtc_ops = {
.disable = decon_disable,
.enable_vblank = decon_enable_vblank,
.disable_vblank = decon_disable_vblank,
.get_vblank_counter = decon_get_vblank_counter,
.atomic_begin = decon_atomic_begin,
.update_plane = decon_update_plane,
.disable_plane = decon_disable_plane,
.atomic_flush = decon_atomic_flush,
.te_handler = decon_te_irq_handler,
};
static int decon_bind(struct device *dev, struct device *master, void *data)
{
struct decon_context *ctx = dev_get_drvdata(dev);
struct drm_device *drm_dev = data;
struct exynos_drm_private *priv = drm_dev->dev_private;
struct exynos_drm_plane *exynos_plane;
enum exynos_drm_output_type out_type;
unsigned int win;
int ret;
ctx->drm_dev = drm_dev;
ctx->pipe = priv->pipe++;
drm_dev->max_vblank_count = 0xffffffff;
for (win = ctx->first_win; win < WINDOWS_NR; win++) {
int tmp = (win == ctx->first_win) ? 0 : win;
@ -593,7 +548,7 @@ static int decon_bind(struct device *dev, struct device *master, void *data)
ctx->configs[win].type = decon_win_types[tmp];
ret = exynos_plane_init(drm_dev, &ctx->planes[win], win,
1 << ctx->pipe, &ctx->configs[win]);
&ctx->configs[win]);
if (ret)
return ret;
}
@ -602,23 +557,13 @@ static int decon_bind(struct device *dev, struct device *master, void *data)
out_type = (ctx->out_type & IFTYPE_HDMI) ? EXYNOS_DISPLAY_TYPE_HDMI
: EXYNOS_DISPLAY_TYPE_LCD;
ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base,
ctx->pipe, out_type,
&decon_crtc_ops, ctx);
if (IS_ERR(ctx->crtc)) {
ret = PTR_ERR(ctx->crtc);
goto err;
}
out_type, &decon_crtc_ops, ctx);
if (IS_ERR(ctx->crtc))
return PTR_ERR(ctx->crtc);
decon_clear_channels(ctx->crtc);
ret = drm_iommu_attach_device(drm_dev, dev);
if (ret)
goto err;
return ret;
err:
priv->pipe--;
return ret;
return drm_iommu_attach_device(drm_dev, dev);
}
static void decon_unbind(struct device *dev, struct device *master, void *data)
@ -659,9 +604,6 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id)
struct decon_context *ctx = dev_id;
u32 val;
if (!test_bit(BIT_CLKS_ENABLED, &ctx->flags))
goto out;
val = readl(ctx->addr + DECON_VIDINTCON1);
val &= VIDINTCON1_INTFRMDONEPEND | VIDINTCON1_INTFRMPEND;
@ -677,7 +619,6 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id)
decon_handle_vblank(ctx);
}
out:
return IRQ_HANDLED;
}
@ -732,6 +673,31 @@ static const struct of_device_id exynos5433_decon_driver_dt_match[] = {
};
MODULE_DEVICE_TABLE(of, exynos5433_decon_driver_dt_match);
static int decon_conf_irq(struct decon_context *ctx, const char *name,
irq_handler_t handler, unsigned long int flags, bool required)
{
struct platform_device *pdev = to_platform_device(ctx->dev);
int ret, irq = platform_get_irq_byname(pdev, name);
if (irq < 0) {
if (irq == -EPROBE_DEFER)
return irq;
if (required)
dev_err(ctx->dev, "cannot get %s IRQ\n", name);
else
irq = 0;
return irq;
}
irq_set_status_flags(irq, IRQ_NOAUTOEN);
ret = devm_request_irq(ctx->dev, irq, handler, flags, "drm_decon", ctx);
if (ret < 0) {
dev_err(ctx->dev, "IRQ %s request failed\n", name);
return ret;
}
return irq;
}
static int exynos5433_decon_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@ -744,7 +710,6 @@ static int exynos5433_decon_probe(struct platform_device *pdev)
if (!ctx)
return -ENOMEM;
__set_bit(BIT_SUSPENDED, &ctx->flags);
ctx->dev = dev;
ctx->out_type = (unsigned long)of_device_get_match_data(dev);
spin_lock_init(&ctx->vblank_lock);
@ -755,15 +720,6 @@ static int exynos5433_decon_probe(struct platform_device *pdev)
ctx->out_type |= IFTYPE_I80;
}
if (ctx->out_type & I80_HW_TRG) {
ctx->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
"samsung,disp-sysreg");
if (IS_ERR(ctx->sysreg)) {
dev_err(dev, "failed to get system register\n");
return PTR_ERR(ctx->sysreg);
}
}
for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++) {
struct clk *clk;
@ -786,18 +742,34 @@ static int exynos5433_decon_probe(struct platform_device *pdev)
return PTR_ERR(ctx->addr);
}
res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
(ctx->out_type & IFTYPE_I80) ? "lcd_sys" : "vsync");
if (!res) {
dev_err(dev, "cannot find IRQ resource\n");
return -ENXIO;
if (ctx->out_type & IFTYPE_I80) {
ret = decon_conf_irq(ctx, "lcd_sys", decon_irq_handler, 0, true);
if (ret < 0)
return ret;
ctx->irq = ret;
ret = decon_conf_irq(ctx, "te", decon_te_irq_handler,
IRQF_TRIGGER_RISING, false);
if (ret < 0)
return ret;
if (ret) {
ctx->te_irq = ret;
ctx->out_type &= ~I80_HW_TRG;
}
} else {
ret = decon_conf_irq(ctx, "vsync", decon_irq_handler, 0, true);
if (ret < 0)
return ret;
ctx->irq = ret;
}
ret = devm_request_irq(dev, res->start, decon_irq_handler, 0,
"drm_decon", ctx);
if (ret < 0) {
dev_err(dev, "lcd_sys irq request failed\n");
return ret;
if (ctx->out_type & I80_HW_TRG) {
ctx->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
"samsung,disp-sysreg");
if (IS_ERR(ctx->sysreg)) {
dev_err(dev, "failed to get system register\n");
return PTR_ERR(ctx->sysreg);
}
}
platform_set_drvdata(pdev, ctx);

View File

@ -55,7 +55,6 @@ struct decon_context {
unsigned long irq_flags;
bool i80_if;
bool suspended;
int pipe;
wait_queue_head_t wait_vsync_queue;
atomic_t wait_vsync_event;
@ -130,19 +129,11 @@ static void decon_clear_channels(struct exynos_drm_crtc *crtc)
static int decon_ctx_initialize(struct decon_context *ctx,
struct drm_device *drm_dev)
{
struct exynos_drm_private *priv = drm_dev->dev_private;
int ret;
ctx->drm_dev = drm_dev;
ctx->pipe = priv->pipe++;
decon_clear_channels(ctx->crtc);
ret = drm_iommu_attach_device(drm_dev, ctx->dev);
if (ret)
priv->pipe--;
return ret;
return drm_iommu_attach_device(drm_dev, ctx->dev);
}
static void decon_ctx_remove(struct decon_context *ctx)
@ -590,7 +581,6 @@ static void decon_disable(struct exynos_drm_crtc *crtc)
static const struct exynos_drm_crtc_ops decon_crtc_ops = {
.enable = decon_enable,
.disable = decon_disable,
.commit = decon_commit,
.enable_vblank = decon_enable_vblank,
.disable_vblank = decon_disable_vblank,
.atomic_begin = decon_atomic_begin,
@ -612,7 +602,7 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id)
writel(clear_bit, ctx->regs + VIDINTCON1);
/* check the crtc is detached already from encoder */
if (ctx->pipe < 0 || !ctx->drm_dev)
if (!ctx->drm_dev)
goto out;
if (!ctx->i80_if) {
@ -649,15 +639,14 @@ static int decon_bind(struct device *dev, struct device *master, void *data)
ctx->configs[i].type = decon_win_types[i];
ret = exynos_plane_init(drm_dev, &ctx->planes[i], i,
1 << ctx->pipe, &ctx->configs[i]);
&ctx->configs[i]);
if (ret)
return ret;
}
exynos_plane = &ctx->planes[DEFAULT_WIN];
ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base,
ctx->pipe, EXYNOS_DISPLAY_TYPE_LCD,
&decon_crtc_ops, ctx);
EXYNOS_DISPLAY_TYPE_LCD, &decon_crtc_ops, ctx);
if (IS_ERR(ctx->crtc)) {
decon_ctx_remove(ctx);
return PTR_ERR(ctx->crtc);

View File

@ -49,15 +49,6 @@ static void exynos_drm_crtc_disable(struct drm_crtc *crtc)
}
}
static void
exynos_drm_crtc_mode_set_nofb(struct drm_crtc *crtc)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
if (exynos_crtc->ops->commit)
exynos_crtc->ops->commit(exynos_crtc);
}
static int exynos_crtc_atomic_check(struct drm_crtc *crtc,
struct drm_crtc_state *state)
{
@ -93,7 +84,6 @@ static void exynos_crtc_atomic_flush(struct drm_crtc *crtc,
static const struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
.enable = exynos_drm_crtc_enable,
.disable = exynos_drm_crtc_disable,
.mode_set_nofb = exynos_drm_crtc_mode_set_nofb,
.atomic_check = exynos_crtc_atomic_check,
.atomic_begin = exynos_crtc_atomic_begin,
.atomic_flush = exynos_crtc_atomic_flush,
@ -105,16 +95,15 @@ void exynos_crtc_handle_event(struct exynos_drm_crtc *exynos_crtc)
struct drm_pending_vblank_event *event = crtc->state->event;
unsigned long flags;
if (event) {
crtc->state->event = NULL;
spin_lock_irqsave(&crtc->dev->event_lock, flags);
if (drm_crtc_vblank_get(crtc) == 0)
drm_crtc_arm_vblank_event(crtc, event);
else
drm_crtc_send_vblank_event(crtc, event);
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
}
if (!event)
return;
crtc->state->event = NULL;
WARN_ON(drm_crtc_vblank_get(crtc) != 0);
spin_lock_irqsave(&crtc->dev->event_lock, flags);
drm_crtc_arm_vblank_event(crtc, event);
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
}
static void exynos_drm_crtc_destroy(struct drm_crtc *crtc)
@ -143,6 +132,16 @@ static void exynos_drm_crtc_disable_vblank(struct drm_crtc *crtc)
exynos_crtc->ops->disable_vblank(exynos_crtc);
}
static u32 exynos_drm_crtc_get_vblank_counter(struct drm_crtc *crtc)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
if (exynos_crtc->ops->get_vblank_counter)
return exynos_crtc->ops->get_vblank_counter(exynos_crtc);
return 0;
}
static const struct drm_crtc_funcs exynos_crtc_funcs = {
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
@ -152,11 +151,11 @@ static const struct drm_crtc_funcs exynos_crtc_funcs = {
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
.enable_vblank = exynos_drm_crtc_enable_vblank,
.disable_vblank = exynos_drm_crtc_disable_vblank,
.get_vblank_counter = exynos_drm_crtc_get_vblank_counter,
};
struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
struct drm_plane *plane,
int pipe,
enum exynos_drm_output_type type,
const struct exynos_drm_crtc_ops *ops,
void *ctx)
@ -169,7 +168,6 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
if (!exynos_crtc)
return ERR_PTR(-ENOMEM);
exynos_crtc->pipe = pipe;
exynos_crtc->type = type;
exynos_crtc->ops = ops;
exynos_crtc->ctx = ctx;
@ -196,13 +194,9 @@ int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev,
{
struct drm_crtc *crtc;
list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) {
struct exynos_drm_crtc *exynos_crtc;
exynos_crtc = to_exynos_crtc(crtc);
if (exynos_crtc->type == out_type)
return exynos_crtc->pipe;
}
drm_for_each_crtc(crtc, drm_dev)
if (to_exynos_crtc(crtc)->type == out_type)
return drm_crtc_index(crtc);
return -EPERM;
}

View File

@ -19,7 +19,6 @@
struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
struct drm_plane *plane,
int pipe,
enum exynos_drm_output_type type,
const struct exynos_drm_crtc_ops *ops,
void *context);

View File

@ -115,7 +115,6 @@ struct exynos_drm_plane_config {
*
* @enable: enable the device
* @disable: disable the device
* @commit: set current hw specific display mode to hw.
* @enable_vblank: specific driver callback for enabling vblank interrupt.
* @disable_vblank: specific driver callback for disabling vblank interrupt.
* @atomic_check: validate state
@ -130,9 +129,9 @@ struct exynos_drm_crtc;
struct exynos_drm_crtc_ops {
void (*enable)(struct exynos_drm_crtc *crtc);
void (*disable)(struct exynos_drm_crtc *crtc);
void (*commit)(struct exynos_drm_crtc *crtc);
int (*enable_vblank)(struct exynos_drm_crtc *crtc);
void (*disable_vblank)(struct exynos_drm_crtc *crtc);
u32 (*get_vblank_counter)(struct exynos_drm_crtc *crtc);
int (*atomic_check)(struct exynos_drm_crtc *crtc,
struct drm_crtc_state *state);
void (*atomic_begin)(struct exynos_drm_crtc *crtc);
@ -153,24 +152,13 @@ struct exynos_drm_clk {
*
* @base: crtc object.
* @type: one of EXYNOS_DISPLAY_TYPE_LCD and HDMI.
* @pipe: a crtc index created at load() with a new crtc object creation
* and the crtc object would be set to private->crtc array
* to get a crtc object corresponding to this pipe from private->crtc
* array when irq interrupt occurred. the reason of using this pipe is that
* drm framework doesn't support multiple irq yet.
* we can refer to the crtc to current hardware interrupt occurred through
* this pipe value.
* @enabled: if the crtc is enabled or not
* @event: vblank event that is currently queued for flip
* @wait_update: wait all pending planes updates to finish
* @pending_update: number of pending plane updates in this crtc
* @ops: pointer to callbacks for exynos drm specific functionality
* @ctx: A pointer to the crtc's implementation specific context
* @pipe_clk: A pointer to the crtc's pipeline clock.
*/
struct exynos_drm_crtc {
struct drm_crtc base;
enum exynos_drm_output_type type;
unsigned int pipe;
const struct exynos_drm_crtc_ops *ops;
void *ctx;
struct exynos_drm_clk *pipe_clk;
@ -203,7 +191,6 @@ struct drm_exynos_file_private {
* otherwise default one.
* @da_space_size: size of device address space.
* if 0 then default value is used for it.
* @pipe: the pipe number for this crtc/manager.
* @pending: the crtcs that have pending updates to finish
* @lock: protect access to @pending
* @wait: wait an atomic commit to finish
@ -214,8 +201,6 @@ struct exynos_drm_private {
struct device *dma_dev;
void *mapping;
unsigned int pipe;
/* for atomic commit */
u32 pending;
spinlock_t lock;

View File

@ -1633,7 +1633,6 @@ static int exynos_dsi_parse_dt(struct exynos_dsi *dsi)
{
struct device *dev = dsi->dev;
struct device_node *node = dev->of_node;
struct device_node *ep;
int ret;
ret = exynos_dsi_of_read_u32(node, "samsung,pll-clock-frequency",
@ -1641,32 +1640,21 @@ static int exynos_dsi_parse_dt(struct exynos_dsi *dsi)
if (ret < 0)
return ret;
ep = of_graph_get_endpoint_by_regs(node, DSI_PORT_OUT, 0);
if (!ep) {
dev_err(dev, "no output port with endpoint specified\n");
return -EINVAL;
}
ret = exynos_dsi_of_read_u32(ep, "samsung,burst-clock-frequency",
ret = exynos_dsi_of_read_u32(node, "samsung,burst-clock-frequency",
&dsi->burst_clk_rate);
if (ret < 0)
goto end;
return ret;
ret = exynos_dsi_of_read_u32(ep, "samsung,esc-clock-frequency",
ret = exynos_dsi_of_read_u32(node, "samsung,esc-clock-frequency",
&dsi->esc_clk_rate);
if (ret < 0)
goto end;
return ret;
of_node_put(ep);
dsi->bridge_node = of_graph_get_remote_node(node, DSI_PORT_OUT, 0);
dsi->bridge_node = of_graph_get_remote_node(node, DSI_PORT_IN, 0);
if (!dsi->bridge_node)
return -EINVAL;
end:
of_node_put(ep);
return ret;
return 0;
}
static int exynos_dsi_bind(struct device *dev, struct device *master,
@ -1817,6 +1805,10 @@ static int exynos_dsi_probe(struct platform_device *pdev)
static int exynos_dsi_remove(struct platform_device *pdev)
{
struct exynos_dsi *dsi = platform_get_drvdata(pdev);
of_node_put(dsi->bridge_node);
pm_runtime_disable(&pdev->dev);
component_del(&pdev->dev, &exynos_dsi_component_ops);

View File

@ -179,7 +179,6 @@ struct fimd_context {
u32 i80ifcon;
bool i80_if;
bool suspended;
int pipe;
wait_queue_head_t wait_vsync_queue;
atomic_t wait_vsync_event;
atomic_t win_updated;
@ -354,18 +353,13 @@ static void fimd_clear_channels(struct exynos_drm_crtc *crtc)
/* Wait for vsync, as disable channel takes effect at next vsync */
if (ch_enabled) {
int pipe = ctx->pipe;
/* ensure that vblank interrupt won't be reported to core */
ctx->suspended = false;
ctx->pipe = -1;
fimd_enable_vblank(ctx->crtc);
fimd_wait_for_vblank(ctx->crtc);
fimd_disable_vblank(ctx->crtc);
ctx->suspended = true;
ctx->pipe = pipe;
}
clk_disable_unprepare(ctx->lcd_clk);
@ -899,7 +893,7 @@ static void fimd_te_handler(struct exynos_drm_crtc *crtc)
u32 trg_type = ctx->driver_data->trg_type;
/* Checks the crtc is detached already from encoder */
if (ctx->pipe < 0 || !ctx->drm_dev)
if (!ctx->drm_dev)
return;
if (trg_type == I80_HW_TRG)
@ -934,7 +928,6 @@ static void fimd_dp_clock_enable(struct exynos_drm_clk *clk, bool enable)
static const struct exynos_drm_crtc_ops fimd_crtc_ops = {
.enable = fimd_enable,
.disable = fimd_disable,
.commit = fimd_commit,
.enable_vblank = fimd_enable_vblank,
.disable_vblank = fimd_disable_vblank,
.atomic_begin = fimd_atomic_begin,
@ -957,7 +950,7 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
writel(clear_bit, ctx->regs + VIDINTCON1);
/* check the crtc is detached already from encoder */
if (ctx->pipe < 0 || !ctx->drm_dev)
if (!ctx->drm_dev)
goto out;
if (!ctx->i80_if)
@ -982,13 +975,11 @@ static int fimd_bind(struct device *dev, struct device *master, void *data)
{
struct fimd_context *ctx = dev_get_drvdata(dev);
struct drm_device *drm_dev = data;
struct exynos_drm_private *priv = drm_dev->dev_private;
struct exynos_drm_plane *exynos_plane;
unsigned int i;
int ret;
ctx->drm_dev = drm_dev;
ctx->pipe = priv->pipe++;
for (i = 0; i < WINDOWS_NR; i++) {
ctx->configs[i].pixel_formats = fimd_formats;
@ -996,15 +987,14 @@ static int fimd_bind(struct device *dev, struct device *master, void *data)
ctx->configs[i].zpos = i;
ctx->configs[i].type = fimd_win_types[i];
ret = exynos_plane_init(drm_dev, &ctx->planes[i], i,
1 << ctx->pipe, &ctx->configs[i]);
&ctx->configs[i]);
if (ret)
return ret;
}
exynos_plane = &ctx->planes[DEFAULT_WIN];
ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base,
ctx->pipe, EXYNOS_DISPLAY_TYPE_LCD,
&fimd_crtc_ops, ctx);
EXYNOS_DISPLAY_TYPE_LCD, &fimd_crtc_ops, ctx);
if (IS_ERR(ctx->crtc))
return PTR_ERR(ctx->crtc);
@ -1019,11 +1009,7 @@ static int fimd_bind(struct device *dev, struct device *master, void *data)
if (is_drm_iommu_supported(drm_dev))
fimd_clear_channels(ctx->crtc);
ret = drm_iommu_attach_device(drm_dev, dev);
if (ret)
priv->pipe--;
return ret;
return drm_iommu_attach_device(drm_dev, dev);
}
static void fimd_unbind(struct device *dev, struct device *master,

View File

@ -273,14 +273,13 @@ static void exynos_plane_attach_zpos_property(struct drm_plane *plane,
}
int exynos_plane_init(struct drm_device *dev,
struct exynos_drm_plane *exynos_plane,
unsigned int index, unsigned long possible_crtcs,
struct exynos_drm_plane *exynos_plane, unsigned int index,
const struct exynos_drm_plane_config *config)
{
int err;
err = drm_universal_plane_init(dev, &exynos_plane->base,
possible_crtcs,
1 << dev->mode_config.num_crtc,
&exynos_plane_funcs,
config->pixel_formats,
config->num_pixel_formats,

View File

@ -11,5 +11,4 @@
int exynos_plane_init(struct drm_device *dev,
struct exynos_drm_plane *exynos_plane, unsigned int index,
unsigned long possible_crtcs,
const struct exynos_drm_plane_config *config);

View File

@ -51,7 +51,6 @@ struct vidi_context {
bool suspended;
struct timer_list timer;
struct mutex lock;
int pipe;
};
static inline struct vidi_context *encoder_to_vidi(struct drm_encoder *e)
@ -153,17 +152,6 @@ static void vidi_disable(struct exynos_drm_crtc *crtc)
mutex_unlock(&ctx->lock);
}
static int vidi_ctx_initialize(struct vidi_context *ctx,
struct drm_device *drm_dev)
{
struct exynos_drm_private *priv = drm_dev->dev_private;
ctx->drm_dev = drm_dev;
ctx->pipe = priv->pipe++;
return 0;
}
static const struct exynos_drm_crtc_ops vidi_crtc_ops = {
.enable = vidi_enable,
.disable = vidi_disable,
@ -177,9 +165,6 @@ static void vidi_fake_vblank_timer(unsigned long arg)
{
struct vidi_context *ctx = (void *)arg;
if (ctx->pipe < 0)
return;
if (drm_crtc_handle_vblank(&ctx->crtc->base))
mod_timer(&ctx->timer,
jiffies + msecs_to_jiffies(VIDI_REFRESH_TIME) - 1);
@ -399,7 +384,7 @@ static int vidi_bind(struct device *dev, struct device *master, void *data)
unsigned int i;
int pipe, ret;
vidi_ctx_initialize(ctx, drm_dev);
ctx->drm_dev = drm_dev;
plane_config.pixel_formats = formats;
plane_config.num_pixel_formats = ARRAY_SIZE(formats);
@ -409,15 +394,14 @@ static int vidi_bind(struct device *dev, struct device *master, void *data)
plane_config.type = vidi_win_types[i];
ret = exynos_plane_init(drm_dev, &ctx->planes[i], i,
1 << ctx->pipe, &plane_config);
&plane_config);
if (ret)
return ret;
}
exynos_plane = &ctx->planes[DEFAULT_WIN];
ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base,
ctx->pipe, EXYNOS_DISPLAY_TYPE_VIDI,
&vidi_crtc_ops, ctx);
EXYNOS_DISPLAY_TYPE_VIDI, &vidi_crtc_ops, ctx);
if (IS_ERR(ctx->crtc)) {
DRM_ERROR("failed to create crtc.\n");
return PTR_ERR(ctx->crtc);

View File

@ -1486,8 +1486,6 @@ static void hdmi_enable(struct drm_encoder *encoder)
static void hdmi_disable(struct drm_encoder *encoder)
{
struct hdmi_context *hdata = encoder_to_hdmi(encoder);
struct drm_crtc *crtc = encoder->crtc;
const struct drm_crtc_helper_funcs *funcs = NULL;
if (!hdata->powered)
return;
@ -1498,16 +1496,11 @@ static void hdmi_disable(struct drm_encoder *encoder)
* to disable TV Subsystem should be as following,
* VP -> Mixer -> HDMI
*
* Below codes will try to disable Mixer and VP(if used)
* prior to disabling HDMI.
* To achieve such sequence HDMI is disabled together with HDMI PHY, via
* pipe clock callback.
*/
if (crtc)
funcs = crtc->helper_private;
if (funcs && funcs->disable)
(*funcs->disable)(crtc);
cec_notifier_set_phys_addr(hdata->notifier, CEC_PHYS_ADDR_INVALID);
cancel_delayed_work(&hdata->hotplug_work);
cec_notifier_set_phys_addr(hdata->notifier, CEC_PHYS_ADDR_INVALID);
hdmiphy_disable(hdata);
}

View File

@ -45,6 +45,22 @@
#define MIXER_WIN_NR 3
#define VP_DEFAULT_WIN 2
/*
* Mixer color space conversion coefficient triplet.
* Used for CSC from RGB to YCbCr.
* Each coefficient is a 10-bit fixed point number with
* sign and no integer part, i.e.
* [0:8] = fractional part (representing a value y = x / 2^9)
* [9] = sign
* Negative values are encoded with two's complement.
*/
#define MXR_CSC_C(x) ((int)((x) * 512.0) & 0x3ff)
#define MXR_CSC_CT(a0, a1, a2) \
((MXR_CSC_C(a0) << 20) | (MXR_CSC_C(a1) << 10) | (MXR_CSC_C(a2) << 0))
/* YCbCr value, used for mixer background color configuration. */
#define MXR_YCBCR_VAL(y, cb, cr) (((y) << 16) | ((cb) << 8) | ((cr) << 0))
/* The pixelformats that are natively supported by the mixer. */
#define MXR_FORMAT_RGB565 4
#define MXR_FORMAT_ARGB1555 5
@ -99,7 +115,6 @@ struct mixer_context {
struct drm_device *drm_dev;
struct exynos_drm_crtc *crtc;
struct exynos_drm_plane planes[MIXER_WIN_NR];
int pipe;
unsigned long flags;
struct mixer_resources mixer_res;
@ -382,37 +397,24 @@ static void mixer_cfg_rgb_fmt(struct mixer_context *ctx, unsigned int height)
struct mixer_resources *res = &ctx->mixer_res;
u32 val;
if (height == 480) {
switch (height) {
case 480:
case 576:
val = MXR_CFG_RGB601_0_255;
} else if (height == 576) {
val = MXR_CFG_RGB601_0_255;
} else if (height == 720) {
break;
case 720:
case 1080:
default:
val = MXR_CFG_RGB709_16_235;
/* Configure the BT.709 CSC matrix for full range RGB. */
mixer_reg_write(res, MXR_CM_COEFF_Y,
(1 << 30) | (94 << 20) | (314 << 10) |
(32 << 0));
MXR_CSC_CT( 0.184, 0.614, 0.063) |
MXR_CM_COEFF_RGB_FULL);
mixer_reg_write(res, MXR_CM_COEFF_CB,
(972 << 20) | (851 << 10) | (225 << 0));
MXR_CSC_CT(-0.102, -0.338, 0.440));
mixer_reg_write(res, MXR_CM_COEFF_CR,
(225 << 20) | (820 << 10) | (1004 << 0));
} else if (height == 1080) {
val = MXR_CFG_RGB709_16_235;
mixer_reg_write(res, MXR_CM_COEFF_Y,
(1 << 30) | (94 << 20) | (314 << 10) |
(32 << 0));
mixer_reg_write(res, MXR_CM_COEFF_CB,
(972 << 20) | (851 << 10) | (225 << 0));
mixer_reg_write(res, MXR_CM_COEFF_CR,
(225 << 20) | (820 << 10) | (1004 << 0));
} else {
val = MXR_CFG_RGB709_16_235;
mixer_reg_write(res, MXR_CM_COEFF_Y,
(1 << 30) | (94 << 20) | (314 << 10) |
(32 << 0));
mixer_reg_write(res, MXR_CM_COEFF_CB,
(972 << 20) | (851 << 10) | (225 << 0));
mixer_reg_write(res, MXR_CM_COEFF_CR,
(225 << 20) | (820 << 10) | (1004 << 0));
MXR_CSC_CT( 0.440, -0.399, -0.040));
break;
}
mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_RGB_FMT_MASK);
@ -729,10 +731,10 @@ static void mixer_win_reset(struct mixer_context *ctx)
/* reset default layer priority */
mixer_reg_write(res, MXR_LAYER_CFG, 0);
/* setting background color */
mixer_reg_write(res, MXR_BG_COLOR0, 0x008080);
mixer_reg_write(res, MXR_BG_COLOR1, 0x008080);
mixer_reg_write(res, MXR_BG_COLOR2, 0x008080);
/* set all background colors to RGB (0,0,0) */
mixer_reg_write(res, MXR_BG_COLOR0, MXR_YCBCR_VAL(0, 128, 128));
mixer_reg_write(res, MXR_BG_COLOR1, MXR_YCBCR_VAL(0, 128, 128));
mixer_reg_write(res, MXR_BG_COLOR2, MXR_YCBCR_VAL(0, 128, 128));
if (test_bit(MXR_BIT_VP_ENABLED, &ctx->flags)) {
/* configuration of Video Processor Registers */
@ -900,7 +902,6 @@ static int mixer_initialize(struct mixer_context *mixer_ctx,
priv = drm_dev->dev_private;
mixer_ctx->drm_dev = drm_dev;
mixer_ctx->pipe = priv->pipe++;
/* acquire resources: regs, irqs, clocks */
ret = mixer_resources_init(mixer_ctx);
@ -918,11 +919,7 @@ static int mixer_initialize(struct mixer_context *mixer_ctx,
}
}
ret = drm_iommu_attach_device(drm_dev, mixer_ctx->dev);
if (ret)
priv->pipe--;
return ret;
return drm_iommu_attach_device(drm_dev, mixer_ctx->dev);
}
static void mixer_ctx_remove(struct mixer_context *mixer_ctx)
@ -1158,15 +1155,14 @@ static int mixer_bind(struct device *dev, struct device *manager, void *data)
continue;
ret = exynos_plane_init(drm_dev, &ctx->planes[i], i,
1 << ctx->pipe, &plane_configs[i]);
&plane_configs[i]);
if (ret)
return ret;
}
exynos_plane = &ctx->planes[DEFAULT_WIN];
ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base,
ctx->pipe, EXYNOS_DISPLAY_TYPE_HDMI,
&mixer_crtc_ops, ctx);
EXYNOS_DISPLAY_TYPE_HDMI, &mixer_crtc_ops, ctx);
if (IS_ERR(ctx->crtc)) {
mixer_ctx_remove(ctx);
ret = PTR_ERR(ctx->crtc);

View File

@ -140,11 +140,11 @@
#define MXR_INT_EN_VSYNC (1 << 11)
#define MXR_INT_EN_ALL (0x0f << 8)
/* bit for MXR_INT_STATUS */
/* bits for MXR_INT_STATUS */
#define MXR_INT_CLEAR_VSYNC (1 << 11)
#define MXR_INT_STATUS_VSYNC (1 << 0)
/* bit for MXR_LAYER_CFG */
/* bits for MXR_LAYER_CFG */
#define MXR_LAYER_CFG_GRP1_VAL(x) MXR_MASK_VAL(x, 11, 8)
#define MXR_LAYER_CFG_GRP1_MASK MXR_LAYER_CFG_GRP1_VAL(~0)
#define MXR_LAYER_CFG_GRP0_VAL(x) MXR_MASK_VAL(x, 7, 4)
@ -152,5 +152,8 @@
#define MXR_LAYER_CFG_VP_VAL(x) MXR_MASK_VAL(x, 3, 0)
#define MXR_LAYER_CFG_VP_MASK MXR_LAYER_CFG_VP_VAL(~0)
/* bits for MXR_CM_COEFF_Y */
#define MXR_CM_COEFF_RGB_FULL (1 << 30)
#endif /* SAMSUNG_REGS_MIXER_H */

View File

@ -118,6 +118,7 @@
#define WINCONx_ENWIN_F (1 << 0)
/* SHADOWCON */
#define SHADOWCON_PROTECT_MASK GENMASK(14, 10)
#define SHADOWCON_Wx_PROTECT(n) (1 << (10 + (n)))
/* VIDOSDxD */