Message ID | 600d29233eb0dcca3af815fbed8995e35f78b4ee.1513178989.git-series.maxime.ripard@free-electrons.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On 13/12/2017 16:33, Maxime Ripard wrote: > The display frontend is an hardware block that can be used to implement > some more advanced features like hardware scaling or colorspace > conversions. It can also be used to implement the output format of the VPU. > > Let's create a minimal driver for it that will only enable the hardware > scaling features. > > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> > --- > drivers/gpu/drm/sun4i/Makefile | 3 +- > drivers/gpu/drm/sun4i/sun4i_backend.c | 5 +- > drivers/gpu/drm/sun4i/sun4i_backend.h | 1 +- > drivers/gpu/drm/sun4i/sun4i_drv.c | 16 +- > drivers/gpu/drm/sun4i/sun4i_drv.h | 1 +- > drivers/gpu/drm/sun4i/sun4i_frontend.c | 377 ++++++++++++++++++++++++++- > drivers/gpu/drm/sun4i/sun4i_frontend.h | 102 +++++++- > 7 files changed, 500 insertions(+), 5 deletions(-) > create mode 100644 drivers/gpu/drm/sun4i/sun4i_frontend.c > create mode 100644 drivers/gpu/drm/sun4i/sun4i_frontend.h > > diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile > index 0c2f8c7facae..b660d82011f4 100644 > --- a/drivers/gpu/drm/sun4i/Makefile > +++ b/drivers/gpu/drm/sun4i/Makefile > @@ -1,5 +1,6 @@ > # SPDX-License-Identifier: GPL-2.0 > sun4i-backend-y += sun4i_backend.o sun4i_layer.o > +sun4i-frontend-y += sun4i_frontend.o > > sun4i-drm-y += sun4i_drv.o > sun4i-drm-y += sun4i_framebuffer.o > @@ -21,6 +22,6 @@ obj-$(CONFIG_DRM_SUN4I) += sun4i-tcon.o > obj-$(CONFIG_DRM_SUN4I) += sun4i_tv.o > obj-$(CONFIG_DRM_SUN4I) += sun6i_drc.o > > -obj-$(CONFIG_DRM_SUN4I_BACKEND) += sun4i-backend.o > +obj-$(CONFIG_DRM_SUN4I_BACKEND) += sun4i-backend.o sun4i-frontend.o > obj-$(CONFIG_DRM_SUN4I_HDMI) += sun4i-drm-hdmi.o > obj-$(CONFIG_DRM_SUN8I_MIXER) += sun8i-mixer.o > diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c > index f971d3fb5ee4..e83e1fe43823 100644 > --- a/drivers/gpu/drm/sun4i/sun4i_backend.c > +++ b/drivers/gpu/drm/sun4i/sun4i_backend.c > @@ -367,6 +367,11 @@ static int sun4i_backend_bind(struct device *dev, struct device *master, > if (backend->engine.id < 0) > return backend->engine.id; > > + backend->frontend = sun4i_backend_find_frontend(drv, dev->of_node); > + if (IS_ERR(backend->frontend)) { > + dev_err(dev, "Couldn't find matching frontend, frontend features disabled\n"); Maybe a dev_warn ? > + } > + > res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > regs = devm_ioremap_resource(dev, res); > if (IS_ERR(regs)) > diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.h b/drivers/gpu/drm/sun4i/sun4i_backend.h > index ac3cc029f5cd..ba1410fd5410 100644 > --- a/drivers/gpu/drm/sun4i/sun4i_backend.h > +++ b/drivers/gpu/drm/sun4i/sun4i_backend.h > @@ -145,6 +145,7 @@ > > struct sun4i_backend { > struct sunxi_engine engine; > + struct sun4i_frontend *frontend; > > struct reset_control *reset; > > diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c > index 75c76cdd82bc..17bf9bfd98ba 100644 > --- a/drivers/gpu/drm/sun4i/sun4i_drv.c > +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c > @@ -98,6 +98,7 @@ static int sun4i_drv_bind(struct device *dev) > goto free_drm; > } > drm->dev_private = drv; > + INIT_LIST_HEAD(&drv->frontend_list); > INIT_LIST_HEAD(&drv->engine_list); > INIT_LIST_HEAD(&drv->tcon_list); > > @@ -239,9 +240,11 @@ static int sun4i_drv_add_endpoints(struct device *dev, > int count = 0; > > /* > - * We don't support the frontend for now, so we will never > - * have a device bound. Just skip over it, but we still want > - * the rest our pipeline to be added. > + * The frontend has been disabled in all of our old device > + * trees. If we find a node that is the frontend and is > + * disabled, we should just follow through and parse its > + * child, but without adding it to the component list. > + * Otherwise, we obviously want to add it to the list. > */ > if (!sun4i_drv_node_is_frontend(node) && > !of_device_is_available(node)) > @@ -254,7 +257,12 @@ static int sun4i_drv_add_endpoints(struct device *dev, > if (sun4i_drv_node_is_connector(node)) > return 0; > > - if (!sun4i_drv_node_is_frontend(node)) { > + /* > + * If the device is either just a regular device, or an > + * enabled frontend, we add it to our component list. > + */ > + if (!sun4i_drv_node_is_frontend(node) || > + (sun4i_drv_node_is_frontend(node) && of_device_is_available(node))) { > /* Add current component */ > DRM_DEBUG_DRIVER("Adding component %pOF\n", node); > drm_of_component_match_add(dev, match, compare_of, node); > diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.h b/drivers/gpu/drm/sun4i/sun4i_drv.h > index a960c89270cc..9c26a345f85c 100644 > --- a/drivers/gpu/drm/sun4i/sun4i_drv.h > +++ b/drivers/gpu/drm/sun4i/sun4i_drv.h > @@ -19,6 +19,7 @@ > > struct sun4i_drv { > struct list_head engine_list; > + struct list_head frontend_list; > struct list_head tcon_list; > > struct drm_fbdev_cma *fbdev; > diff --git a/drivers/gpu/drm/sun4i/sun4i_frontend.c b/drivers/gpu/drm/sun4i/sun4i_frontend.c > new file mode 100644 > index 000000000000..1be0e86d1457 > --- /dev/null > +++ b/drivers/gpu/drm/sun4i/sun4i_frontend.c > @@ -0,0 +1,377 @@ // SPDX-Licence-Identifier... new format ? > +/* > + * Copyright (C) 2017 Free Electrons > + * > + * Maxime Ripard <maxime.ripard@free-electrons.com> > + * > + * 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. > + */ > +#include <drm/drmP.h> > +#include <drm/drm_gem_cma_helper.h> > +#include <drm/drm_fb_cma_helper.h> > + > +#include <linux/clk.h> > +#include <linux/component.h> > +#include <linux/module.h> > +#include <linux/platform_device.h> > +#include <linux/regmap.h> > +#include <linux/reset.h> > + > +#include "sun4i_drv.h" > +#include "sun4i_frontend.h" > + > +static const u32 sun4i_frontend_vert_coef[32] = { > + 0x00004000, 0x000140ff, 0x00033ffe, 0x00043ffd, > + 0x00063efc, 0xff083dfc, 0x000a3bfb, 0xff0d39fb, > + 0xff0f37fb, 0xff1136fa, 0xfe1433fb, 0xfe1631fb, > + 0xfd192ffb, 0xfd1c2cfb, 0xfd1f29fb, 0xfc2127fc, > + 0xfc2424fc, 0xfc2721fc, 0xfb291ffd, 0xfb2c1cfd, > + 0xfb2f19fd, 0xfb3116fe, 0xfb3314fe, 0xfa3611ff, > + 0xfb370fff, 0xfb390dff, 0xfb3b0a00, 0xfc3d08ff, > + 0xfc3e0600, 0xfd3f0400, 0xfe3f0300, 0xff400100, > +}; > + > +static const u32 sun4i_frontend_horz_coef[64] = { > + 0x40000000, 0x00000000, 0x40fe0000, 0x0000ff03, > + 0x3ffd0000, 0x0000ff05, 0x3ffc0000, 0x0000ff06, > + 0x3efb0000, 0x0000ff08, 0x3dfb0000, 0x0000ff09, > + 0x3bfa0000, 0x0000fe0d, 0x39fa0000, 0x0000fe0f, > + 0x38fa0000, 0x0000fe10, 0x36fa0000, 0x0000fe12, > + 0x33fa0000, 0x0000fd16, 0x31fa0000, 0x0000fd18, > + 0x2ffa0000, 0x0000fd1a, 0x2cfa0000, 0x0000fc1e, > + 0x29fa0000, 0x0000fc21, 0x27fb0000, 0x0000fb23, > + 0x24fb0000, 0x0000fb26, 0x21fb0000, 0x0000fb29, > + 0x1ffc0000, 0x0000fa2b, 0x1cfc0000, 0x0000fa2e, > + 0x19fd0000, 0x0000fa30, 0x16fd0000, 0x0000fa33, > + 0x14fd0000, 0x0000fa35, 0x11fe0000, 0x0000fa37, > + 0x0ffe0000, 0x0000fa39, 0x0dfe0000, 0x0000fa3b, > + 0x0afe0000, 0x0000fa3e, 0x08ff0000, 0x0000fb3e, > + 0x06ff0000, 0x0000fb40, 0x05ff0000, 0x0000fc40, > + 0x03ff0000, 0x0000fd41, 0x01ff0000, 0x0000fe42, > +}; > + > +static void sun4i_frontend_scaler_init(struct sun4i_frontend *frontend) > +{ > + int i; > + > + for (i = 0; i < 32; i++) { > + regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZCOEF0_REG(i), > + sun4i_frontend_horz_coef[2 * i]); > + regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZCOEF0_REG(i), > + sun4i_frontend_horz_coef[2 * i]); > + regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZCOEF1_REG(i), > + sun4i_frontend_horz_coef[2 * i + 1]); > + regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZCOEF1_REG(i), > + sun4i_frontend_horz_coef[2 * i + 1]); > + regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTCOEF_REG(i), > + sun4i_frontend_vert_coef[i]); > + regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTCOEF_REG(i), > + sun4i_frontend_vert_coef[i]); > + } > + > + regmap_update_bits(frontend->regs, SUN4I_FRONTEND_FRM_CTRL_REG, BIT(23), BIT(23)); > +} > + > +int sun4i_frontend_init(struct sun4i_frontend *frontend) > +{ > + int ret; > + > + if (atomic_read(&frontend->users)) > + return -EBUSY; > + > + ret = reset_control_deassert(frontend->reset); > + if (ret) { > + DRM_DEBUG_DRIVER("Couldn't deassert our reset line\n"); > + return ret; > + } > + > + clk_set_rate(frontend->mod_clk, 300000000); > + > + clk_prepare_enable(frontend->bus_clk); > + clk_prepare_enable(frontend->mod_clk); > + clk_prepare_enable(frontend->ram_clk); > + > + regmap_update_bits(frontend->regs, SUN4I_FRONTEND_EN_REG, > + SUN4I_FRONTEND_EN_EN, > + SUN4I_FRONTEND_EN_EN); > + > + regmap_update_bits(frontend->regs, SUN4I_FRONTEND_BYPASS_REG, > + SUN4I_FRONTEND_BYPASS_CSC_EN, > + SUN4I_FRONTEND_BYPASS_CSC_EN); > + > + sun4i_frontend_scaler_init(frontend); > + > + atomic_inc(&frontend->users); > + > + return 0; > +} > +EXPORT_SYMBOL(sun4i_frontend_init); > + > +void sun4i_frontend_exit(struct sun4i_frontend *frontend) > +{ > + atomic_dec(&frontend->users); > + > + clk_disable_unprepare(frontend->ram_clk); > + clk_disable_unprepare(frontend->mod_clk); > + clk_disable_unprepare(frontend->bus_clk); Maybe a blank line ? > + reset_control_assert(frontend->reset); > +} > +EXPORT_SYMBOL(sun4i_frontend_exit); > + > +void sun4i_frontend_update_buffer(struct sun4i_frontend *frontend, > + struct drm_plane *plane) > +{ > + struct drm_plane_state *state = plane->state; > + struct drm_framebuffer *fb = state->fb; > + struct drm_gem_cma_object *gem; > + dma_addr_t paddr; > + int bpp; > + > + /* Get the physical address of the buffer in memory */ > + gem = drm_fb_cma_get_gem_obj(fb, 0); > + > + DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr); > + > + /* Set the line width */ > + DRM_DEBUG_DRIVER("Frontend stride: %d bytes\n", fb->pitches[0]); > + regmap_write(frontend->regs, SUN4I_FRONTEND_LINESTRD0_REG, > + fb->pitches[0]); > + > + /* Compute the start of the displayed memory */ > + bpp = fb->format->cpp[0]; > + paddr = gem->paddr + fb->offsets[0]; > + paddr += (state->src_x >> 16) * bpp; > + paddr += (state->src_y >> 16) * fb->pitches[0]; > + > + DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr); > + regmap_write(frontend->regs, SUN4I_FRONTEND_BUF_ADDR0_REG, paddr); > +} > + > +static int sun4i_frontend_drm_format_to_input_fmt(uint32_t fmt, u32 *val) > +{ > + switch (fmt) { > + case DRM_FORMAT_ARGB8888: > + case DRM_FORMAT_XRGB8888: > + *val = 3; > + return 0; > + > + default: > + return -EINVAL; > + } > +} > + > +static int sun4i_frontend_drm_format_to_output_fmt(uint32_t fmt, u32 *val) > +{ > + switch (fmt) { > + case DRM_FORMAT_ARGB8888: > + *val = 2; > + return 0; > + > + default: > + return -EINVAL; > + } > +} > + > +int sun4i_frontend_update_formats(struct sun4i_frontend *frontend, > + struct drm_plane *plane, uint32_t out_fmt) > +{ > + struct drm_plane_state *state = plane->state; > + struct drm_framebuffer *fb = state->fb; > + u32 out_fmt_val; > + u32 in_fmt_val; > + int ret; > + > + ret = sun4i_frontend_drm_format_to_input_fmt(fb->format->format, > + &in_fmt_val); > + if (ret) { > + DRM_DEBUG_DRIVER("Invalid input format\n"); > + return ret; > + } > + > + ret = sun4i_frontend_drm_format_to_output_fmt(out_fmt, &out_fmt_val); > + if (ret) { > + DRM_DEBUG_DRIVER("Invalid output format\n"); > + return ret; > + } > + > + regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZPHASE_REG, 0x400); > + regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZPHASE_REG, 0x400); > + > + regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTPHASE0_REG, 0x400); > + regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTPHASE0_REG, 0x400); > + > + regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTPHASE1_REG, 0x400); > + regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTPHASE1_REG, 0x400); > + > + regmap_write(frontend->regs, SUN4I_FRONTEND_INPUT_FMT_REG, 0x151); > + /* SUN4I_FRONTEND_INPUT_FMT_DATA_MOD(1) | */ > + /* SUN4I_FRONTEND_INPUT_FMT_DATA_FMT(5) | */ > + /* SUN4I_FRONTEND_INPUT_FMT_PS(1)); */ Why are these commented ? > + regmap_write(frontend->regs, SUN4I_FRONTEND_OUTPUT_FMT_REG, 0x82); > + /* SUN4I_FRONTEND_OUTPUT_FMT_DATA_FMT(1)); */ Same here > + > + return 0; > +} > + > +void sun4i_frontend_update_coord(struct sun4i_frontend *frontend, > + struct drm_plane *plane) > +{ > + struct drm_plane_state *state = plane->state; > + > + /* Set height and width */ > + DRM_DEBUG_DRIVER("Frontend size W: %u H: %u\n", > + state->crtc_w, state->crtc_h); > + regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_INSIZE_REG, > + SUN4I_FRONTEND_INSIZE(state->src_h >> 16, > + state->src_w >> 16)); > + regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_INSIZE_REG, > + SUN4I_FRONTEND_INSIZE(state->src_h >> 16, > + state->src_w >> 16)); > + > + regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_OUTSIZE_REG, > + SUN4I_FRONTEND_OUTSIZE(state->crtc_h, state->crtc_w)); > + regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_OUTSIZE_REG, > + SUN4I_FRONTEND_OUTSIZE(state->crtc_h, state->crtc_w)); > + > + DRM_DEBUG_DRIVER("Frontend horizontal scaling factor %d.%d\n", 1, 0); > + regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZFACT_REG, > + state->src_w / state->crtc_w); > + regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZFACT_REG, > + state->src_w / state->crtc_w); > + > + DRM_DEBUG_DRIVER("Frontend vertical scaling factor %d.%d\n", 1, 0); > + regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTFACT_REG, > + state->src_h / state->crtc_h); > + regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTFACT_REG, > + state->src_h / state->crtc_h); > + > + regmap_write_bits(frontend->regs, SUN4I_FRONTEND_FRM_CTRL_REG, > + SUN4I_FRONTEND_FRM_CTRL_REG_RDY, > + SUN4I_FRONTEND_FRM_CTRL_REG_RDY); > +} > +EXPORT_SYMBOL(sun4i_frontend_update_coord); > + > +int sun4i_frontend_enable(struct sun4i_frontend *frontend) > +{ > + regmap_write_bits(frontend->regs, SUN4I_FRONTEND_FRM_CTRL_REG, > + SUN4I_FRONTEND_FRM_CTRL_FRM_START, > + SUN4I_FRONTEND_FRM_CTRL_FRM_START); > + > + return 0; > +} > +EXPORT_SYMBOL(sun4i_frontend_enable); > + > +static struct regmap_config sun4i_frontend_regmap_config = { > + .reg_bits = 32, > + .val_bits = 32, > + .reg_stride = 4, > + .max_register = 0x0a14, > +}; > + > +static int sun4i_frontend_bind(struct device *dev, struct device *master, > + void *data) > +{ > + struct platform_device *pdev = to_platform_device(dev); > + struct sun4i_frontend *frontend; > + struct drm_device *drm = data; > + struct sun4i_drv *drv = drm->dev_private; > + struct resource *res; > + void __iomem *regs; > + > + frontend = devm_kzalloc(dev, sizeof(*frontend), GFP_KERNEL); > + if (!frontend) > + return -ENOMEM; Blank line ? > + dev_set_drvdata(dev, frontend); > + frontend->node = dev->of_node; > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + regs = devm_ioremap_resource(dev, res); > + if (IS_ERR(regs)) > + return PTR_ERR(regs); > + > + frontend->regs = devm_regmap_init_mmio(dev, regs, > + &sun4i_frontend_regmap_config); > + if (IS_ERR(frontend->regs)) { > + dev_err(dev, "Couldn't create the frontend regmap\n"); > + return PTR_ERR(frontend->regs); > + } > + > + frontend->reset = devm_reset_control_get(dev, NULL); > + if (IS_ERR(frontend->reset)) { > + dev_err(dev, "Couldn't get our reset line\n"); > + return PTR_ERR(frontend->reset); > + } > + > + frontend->bus_clk = devm_clk_get(dev, "ahb"); > + if (IS_ERR(frontend->bus_clk)) { > + dev_err(dev, "Couldn't get our bus clock\n"); > + return PTR_ERR(frontend->bus_clk); > + } > + > + frontend->mod_clk = devm_clk_get(dev, "mod"); > + if (IS_ERR(frontend->mod_clk)) { > + dev_err(dev, "Couldn't get our mod clock\n"); > + return PTR_ERR(frontend->mod_clk); > + } > + > + frontend->ram_clk = devm_clk_get(dev, "ram"); > + if (IS_ERR(frontend->ram_clk)) { > + dev_err(dev, "Couldn't get our ram clock\n"); > + return PTR_ERR(frontend->ram_clk); > + } > + > + list_add_tail(&frontend->list, &drv->frontend_list); > + > + return 0; > +} > + > +static void sun4i_frontend_unbind(struct device *dev, struct device *master, > + void *data) > +{ > + struct sun4i_frontend *frontend = dev_get_drvdata(dev); > + > + clk_disable_unprepare(frontend->mod_clk); > + clk_disable_unprepare(frontend->bus_clk); Blank line ? > + reset_control_assert(frontend->reset); > +} > + > +static const struct component_ops sun4i_frontend_ops = { > + .bind = sun4i_frontend_bind, > + .unbind = sun4i_frontend_unbind, > +}; > + > +static int sun4i_frontend_probe(struct platform_device *pdev) > +{ > + return component_add(&pdev->dev, &sun4i_frontend_ops); > +} > + > +static int sun4i_frontend_remove(struct platform_device *pdev) > +{ > + component_del(&pdev->dev, &sun4i_frontend_ops); > + > + return 0; > +} > + > +static const struct of_device_id sun4i_frontend_of_table[] = { > + { .compatible = "allwinner,sun5i-a13-display-frontend" }, > + { .compatible = "allwinner,sun6i-a31-display-frontend" }, > + { .compatible = "allwinner,sun8i-a33-display-frontend" }, > + { } > +}; > +MODULE_DEVICE_TABLE(of, sun4i_frontend_of_table); > + > +static struct platform_driver sun4i_frontend_driver = { > + .probe = sun4i_frontend_probe, > + .remove = sun4i_frontend_remove, > + .driver = { > + .name = "sun4i-frontend", > + .of_match_table = sun4i_frontend_of_table, > + }, > +}; > +module_platform_driver(sun4i_frontend_driver); > + > +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); > +MODULE_DESCRIPTION("Allwinner A10 Display Engine Frontend Driver"); > +MODULE_LICENSE("GPL"); > diff --git a/drivers/gpu/drm/sun4i/sun4i_frontend.h b/drivers/gpu/drm/sun4i/sun4i_frontend.h > new file mode 100644 > index 000000000000..2df9f6de0f3f > --- /dev/null > +++ b/drivers/gpu/drm/sun4i/sun4i_frontend.h > @@ -0,0 +1,102 @@ // SPDX-Licence-Identifier... new format ? > +/* > + * Copyright (C) 2017 Free Electrons > + * > + * Maxime Ripard <maxime.ripard@free-electrons.com> > + * > + * 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. > + */ > + > +#ifndef _SUN4I_FRONTEND_H_ > +#define _SUN4I_FRONTEND_H_ > + > +#include <linux/atomic.h> > +#include <linux/list.h> > + > +#define SUN4I_FRONTEND_EN_REG 0x000 > +#define SUN4I_FRONTEND_EN_EN BIT(0) > + > +#define SUN4I_FRONTEND_FRM_CTRL_REG 0x004 > +#define SUN4I_FRONTEND_FRM_CTRL_FRM_START BIT(16) > +#define SUN4I_FRONTEND_FRM_CTRL_COEF_RDY BIT(1) > +#define SUN4I_FRONTEND_FRM_CTRL_REG_RDY BIT(0) > + > +#define SUN4I_FRONTEND_BYPASS_REG 0x008 > +#define SUN4I_FRONTEND_BYPASS_CSC_EN BIT(1) > + > +#define SUN4I_FRONTEND_BUF_ADDR0_REG 0x020 > + > +#define SUN4I_FRONTEND_LINESTRD0_REG 0x040 > + > +#define SUN4I_FRONTEND_INPUT_FMT_REG 0x04c > +#define SUN4I_FRONTEND_INPUT_FMT_DATA_MOD(mod) ((mod) << 8) > +#define SUN4I_FRONTEND_INPUT_FMT_DATA_FMT(fmt) ((fmt) << 4) > +#define SUN4I_FRONTEND_INPUT_FMT_PS(ps) (ps) > + > +#define SUN4I_FRONTEND_OUTPUT_FMT_REG 0x05c > +#define SUN4I_FRONTEND_OUTPUT_FMT_DATA_FMT(fmt) (fmt) > + > +#define SUN4I_FRONTEND_CH0_INSIZE_REG 0x100 > +#define SUN4I_FRONTEND_INSIZE(h, w) ((((h) - 1) << 16) | (((w) - 1))) > + > +#define SUN4I_FRONTEND_CH0_OUTSIZE_REG 0x104 > +#define SUN4I_FRONTEND_OUTSIZE(h, w) ((((h) - 1) << 16) | (((w) - 1))) > + > +#define SUN4I_FRONTEND_CH0_HORZFACT_REG 0x108 > +#define SUN4I_FRONTEND_HORZFACT(i, f) (((i) << 16) | (f)) > + > +#define SUN4I_FRONTEND_CH0_VERTFACT_REG 0x10c > +#define SUN4I_FRONTEND_VERTFACT(i, f) (((i) << 16) | (f)) > + > +#define SUN4I_FRONTEND_CH0_HORZPHASE_REG 0x110 > +#define SUN4I_FRONTEND_CH0_VERTPHASE0_REG 0x114 > +#define SUN4I_FRONTEND_CH0_VERTPHASE1_REG 0x118 > + > +#define SUN4I_FRONTEND_CH1_INSIZE_REG 0x200 > +#define SUN4I_FRONTEND_CH1_OUTSIZE_REG 0x204 > +#define SUN4I_FRONTEND_CH1_HORZFACT_REG 0x208 > +#define SUN4I_FRONTEND_CH1_VERTFACT_REG 0x20c > + > +#define SUN4I_FRONTEND_CH1_HORZPHASE_REG 0x210 > +#define SUN4I_FRONTEND_CH1_VERTPHASE0_REG 0x214 > +#define SUN4I_FRONTEND_CH1_VERTPHASE1_REG 0x218 > + > +#define SUN4I_FRONTEND_CH0_HORZCOEF0_REG(i) (0x400 + i * 4) > +#define SUN4I_FRONTEND_CH0_HORZCOEF1_REG(i) (0x480 + i * 4) > +#define SUN4I_FRONTEND_CH0_VERTCOEF_REG(i) (0x500 + i * 4) > +#define SUN4I_FRONTEND_CH1_HORZCOEF0_REG(i) (0x600 + i * 4) > +#define SUN4I_FRONTEND_CH1_HORZCOEF1_REG(i) (0x680 + i * 4) > +#define SUN4I_FRONTEND_CH1_VERTCOEF_REG(i) (0x700 + i * 4) > + > +struct clk; > +struct device_node; > +struct drm_plane; > +struct regmap; > +struct reset_control; > + > +struct sun4i_frontend { > + struct list_head list; > + atomic_t users; > + struct device_node *node; > + > + struct clk *bus_clk; > + struct clk *mod_clk; > + struct clk *ram_clk; > + struct regmap *regs; > + struct reset_control *reset; > +}; > + > +int sun4i_frontend_init(struct sun4i_frontend *frontend); > +void sun4i_frontend_exit(struct sun4i_frontend *frontend); > +int sun4i_frontend_enable(struct sun4i_frontend *frontend); > + > +void sun4i_frontend_update_buffer(struct sun4i_frontend *frontend, > + struct drm_plane *plane); > +void sun4i_frontend_update_coord(struct sun4i_frontend *frontend, > + struct drm_plane *plane); > +int sun4i_frontend_update_formats(struct sun4i_frontend *frontend, > + struct drm_plane *plane, uint32_t out_fmt); > + > +#endif /* _SUN4I_FRONTEND_H_ */ > Apart these nits, Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
Hi Maxime, I love your patch! Yet something to improve: [auto build test ERROR on ] url: https://github.com/0day-ci/linux/commits/Maxime-Ripard/drm-sun4i-Support-the-Display-Engine-frontend/20171216-122702 base: config: arm-sunxi_defconfig (attached as .config) compiler: arm-linux-gnueabi-gcc (Debian 7.2.0-11) 7.2.0 reproduce: wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # save the attached .config to linux build tree make.cross ARCH=arm Note: the linux-review/Maxime-Ripard/drm-sun4i-Support-the-Display-Engine-frontend/20171216-122702 HEAD c38c4ce4b14c4c68a9fde0cc35ead5b1c894776b builds fine. It only hurts bisectibility. All error/warnings (new ones prefixed by >>): drivers/gpu/drm/sun4i/sun4i_backend.c: In function 'sun4i_backend_bind': >> drivers/gpu/drm/sun4i/sun4i_backend.c:370:22: error: implicit declaration of function 'sun4i_backend_find_frontend'; did you mean 'sun4i_backend_bind'? [-Werror=implicit-function-declaration] backend->frontend = sun4i_backend_find_frontend(drv, dev->of_node); ^~~~~~~~~~~~~~~~~~~~~~~~~~~ sun4i_backend_bind >> drivers/gpu/drm/sun4i/sun4i_backend.c:370:20: warning: assignment makes pointer from integer without a cast [-Wint-conversion] backend->frontend = sun4i_backend_find_frontend(drv, dev->of_node); ^ cc1: some warnings being treated as errors vim +370 drivers/gpu/drm/sun4i/sun4i_backend.c 346 347 static int sun4i_backend_bind(struct device *dev, struct device *master, 348 void *data) 349 { 350 struct platform_device *pdev = to_platform_device(dev); 351 struct drm_device *drm = data; 352 struct sun4i_drv *drv = drm->dev_private; 353 struct sun4i_backend *backend; 354 const struct sun4i_backend_quirks *quirks; 355 struct resource *res; 356 void __iomem *regs; 357 int i, ret; 358 359 backend = devm_kzalloc(dev, sizeof(*backend), GFP_KERNEL); 360 if (!backend) 361 return -ENOMEM; 362 dev_set_drvdata(dev, backend); 363 364 backend->engine.node = dev->of_node; 365 backend->engine.ops = &sun4i_backend_engine_ops; 366 backend->engine.id = sun4i_backend_of_get_id(dev->of_node); 367 if (backend->engine.id < 0) 368 return backend->engine.id; 369 > 370 backend->frontend = sun4i_backend_find_frontend(drv, dev->of_node); 371 if (IS_ERR(backend->frontend)) { 372 dev_err(dev, "Couldn't find matching frontend, frontend features disabled\n"); 373 } 374 375 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 376 regs = devm_ioremap_resource(dev, res); 377 if (IS_ERR(regs)) 378 return PTR_ERR(regs); 379 380 backend->reset = devm_reset_control_get(dev, NULL); 381 if (IS_ERR(backend->reset)) { 382 dev_err(dev, "Couldn't get our reset line\n"); 383 return PTR_ERR(backend->reset); 384 } 385 386 ret = reset_control_deassert(backend->reset); 387 if (ret) { 388 dev_err(dev, "Couldn't deassert our reset line\n"); 389 return ret; 390 } 391 392 backend->bus_clk = devm_clk_get(dev, "ahb"); 393 if (IS_ERR(backend->bus_clk)) { 394 dev_err(dev, "Couldn't get the backend bus clock\n"); 395 ret = PTR_ERR(backend->bus_clk); 396 goto err_assert_reset; 397 } 398 clk_prepare_enable(backend->bus_clk); 399 400 backend->mod_clk = devm_clk_get(dev, "mod"); 401 if (IS_ERR(backend->mod_clk)) { 402 dev_err(dev, "Couldn't get the backend module clock\n"); 403 ret = PTR_ERR(backend->mod_clk); 404 goto err_disable_bus_clk; 405 } 406 clk_prepare_enable(backend->mod_clk); 407 408 backend->ram_clk = devm_clk_get(dev, "ram"); 409 if (IS_ERR(backend->ram_clk)) { 410 dev_err(dev, "Couldn't get the backend RAM clock\n"); 411 ret = PTR_ERR(backend->ram_clk); 412 goto err_disable_mod_clk; 413 } 414 clk_prepare_enable(backend->ram_clk); 415 416 if (of_device_is_compatible(dev->of_node, 417 "allwinner,sun8i-a33-display-backend")) { 418 ret = sun4i_backend_init_sat(dev); 419 if (ret) { 420 dev_err(dev, "Couldn't init SAT resources\n"); 421 goto err_disable_ram_clk; 422 } 423 } 424 425 backend->engine.regs = devm_regmap_init_mmio(dev, regs, 426 &sun4i_backend_regmap_config); 427 if (IS_ERR(backend->engine.regs)) { 428 dev_err(dev, "Couldn't create the backend regmap\n"); 429 return PTR_ERR(backend->engine.regs); 430 } 431 432 list_add_tail(&backend->engine.list, &drv->engine_list); 433 434 /* 435 * Many of the backend's layer configuration registers have 436 * undefined default values. This poses a risk as we use 437 * regmap_update_bits in some places, and don't overwrite 438 * the whole register. 439 * 440 * Clear the registers here to have something predictable. 441 */ 442 for (i = 0x800; i < 0x1000; i += 4) 443 regmap_write(backend->engine.regs, i, 0); 444 445 /* Disable registers autoloading */ 446 regmap_write(backend->engine.regs, SUN4I_BACKEND_REGBUFFCTL_REG, 447 SUN4I_BACKEND_REGBUFFCTL_AUTOLOAD_DIS); 448 449 /* Enable the backend */ 450 regmap_write(backend->engine.regs, SUN4I_BACKEND_MODCTL_REG, 451 SUN4I_BACKEND_MODCTL_DEBE_EN | 452 SUN4I_BACKEND_MODCTL_START_CTL); 453 454 /* Set output selection if needed */ 455 quirks = of_device_get_match_data(dev); 456 if (quirks->needs_output_muxing) { 457 /* 458 * We assume there is no dynamic muxing of backends 459 * and TCONs, so we select the backend with same ID. 460 * 461 * While dynamic selection might be interesting, since 462 * the CRTC is tied to the TCON, while the layers are 463 * tied to the backends, this means, we will need to 464 * switch between groups of layers. There might not be 465 * a way to represent this constraint in DRM. 466 */ 467 regmap_update_bits(backend->engine.regs, 468 SUN4I_BACKEND_MODCTL_REG, 469 SUN4I_BACKEND_MODCTL_OUT_SEL, 470 (backend->engine.id 471 ? SUN4I_BACKEND_MODCTL_OUT_LCD1 472 : SUN4I_BACKEND_MODCTL_OUT_LCD0)); 473 } 474 475 return 0; 476 477 err_disable_ram_clk: 478 clk_disable_unprepare(backend->ram_clk); 479 err_disable_mod_clk: 480 clk_disable_unprepare(backend->mod_clk); 481 err_disable_bus_clk: 482 clk_disable_unprepare(backend->bus_clk); 483 err_assert_reset: 484 reset_control_assert(backend->reset); 485 return ret; 486 } 487 --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation
diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile index 0c2f8c7facae..b660d82011f4 100644 --- a/drivers/gpu/drm/sun4i/Makefile +++ b/drivers/gpu/drm/sun4i/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 sun4i-backend-y += sun4i_backend.o sun4i_layer.o +sun4i-frontend-y += sun4i_frontend.o sun4i-drm-y += sun4i_drv.o sun4i-drm-y += sun4i_framebuffer.o @@ -21,6 +22,6 @@ obj-$(CONFIG_DRM_SUN4I) += sun4i-tcon.o obj-$(CONFIG_DRM_SUN4I) += sun4i_tv.o obj-$(CONFIG_DRM_SUN4I) += sun6i_drc.o -obj-$(CONFIG_DRM_SUN4I_BACKEND) += sun4i-backend.o +obj-$(CONFIG_DRM_SUN4I_BACKEND) += sun4i-backend.o sun4i-frontend.o obj-$(CONFIG_DRM_SUN4I_HDMI) += sun4i-drm-hdmi.o obj-$(CONFIG_DRM_SUN8I_MIXER) += sun8i-mixer.o diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c index f971d3fb5ee4..e83e1fe43823 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.c +++ b/drivers/gpu/drm/sun4i/sun4i_backend.c @@ -367,6 +367,11 @@ static int sun4i_backend_bind(struct device *dev, struct device *master, if (backend->engine.id < 0) return backend->engine.id; + backend->frontend = sun4i_backend_find_frontend(drv, dev->of_node); + if (IS_ERR(backend->frontend)) { + dev_err(dev, "Couldn't find matching frontend, frontend features disabled\n"); + } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); regs = devm_ioremap_resource(dev, res); if (IS_ERR(regs)) diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.h b/drivers/gpu/drm/sun4i/sun4i_backend.h index ac3cc029f5cd..ba1410fd5410 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.h +++ b/drivers/gpu/drm/sun4i/sun4i_backend.h @@ -145,6 +145,7 @@ struct sun4i_backend { struct sunxi_engine engine; + struct sun4i_frontend *frontend; struct reset_control *reset; diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c index 75c76cdd82bc..17bf9bfd98ba 100644 --- a/drivers/gpu/drm/sun4i/sun4i_drv.c +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c @@ -98,6 +98,7 @@ static int sun4i_drv_bind(struct device *dev) goto free_drm; } drm->dev_private = drv; + INIT_LIST_HEAD(&drv->frontend_list); INIT_LIST_HEAD(&drv->engine_list); INIT_LIST_HEAD(&drv->tcon_list); @@ -239,9 +240,11 @@ static int sun4i_drv_add_endpoints(struct device *dev, int count = 0; /* - * We don't support the frontend for now, so we will never - * have a device bound. Just skip over it, but we still want - * the rest our pipeline to be added. + * The frontend has been disabled in all of our old device + * trees. If we find a node that is the frontend and is + * disabled, we should just follow through and parse its + * child, but without adding it to the component list. + * Otherwise, we obviously want to add it to the list. */ if (!sun4i_drv_node_is_frontend(node) && !of_device_is_available(node)) @@ -254,7 +257,12 @@ static int sun4i_drv_add_endpoints(struct device *dev, if (sun4i_drv_node_is_connector(node)) return 0; - if (!sun4i_drv_node_is_frontend(node)) { + /* + * If the device is either just a regular device, or an + * enabled frontend, we add it to our component list. + */ + if (!sun4i_drv_node_is_frontend(node) || + (sun4i_drv_node_is_frontend(node) && of_device_is_available(node))) { /* Add current component */ DRM_DEBUG_DRIVER("Adding component %pOF\n", node); drm_of_component_match_add(dev, match, compare_of, node); diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.h b/drivers/gpu/drm/sun4i/sun4i_drv.h index a960c89270cc..9c26a345f85c 100644 --- a/drivers/gpu/drm/sun4i/sun4i_drv.h +++ b/drivers/gpu/drm/sun4i/sun4i_drv.h @@ -19,6 +19,7 @@ struct sun4i_drv { struct list_head engine_list; + struct list_head frontend_list; struct list_head tcon_list; struct drm_fbdev_cma *fbdev; diff --git a/drivers/gpu/drm/sun4i/sun4i_frontend.c b/drivers/gpu/drm/sun4i/sun4i_frontend.c new file mode 100644 index 000000000000..1be0e86d1457 --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun4i_frontend.c @@ -0,0 +1,377 @@ +/* + * Copyright (C) 2017 Free Electrons + * + * Maxime Ripard <maxime.ripard@free-electrons.com> + * + * 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. + */ +#include <drm/drmP.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_fb_cma_helper.h> + +#include <linux/clk.h> +#include <linux/component.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/reset.h> + +#include "sun4i_drv.h" +#include "sun4i_frontend.h" + +static const u32 sun4i_frontend_vert_coef[32] = { + 0x00004000, 0x000140ff, 0x00033ffe, 0x00043ffd, + 0x00063efc, 0xff083dfc, 0x000a3bfb, 0xff0d39fb, + 0xff0f37fb, 0xff1136fa, 0xfe1433fb, 0xfe1631fb, + 0xfd192ffb, 0xfd1c2cfb, 0xfd1f29fb, 0xfc2127fc, + 0xfc2424fc, 0xfc2721fc, 0xfb291ffd, 0xfb2c1cfd, + 0xfb2f19fd, 0xfb3116fe, 0xfb3314fe, 0xfa3611ff, + 0xfb370fff, 0xfb390dff, 0xfb3b0a00, 0xfc3d08ff, + 0xfc3e0600, 0xfd3f0400, 0xfe3f0300, 0xff400100, +}; + +static const u32 sun4i_frontend_horz_coef[64] = { + 0x40000000, 0x00000000, 0x40fe0000, 0x0000ff03, + 0x3ffd0000, 0x0000ff05, 0x3ffc0000, 0x0000ff06, + 0x3efb0000, 0x0000ff08, 0x3dfb0000, 0x0000ff09, + 0x3bfa0000, 0x0000fe0d, 0x39fa0000, 0x0000fe0f, + 0x38fa0000, 0x0000fe10, 0x36fa0000, 0x0000fe12, + 0x33fa0000, 0x0000fd16, 0x31fa0000, 0x0000fd18, + 0x2ffa0000, 0x0000fd1a, 0x2cfa0000, 0x0000fc1e, + 0x29fa0000, 0x0000fc21, 0x27fb0000, 0x0000fb23, + 0x24fb0000, 0x0000fb26, 0x21fb0000, 0x0000fb29, + 0x1ffc0000, 0x0000fa2b, 0x1cfc0000, 0x0000fa2e, + 0x19fd0000, 0x0000fa30, 0x16fd0000, 0x0000fa33, + 0x14fd0000, 0x0000fa35, 0x11fe0000, 0x0000fa37, + 0x0ffe0000, 0x0000fa39, 0x0dfe0000, 0x0000fa3b, + 0x0afe0000, 0x0000fa3e, 0x08ff0000, 0x0000fb3e, + 0x06ff0000, 0x0000fb40, 0x05ff0000, 0x0000fc40, + 0x03ff0000, 0x0000fd41, 0x01ff0000, 0x0000fe42, +}; + +static void sun4i_frontend_scaler_init(struct sun4i_frontend *frontend) +{ + int i; + + for (i = 0; i < 32; i++) { + regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZCOEF0_REG(i), + sun4i_frontend_horz_coef[2 * i]); + regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZCOEF0_REG(i), + sun4i_frontend_horz_coef[2 * i]); + regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZCOEF1_REG(i), + sun4i_frontend_horz_coef[2 * i + 1]); + regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZCOEF1_REG(i), + sun4i_frontend_horz_coef[2 * i + 1]); + regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTCOEF_REG(i), + sun4i_frontend_vert_coef[i]); + regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTCOEF_REG(i), + sun4i_frontend_vert_coef[i]); + } + + regmap_update_bits(frontend->regs, SUN4I_FRONTEND_FRM_CTRL_REG, BIT(23), BIT(23)); +} + +int sun4i_frontend_init(struct sun4i_frontend *frontend) +{ + int ret; + + if (atomic_read(&frontend->users)) + return -EBUSY; + + ret = reset_control_deassert(frontend->reset); + if (ret) { + DRM_DEBUG_DRIVER("Couldn't deassert our reset line\n"); + return ret; + } + + clk_set_rate(frontend->mod_clk, 300000000); + + clk_prepare_enable(frontend->bus_clk); + clk_prepare_enable(frontend->mod_clk); + clk_prepare_enable(frontend->ram_clk); + + regmap_update_bits(frontend->regs, SUN4I_FRONTEND_EN_REG, + SUN4I_FRONTEND_EN_EN, + SUN4I_FRONTEND_EN_EN); + + regmap_update_bits(frontend->regs, SUN4I_FRONTEND_BYPASS_REG, + SUN4I_FRONTEND_BYPASS_CSC_EN, + SUN4I_FRONTEND_BYPASS_CSC_EN); + + sun4i_frontend_scaler_init(frontend); + + atomic_inc(&frontend->users); + + return 0; +} +EXPORT_SYMBOL(sun4i_frontend_init); + +void sun4i_frontend_exit(struct sun4i_frontend *frontend) +{ + atomic_dec(&frontend->users); + + clk_disable_unprepare(frontend->ram_clk); + clk_disable_unprepare(frontend->mod_clk); + clk_disable_unprepare(frontend->bus_clk); + reset_control_assert(frontend->reset); +} +EXPORT_SYMBOL(sun4i_frontend_exit); + +void sun4i_frontend_update_buffer(struct sun4i_frontend *frontend, + struct drm_plane *plane) +{ + struct drm_plane_state *state = plane->state; + struct drm_framebuffer *fb = state->fb; + struct drm_gem_cma_object *gem; + dma_addr_t paddr; + int bpp; + + /* Get the physical address of the buffer in memory */ + gem = drm_fb_cma_get_gem_obj(fb, 0); + + DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr); + + /* Set the line width */ + DRM_DEBUG_DRIVER("Frontend stride: %d bytes\n", fb->pitches[0]); + regmap_write(frontend->regs, SUN4I_FRONTEND_LINESTRD0_REG, + fb->pitches[0]); + + /* Compute the start of the displayed memory */ + bpp = fb->format->cpp[0]; + paddr = gem->paddr + fb->offsets[0]; + paddr += (state->src_x >> 16) * bpp; + paddr += (state->src_y >> 16) * fb->pitches[0]; + + DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr); + regmap_write(frontend->regs, SUN4I_FRONTEND_BUF_ADDR0_REG, paddr); +} + +static int sun4i_frontend_drm_format_to_input_fmt(uint32_t fmt, u32 *val) +{ + switch (fmt) { + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XRGB8888: + *val = 3; + return 0; + + default: + return -EINVAL; + } +} + +static int sun4i_frontend_drm_format_to_output_fmt(uint32_t fmt, u32 *val) +{ + switch (fmt) { + case DRM_FORMAT_ARGB8888: + *val = 2; + return 0; + + default: + return -EINVAL; + } +} + +int sun4i_frontend_update_formats(struct sun4i_frontend *frontend, + struct drm_plane *plane, uint32_t out_fmt) +{ + struct drm_plane_state *state = plane->state; + struct drm_framebuffer *fb = state->fb; + u32 out_fmt_val; + u32 in_fmt_val; + int ret; + + ret = sun4i_frontend_drm_format_to_input_fmt(fb->format->format, + &in_fmt_val); + if (ret) { + DRM_DEBUG_DRIVER("Invalid input format\n"); + return ret; + } + + ret = sun4i_frontend_drm_format_to_output_fmt(out_fmt, &out_fmt_val); + if (ret) { + DRM_DEBUG_DRIVER("Invalid output format\n"); + return ret; + } + + regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZPHASE_REG, 0x400); + regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZPHASE_REG, 0x400); + + regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTPHASE0_REG, 0x400); + regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTPHASE0_REG, 0x400); + + regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTPHASE1_REG, 0x400); + regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTPHASE1_REG, 0x400); + + regmap_write(frontend->regs, SUN4I_FRONTEND_INPUT_FMT_REG, 0x151); + /* SUN4I_FRONTEND_INPUT_FMT_DATA_MOD(1) | */ + /* SUN4I_FRONTEND_INPUT_FMT_DATA_FMT(5) | */ + /* SUN4I_FRONTEND_INPUT_FMT_PS(1)); */ + regmap_write(frontend->regs, SUN4I_FRONTEND_OUTPUT_FMT_REG, 0x82); + /* SUN4I_FRONTEND_OUTPUT_FMT_DATA_FMT(1)); */ + + return 0; +} + +void sun4i_frontend_update_coord(struct sun4i_frontend *frontend, + struct drm_plane *plane) +{ + struct drm_plane_state *state = plane->state; + + /* Set height and width */ + DRM_DEBUG_DRIVER("Frontend size W: %u H: %u\n", + state->crtc_w, state->crtc_h); + regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_INSIZE_REG, + SUN4I_FRONTEND_INSIZE(state->src_h >> 16, + state->src_w >> 16)); + regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_INSIZE_REG, + SUN4I_FRONTEND_INSIZE(state->src_h >> 16, + state->src_w >> 16)); + + regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_OUTSIZE_REG, + SUN4I_FRONTEND_OUTSIZE(state->crtc_h, state->crtc_w)); + regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_OUTSIZE_REG, + SUN4I_FRONTEND_OUTSIZE(state->crtc_h, state->crtc_w)); + + DRM_DEBUG_DRIVER("Frontend horizontal scaling factor %d.%d\n", 1, 0); + regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZFACT_REG, + state->src_w / state->crtc_w); + regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZFACT_REG, + state->src_w / state->crtc_w); + + DRM_DEBUG_DRIVER("Frontend vertical scaling factor %d.%d\n", 1, 0); + regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTFACT_REG, + state->src_h / state->crtc_h); + regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTFACT_REG, + state->src_h / state->crtc_h); + + regmap_write_bits(frontend->regs, SUN4I_FRONTEND_FRM_CTRL_REG, + SUN4I_FRONTEND_FRM_CTRL_REG_RDY, + SUN4I_FRONTEND_FRM_CTRL_REG_RDY); +} +EXPORT_SYMBOL(sun4i_frontend_update_coord); + +int sun4i_frontend_enable(struct sun4i_frontend *frontend) +{ + regmap_write_bits(frontend->regs, SUN4I_FRONTEND_FRM_CTRL_REG, + SUN4I_FRONTEND_FRM_CTRL_FRM_START, + SUN4I_FRONTEND_FRM_CTRL_FRM_START); + + return 0; +} +EXPORT_SYMBOL(sun4i_frontend_enable); + +static struct regmap_config sun4i_frontend_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x0a14, +}; + +static int sun4i_frontend_bind(struct device *dev, struct device *master, + void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct sun4i_frontend *frontend; + struct drm_device *drm = data; + struct sun4i_drv *drv = drm->dev_private; + struct resource *res; + void __iomem *regs; + + frontend = devm_kzalloc(dev, sizeof(*frontend), GFP_KERNEL); + if (!frontend) + return -ENOMEM; + dev_set_drvdata(dev, frontend); + frontend->node = dev->of_node; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + frontend->regs = devm_regmap_init_mmio(dev, regs, + &sun4i_frontend_regmap_config); + if (IS_ERR(frontend->regs)) { + dev_err(dev, "Couldn't create the frontend regmap\n"); + return PTR_ERR(frontend->regs); + } + + frontend->reset = devm_reset_control_get(dev, NULL); + if (IS_ERR(frontend->reset)) { + dev_err(dev, "Couldn't get our reset line\n"); + return PTR_ERR(frontend->reset); + } + + frontend->bus_clk = devm_clk_get(dev, "ahb"); + if (IS_ERR(frontend->bus_clk)) { + dev_err(dev, "Couldn't get our bus clock\n"); + return PTR_ERR(frontend->bus_clk); + } + + frontend->mod_clk = devm_clk_get(dev, "mod"); + if (IS_ERR(frontend->mod_clk)) { + dev_err(dev, "Couldn't get our mod clock\n"); + return PTR_ERR(frontend->mod_clk); + } + + frontend->ram_clk = devm_clk_get(dev, "ram"); + if (IS_ERR(frontend->ram_clk)) { + dev_err(dev, "Couldn't get our ram clock\n"); + return PTR_ERR(frontend->ram_clk); + } + + list_add_tail(&frontend->list, &drv->frontend_list); + + return 0; +} + +static void sun4i_frontend_unbind(struct device *dev, struct device *master, + void *data) +{ + struct sun4i_frontend *frontend = dev_get_drvdata(dev); + + clk_disable_unprepare(frontend->mod_clk); + clk_disable_unprepare(frontend->bus_clk); + reset_control_assert(frontend->reset); +} + +static const struct component_ops sun4i_frontend_ops = { + .bind = sun4i_frontend_bind, + .unbind = sun4i_frontend_unbind, +}; + +static int sun4i_frontend_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &sun4i_frontend_ops); +} + +static int sun4i_frontend_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &sun4i_frontend_ops); + + return 0; +} + +static const struct of_device_id sun4i_frontend_of_table[] = { + { .compatible = "allwinner,sun5i-a13-display-frontend" }, + { .compatible = "allwinner,sun6i-a31-display-frontend" }, + { .compatible = "allwinner,sun8i-a33-display-frontend" }, + { } +}; +MODULE_DEVICE_TABLE(of, sun4i_frontend_of_table); + +static struct platform_driver sun4i_frontend_driver = { + .probe = sun4i_frontend_probe, + .remove = sun4i_frontend_remove, + .driver = { + .name = "sun4i-frontend", + .of_match_table = sun4i_frontend_of_table, + }, +}; +module_platform_driver(sun4i_frontend_driver); + +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); +MODULE_DESCRIPTION("Allwinner A10 Display Engine Frontend Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/sun4i/sun4i_frontend.h b/drivers/gpu/drm/sun4i/sun4i_frontend.h new file mode 100644 index 000000000000..2df9f6de0f3f --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun4i_frontend.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2017 Free Electrons + * + * Maxime Ripard <maxime.ripard@free-electrons.com> + * + * 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. + */ + +#ifndef _SUN4I_FRONTEND_H_ +#define _SUN4I_FRONTEND_H_ + +#include <linux/atomic.h> +#include <linux/list.h> + +#define SUN4I_FRONTEND_EN_REG 0x000 +#define SUN4I_FRONTEND_EN_EN BIT(0) + +#define SUN4I_FRONTEND_FRM_CTRL_REG 0x004 +#define SUN4I_FRONTEND_FRM_CTRL_FRM_START BIT(16) +#define SUN4I_FRONTEND_FRM_CTRL_COEF_RDY BIT(1) +#define SUN4I_FRONTEND_FRM_CTRL_REG_RDY BIT(0) + +#define SUN4I_FRONTEND_BYPASS_REG 0x008 +#define SUN4I_FRONTEND_BYPASS_CSC_EN BIT(1) + +#define SUN4I_FRONTEND_BUF_ADDR0_REG 0x020 + +#define SUN4I_FRONTEND_LINESTRD0_REG 0x040 + +#define SUN4I_FRONTEND_INPUT_FMT_REG 0x04c +#define SUN4I_FRONTEND_INPUT_FMT_DATA_MOD(mod) ((mod) << 8) +#define SUN4I_FRONTEND_INPUT_FMT_DATA_FMT(fmt) ((fmt) << 4) +#define SUN4I_FRONTEND_INPUT_FMT_PS(ps) (ps) + +#define SUN4I_FRONTEND_OUTPUT_FMT_REG 0x05c +#define SUN4I_FRONTEND_OUTPUT_FMT_DATA_FMT(fmt) (fmt) + +#define SUN4I_FRONTEND_CH0_INSIZE_REG 0x100 +#define SUN4I_FRONTEND_INSIZE(h, w) ((((h) - 1) << 16) | (((w) - 1))) + +#define SUN4I_FRONTEND_CH0_OUTSIZE_REG 0x104 +#define SUN4I_FRONTEND_OUTSIZE(h, w) ((((h) - 1) << 16) | (((w) - 1))) + +#define SUN4I_FRONTEND_CH0_HORZFACT_REG 0x108 +#define SUN4I_FRONTEND_HORZFACT(i, f) (((i) << 16) | (f)) + +#define SUN4I_FRONTEND_CH0_VERTFACT_REG 0x10c +#define SUN4I_FRONTEND_VERTFACT(i, f) (((i) << 16) | (f)) + +#define SUN4I_FRONTEND_CH0_HORZPHASE_REG 0x110 +#define SUN4I_FRONTEND_CH0_VERTPHASE0_REG 0x114 +#define SUN4I_FRONTEND_CH0_VERTPHASE1_REG 0x118 + +#define SUN4I_FRONTEND_CH1_INSIZE_REG 0x200 +#define SUN4I_FRONTEND_CH1_OUTSIZE_REG 0x204 +#define SUN4I_FRONTEND_CH1_HORZFACT_REG 0x208 +#define SUN4I_FRONTEND_CH1_VERTFACT_REG 0x20c + +#define SUN4I_FRONTEND_CH1_HORZPHASE_REG 0x210 +#define SUN4I_FRONTEND_CH1_VERTPHASE0_REG 0x214 +#define SUN4I_FRONTEND_CH1_VERTPHASE1_REG 0x218 + +#define SUN4I_FRONTEND_CH0_HORZCOEF0_REG(i) (0x400 + i * 4) +#define SUN4I_FRONTEND_CH0_HORZCOEF1_REG(i) (0x480 + i * 4) +#define SUN4I_FRONTEND_CH0_VERTCOEF_REG(i) (0x500 + i * 4) +#define SUN4I_FRONTEND_CH1_HORZCOEF0_REG(i) (0x600 + i * 4) +#define SUN4I_FRONTEND_CH1_HORZCOEF1_REG(i) (0x680 + i * 4) +#define SUN4I_FRONTEND_CH1_VERTCOEF_REG(i) (0x700 + i * 4) + +struct clk; +struct device_node; +struct drm_plane; +struct regmap; +struct reset_control; + +struct sun4i_frontend { + struct list_head list; + atomic_t users; + struct device_node *node; + + struct clk *bus_clk; + struct clk *mod_clk; + struct clk *ram_clk; + struct regmap *regs; + struct reset_control *reset; +}; + +int sun4i_frontend_init(struct sun4i_frontend *frontend); +void sun4i_frontend_exit(struct sun4i_frontend *frontend); +int sun4i_frontend_enable(struct sun4i_frontend *frontend); + +void sun4i_frontend_update_buffer(struct sun4i_frontend *frontend, + struct drm_plane *plane); +void sun4i_frontend_update_coord(struct sun4i_frontend *frontend, + struct drm_plane *plane); +int sun4i_frontend_update_formats(struct sun4i_frontend *frontend, + struct drm_plane *plane, uint32_t out_fmt); + +#endif /* _SUN4I_FRONTEND_H_ */
The display frontend is an hardware block that can be used to implement some more advanced features like hardware scaling or colorspace conversions. It can also be used to implement the output format of the VPU. Let's create a minimal driver for it that will only enable the hardware scaling features. Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> --- drivers/gpu/drm/sun4i/Makefile | 3 +- drivers/gpu/drm/sun4i/sun4i_backend.c | 5 +- drivers/gpu/drm/sun4i/sun4i_backend.h | 1 +- drivers/gpu/drm/sun4i/sun4i_drv.c | 16 +- drivers/gpu/drm/sun4i/sun4i_drv.h | 1 +- drivers/gpu/drm/sun4i/sun4i_frontend.c | 377 ++++++++++++++++++++++++++- drivers/gpu/drm/sun4i/sun4i_frontend.h | 102 +++++++- 7 files changed, 500 insertions(+), 5 deletions(-) create mode 100644 drivers/gpu/drm/sun4i/sun4i_frontend.c create mode 100644 drivers/gpu/drm/sun4i/sun4i_frontend.h