This patch set adds a new drm driver for HiSilicon Kirin hi6220 SoC. Current testing and support board is Hikey board which is one of Linaro 96boards. It is an arm64 open source board. For more information about this board, please access https://www.96boards.org.
Hardware Detail --------------- The display subsystem of Hi6220 SoC is shown as bellow: +-----+ +----------+ +-----+ +-------------+ | | | | | | | | | FB |------>| ADE |---->| DSI |---->| External | | | | | | | | HDMI/panel | +-----+ +----------+ +-----+ +-------------+
- ADE(Advanced Display Engine) is the display controller. It contains 7 channels, 3 overlay compositors and a LDI. - A channel looks like: DMA-->clip-->scale-->ctrans(or called csc). - Overlay compositor is response to compose planes which come from 7 channels and pass composed image to LDI. - LDI is response to generate timings and RGB data stream. - DSI converts the RGB data stream from ADE to DSI packets. - External HDMI/panel module is connected with DSI bus. Now Hikey use a ADI's ADV7533 external HDMI chip.
Change History ------------- Changes in v6: - Cleanup values part of reg and clocks relevant properties. - Change "pclk_dsi" clock name to "pclk".
Changes in v5: - Remove endpoint unit address of dsi output port. - Use syscon to access ADE media NOC QoS registers instread of directly writing registers. - Use reset controller to reset ADE instead of directly writing registers.
Changes in v4: - Describe more specific of clocks and ports of binding docs. - Fix indentation of binding docs.
Changes in v3: - Move and rename all the files to kirin sub-directory. So that we could separate different seires SoCs' driver. - Make ade as the drm master node. - Replace drm_platform_init, load, unload implementation. - Use assigned-clocks to set clock rate. - Use ports to connect display relavant nodes. - Rename hisi_drm_dsi.c to dw_drm_dsi.c - Make encoder type as DRM_MODE_ENCODER_DSI. - A few cleanup on regs and code.
Changes in v2: - Remove abtraction layer of plane/crtc/encoder/connector. - Refactor atomic implementation according to Daniel Vetter's guides: http://blog.ffwll.ch/2014/11/atomic-modeset-support-for-kms-drivers.html http://blog.ffwll.ch/2015/09/xdc-2015-atomic-modesetting-for-drivers.html http://blog.ffwll.ch/2015/08/atomic-modesetting-design-overview.html - Use bridge instead of slave encoder to connect external HDMI. - Move dt binding docs to bindings/display/hisilicon directory.
Xinliang Liu (11): drm/hisilicon: Add device tree binding for hi6220 display subsystem drm/hisilicon: Add hisilicon kirin drm master driver drm/hisilicon: Add crtc driver for ADE drm/hisilicon: Add plane driver for ADE drm/hisilicon: Add vblank driver for ADE drm/hisilicon: Add cma fbdev and hotplug drm/hisilicon: Add designware dsi encoder driver drm/hisilicon: Add designware dsi host driver drm/hisilicon: Add support for external bridge MAINTAINERS: Add maintainer for hisilicon DRM driver arm64: dts: hisilicon: Add display subsystem DT nodes for hi6220
.../bindings/display/hisilicon/dw-dsi.txt | 72 ++ .../bindings/display/hisilicon/hisi-ade.txt | 64 ++ MAINTAINERS | 10 + arch/arm64/boot/dts/hisilicon/hi6220-hikey.dts | 40 + arch/arm64/boot/dts/hisilicon/hi6220.dtsi | 55 + drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/hisilicon/Kconfig | 5 + drivers/gpu/drm/hisilicon/Makefile | 5 + drivers/gpu/drm/hisilicon/kirin/Kconfig | 10 + drivers/gpu/drm/hisilicon/kirin/Makefile | 5 + drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c | 845 ++++++++++++++++ drivers/gpu/drm/hisilicon/kirin/dw_dsi_reg.h | 83 ++ drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h | 290 ++++++ drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c | 1047 ++++++++++++++++++++ drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c | 382 +++++++ drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h | 31 + 17 files changed, 2947 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/hisilicon/dw-dsi.txt create mode 100644 Documentation/devicetree/bindings/display/hisilicon/hisi-ade.txt create mode 100644 drivers/gpu/drm/hisilicon/Kconfig create mode 100644 drivers/gpu/drm/hisilicon/Makefile create mode 100644 drivers/gpu/drm/hisilicon/kirin/Kconfig create mode 100644 drivers/gpu/drm/hisilicon/kirin/Makefile create mode 100644 drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c create mode 100644 drivers/gpu/drm/hisilicon/kirin/dw_dsi_reg.h create mode 100644 drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h create mode 100644 drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c create mode 100644 drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c create mode 100644 drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h
Add ADE display controller binding doc. Add DesignWare DSI Host Controller v1.20a binding doc.
v6: - Cleanup values part of reg and clocks properties. - Change "pclk_dsi" clock name to "pclk". v5: - Remove endpoint unit address of dsi output port. - Add "hisilicon,noc-syscon" property for ADE NOC QoS syscon. - Add "resets" property for ADE reset. v4: - Describe more specific of clocks and ports. - Fix indentation. v3: - Make ade as the drm master node. - Use assigned-clocks to set clock rate. - Use ports to connect display relavant nodes. v2: - Move dt binding docs to bindings/display/hisilicon directory.
Signed-off-by: Xinliang Liu xinliang.liu@linaro.org --- .../bindings/display/hisilicon/dw-dsi.txt | 72 ++++++++++++++++++++++ .../bindings/display/hisilicon/hisi-ade.txt | 64 +++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/hisilicon/dw-dsi.txt create mode 100644 Documentation/devicetree/bindings/display/hisilicon/hisi-ade.txt
diff --git a/Documentation/devicetree/bindings/display/hisilicon/dw-dsi.txt b/Documentation/devicetree/bindings/display/hisilicon/dw-dsi.txt new file mode 100644 index 000000000000..d270bfe4e4e0 --- /dev/null +++ b/Documentation/devicetree/bindings/display/hisilicon/dw-dsi.txt @@ -0,0 +1,72 @@ +Device-Tree bindings for DesignWare DSI Host Controller v1.20a driver + +A DSI Host Controller resides in the middle of display controller and external +HDMI converter or panel. + +Required properties: +- compatible: value should be "hisilicon,hi6220-dsi". +- reg: physical base address and length of dsi controller's registers. +- clocks: contains APB clock phandle + clock-specifier pair. +- clock-names: should be "pclk". +- ports: contains DSI controller input and output sub port. + The input port connects to ADE output port with the reg value "0". + The output port with the reg value "1", it could connect to panel or + any other bridge endpoints. + See Documentation/devicetree/bindings/graph.txt for more device graph info. + +A example of HiKey board hi6220 SoC and board specific DT entry: +Example: + +SoC specific: + dsi: dsi@f4107800 { + compatible = "hisilicon,hi6220-dsi"; + reg = <0x0 0xf4107800 0x0 0x100>; + clocks = <&media_ctrl HI6220_DSI_PCLK>; + clock-names = "pclk"; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + /* 0 for input port */ + port@0 { + reg = <0>; + dsi_in: endpoint { + remote-endpoint = <&ade_out>; + }; + }; + }; + }; + + +Board specific: + &dsi { + status = "ok"; + + ports { + /* 1 for output port */ + port@1 { + reg = <1>; + + dsi_out0: endpoint@0 { + remote-endpoint = <&adv7533_in>; + }; + }; + }; + }; + + &i2c2 { + ... + + adv7533: adv7533@39 { + ... + + port { + adv7533_in: endpoint { + remote-endpoint = <&dsi_out0>; + }; + }; + }; + }; + diff --git a/Documentation/devicetree/bindings/display/hisilicon/hisi-ade.txt b/Documentation/devicetree/bindings/display/hisilicon/hisi-ade.txt new file mode 100644 index 000000000000..38dc9d60eef8 --- /dev/null +++ b/Documentation/devicetree/bindings/display/hisilicon/hisi-ade.txt @@ -0,0 +1,64 @@ +Device-Tree bindings for hisilicon ADE display controller driver + +ADE (Advanced Display Engine) is the display controller which grab image +data from memory, do composition, do post image processing, generate RGB +timing stream and transfer to DSI. + +Required properties: +- compatible: value should be "hisilicon,hi6220-ade". +- reg: physical base address and length of the ADE controller's registers. +- hisilicon,noc-syscon: ADE NOC QoS syscon. +- resets: The ADE reset controller node. +- interrupt: the ldi vblank interrupt number used. +- clocks: a list of phandle + clock-specifier pairs, one for each entry + in clock-names. +- clock-names: should contain: + "clk_ade_core" for the ADE core clock. + "clk_codec_jpeg" for the media NOC QoS clock, which use the same clock with + jpeg codec. + "clk_ade_pix" for the ADE pixel clok. +- assigned-clocks: Should contain "clk_ade_core" and "clk_codec_jpeg" clocks' + phandle + clock-specifier pairs. +- assigned-clock-rates: clock rates, one for each entry in assigned-clocks. + The rate of "clk_ade_core" could be "360000000" or "180000000"; + The rate of "clk_codec_jpeg" could be or less than "1440000000". + These rate values could be configured according to performance and power + consumption. +- port: the output port. This contains one endpoint subnode, with its + remote-endpoint set to the phandle of the connected DSI input endpoint. + See Documentation/devicetree/bindings/graph.txt for more device graph info. + +Optional properties: +- dma-coherent: Present if dma operations are coherent. + + +A example of HiKey board hi6220 SoC specific DT entry: +Example: + + ade: ade@f4100000 { + compatible = "hisilicon,hi6220-ade"; + reg = <0x0 0xf4100000 0x0 0x7800>; + reg-names = "ade_base"; + hisilicon,noc-syscon = <&medianoc_ade>; + resets = <&media_ctrl MEDIA_ADE>; + interrupts = <0 115 4>; /* ldi interrupt */ + + clocks = <&media_ctrl HI6220_ADE_CORE>, + <&media_ctrl HI6220_CODEC_JPEG>, + <&media_ctrl HI6220_ADE_PIX_SRC>; + /*clock name*/ + clock-names = "clk_ade_core", + "clk_codec_jpeg", + "clk_ade_pix"; + + assigned-clocks = <&media_ctrl HI6220_ADE_CORE>, + <&media_ctrl HI6220_CODEC_JPEG>; + assigned-clock-rates = <360000000>, <288000000>; + dma-coherent; + + port { + ade_out: endpoint { + remote-endpoint = <&dsi_in>; + }; + }; + };
On Fri, Feb 26, 2016 at 04:40:18PM +0800, Xinliang Liu wrote:
Add ADE display controller binding doc. Add DesignWare DSI Host Controller v1.20a binding doc.
v6:
- Cleanup values part of reg and clocks properties.
- Change "pclk_dsi" clock name to "pclk".
v5:
- Remove endpoint unit address of dsi output port.
- Add "hisilicon,noc-syscon" property for ADE NOC QoS syscon.
- Add "resets" property for ADE reset.
v4:
- Describe more specific of clocks and ports.
- Fix indentation.
v3:
- Make ade as the drm master node.
- Use assigned-clocks to set clock rate.
- Use ports to connect display relavant nodes.
v2:
- Move dt binding docs to bindings/display/hisilicon directory.
Signed-off-by: Xinliang Liu xinliang.liu@linaro.org
.../bindings/display/hisilicon/dw-dsi.txt | 72 ++++++++++++++++++++++ .../bindings/display/hisilicon/hisi-ade.txt | 64 +++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/hisilicon/dw-dsi.txt create mode 100644 Documentation/devicetree/bindings/display/hisilicon/hisi-ade.txt
Acked-by: Rob Herring robh@kernel.org
Hi,
On 3 March 2016 at 02:29, Rob Herring robh@kernel.org wrote:
On Fri, Feb 26, 2016 at 04:40:18PM +0800, Xinliang Liu wrote:
Add ADE display controller binding doc. Add DesignWare DSI Host Controller v1.20a binding doc.
v6:
- Cleanup values part of reg and clocks properties.
- Change "pclk_dsi" clock name to "pclk".
v5:
- Remove endpoint unit address of dsi output port.
- Add "hisilicon,noc-syscon" property for ADE NOC QoS syscon.
- Add "resets" property for ADE reset.
v4:
- Describe more specific of clocks and ports.
- Fix indentation.
v3:
- Make ade as the drm master node.
- Use assigned-clocks to set clock rate.
- Use ports to connect display relavant nodes.
v2:
- Move dt binding docs to bindings/display/hisilicon directory.
Signed-off-by: Xinliang Liu xinliang.liu@linaro.org
.../bindings/display/hisilicon/dw-dsi.txt | 72 ++++++++++++++++++++++ .../bindings/display/hisilicon/hisi-ade.txt | 64 +++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/hisilicon/dw-dsi.txt create mode 100644 Documentation/devicetree/bindings/display/hisilicon/hisi-ade.txt
Acked-by: Rob Herring robh@kernel.org
Thanks :-) -xinliang
Add kirin DRM master driver for hi6220 SoC which used in HiKey board. Add dumb buffer feature. Add prime dmabuf feature.
v6: None. v5: None. v4: None. v3: - Move and rename all the files to kirin sub-directory. So that we could separate different seires SoCs' driver. - Replace drm_platform_init, load, unload implementation. v2: - Remove abtraction layer.
Signed-off-by: Xinliang Liu xinliang.liu@linaro.org --- drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/hisilicon/Kconfig | 5 + drivers/gpu/drm/hisilicon/Makefile | 5 + drivers/gpu/drm/hisilicon/kirin/Kconfig | 9 + drivers/gpu/drm/hisilicon/kirin/Makefile | 3 + drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c | 321 ++++++++++++++++++++++++ drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h | 20 ++ 8 files changed, 366 insertions(+) create mode 100644 drivers/gpu/drm/hisilicon/Kconfig create mode 100644 drivers/gpu/drm/hisilicon/Makefile create mode 100644 drivers/gpu/drm/hisilicon/kirin/Kconfig create mode 100644 drivers/gpu/drm/hisilicon/kirin/Makefile create mode 100644 drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c create mode 100644 drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index b50ae60f5f50..f5c5656e2547 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -277,3 +277,5 @@ source "drivers/gpu/drm/imx/Kconfig" source "drivers/gpu/drm/vc4/Kconfig"
source "drivers/gpu/drm/etnaviv/Kconfig" + +source "drivers/gpu/drm/hisilicon/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 61766dec6a8d..60554832079c 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -74,3 +74,4 @@ obj-y += panel/ obj-y += bridge/ obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu/ obj-$(CONFIG_DRM_ETNAVIV) += etnaviv/ +obj-y += hisilicon/ diff --git a/drivers/gpu/drm/hisilicon/Kconfig b/drivers/gpu/drm/hisilicon/Kconfig new file mode 100644 index 000000000000..558c61b1b8e8 --- /dev/null +++ b/drivers/gpu/drm/hisilicon/Kconfig @@ -0,0 +1,5 @@ +# +# hisilicon drm device configuration. +# Please keep this list sorted alphabetically + +source "drivers/gpu/drm/hisilicon/kirin/Kconfig" diff --git a/drivers/gpu/drm/hisilicon/Makefile b/drivers/gpu/drm/hisilicon/Makefile new file mode 100644 index 000000000000..e3f6d493c996 --- /dev/null +++ b/drivers/gpu/drm/hisilicon/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for hisilicon drm drivers. +# Please keep this list sorted alphabetically + +obj-$(CONFIG_DRM_HISI_KIRIN) += kirin/ diff --git a/drivers/gpu/drm/hisilicon/kirin/Kconfig b/drivers/gpu/drm/hisilicon/kirin/Kconfig new file mode 100644 index 000000000000..3ac4b8edeac1 --- /dev/null +++ b/drivers/gpu/drm/hisilicon/kirin/Kconfig @@ -0,0 +1,9 @@ +config DRM_HISI_KIRIN + tristate "DRM Support for Hisilicon Kirin series SoCs Platform" + depends on DRM + select DRM_KMS_HELPER + select DRM_GEM_CMA_HELPER + select DRM_KMS_CMA_HELPER + help + Choose this option if you have a hisilicon Kirin chipsets(hi6220). + If M is selected the module will be called kirin-drm. diff --git a/drivers/gpu/drm/hisilicon/kirin/Makefile b/drivers/gpu/drm/hisilicon/kirin/Makefile new file mode 100644 index 000000000000..cb346de47d48 --- /dev/null +++ b/drivers/gpu/drm/hisilicon/kirin/Makefile @@ -0,0 +1,3 @@ +kirin-drm-y := kirin_drm_drv.o + +obj-$(CONFIG_DRM_HISI_KIRIN) += kirin-drm.o diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c new file mode 100644 index 000000000000..789ebd1f5922 --- /dev/null +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c @@ -0,0 +1,321 @@ +/* + * Hisilicon Kirin SoCs drm master driver + * + * Copyright (c) 2016 Linaro Limited. + * Copyright (c) 2014-2016 Hisilicon Limited. + * + * Author: + * Xinliang Liu z.liuxinliang@hisilicon.com + * Xinliang Liu xinliang.liu@linaro.org + * Xinwei Kong kong.kongxinwei@hisilicon.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/of_platform.h> +#include <linux/component.h> +#include <linux/of_graph.h> + +#include <drm/drmP.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_atomic_helper.h> + +#include "kirin_drm_drv.h" + +static struct kirin_dc_ops *dc_ops; + +static int kirin_drm_kms_cleanup(struct drm_device *dev) +{ + dc_ops->cleanup(dev); + drm_mode_config_cleanup(dev); + + return 0; +} + +static const struct drm_mode_config_funcs kirin_drm_mode_config_funcs = { + .fb_create = drm_fb_cma_create, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +static void kirin_drm_mode_config_init(struct drm_device *dev) +{ + dev->mode_config.min_width = 0; + dev->mode_config.min_height = 0; + + dev->mode_config.max_width = 2048; + dev->mode_config.max_height = 2048; + + dev->mode_config.funcs = &kirin_drm_mode_config_funcs; +} + +static int kirin_drm_kms_init(struct drm_device *dev) +{ + int ret; + + dev_set_drvdata(dev->dev, dev); + + /* dev->mode_config initialization */ + drm_mode_config_init(dev); + kirin_drm_mode_config_init(dev); + + /* display controller init */ + ret = dc_ops->init(dev); + if (ret) + goto err_mode_config_cleanup; + + /* bind and init sub drivers */ + ret = component_bind_all(dev->dev, dev); + if (ret) { + DRM_ERROR("failed to bind all component.\n"); + goto err_dc_cleanup; + } + + /* reset all the states of crtc/plane/encoder/connector */ + drm_mode_config_reset(dev); + + return 0; + +err_dc_cleanup: + dc_ops->cleanup(dev); +err_mode_config_cleanup: + drm_mode_config_cleanup(dev); + + return ret; +} + +static const struct file_operations kirin_drm_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = drm_compat_ioctl, +#endif + .poll = drm_poll, + .read = drm_read, + .llseek = no_llseek, + .mmap = drm_gem_cma_mmap, +}; + +static int kirin_gem_cma_dumb_create(struct drm_file *file, + struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + return drm_gem_cma_dumb_create_internal(file, dev, args); +} + +static struct drm_driver kirin_drm_driver = { + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | + DRIVER_ATOMIC, + .fops = &kirin_drm_fops, + .set_busid = drm_platform_set_busid, + + .gem_free_object = drm_gem_cma_free_object, + .gem_vm_ops = &drm_gem_cma_vm_ops, + .dumb_create = kirin_gem_cma_dumb_create, + .dumb_map_offset = drm_gem_cma_dumb_map_offset, + .dumb_destroy = drm_gem_dumb_destroy, + + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .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, + .gem_prime_vmap = drm_gem_cma_prime_vmap, + .gem_prime_vunmap = drm_gem_cma_prime_vunmap, + .gem_prime_mmap = drm_gem_cma_prime_mmap, + + .name = "kirin", + .desc = "Hisilicon Kirin SoCs' DRM Driver", + .date = "20150718", + .major = 1, + .minor = 0, +}; + +static int compare_of(struct device *dev, void *data) +{ + return dev->of_node == data; +} + +static int kirin_drm_connectors_register(struct drm_device *dev) +{ + struct drm_connector *connector; + struct drm_connector *failed_connector; + int ret; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + ret = drm_connector_register(connector); + if (ret) { + failed_connector = connector; + goto err; + } + } + + return 0; + +err: + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (failed_connector == connector) + break; + drm_connector_unregister(connector); + } + + return ret; +} + +static int kirin_drm_bind(struct device *dev) +{ + struct drm_driver *driver = &kirin_drm_driver; + struct drm_device *drm_dev; + int ret; + + drm_dev = drm_dev_alloc(driver, dev); + if (!drm_dev) + return -ENOMEM; + + drm_dev->platformdev = to_platform_device(dev); + + ret = kirin_drm_kms_init(drm_dev); + if (ret) + goto err_drm_dev_unref; + + ret = drm_dev_register(drm_dev, 0); + if (ret) + goto err_kms_cleanup; + + /* connectors should be registered after drm device register */ + ret = kirin_drm_connectors_register(drm_dev); + if (ret) + goto err_drm_dev_unregister; + + DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", + driver->name, driver->major, driver->minor, driver->patchlevel, + driver->date, drm_dev->primary->index); + + return 0; + +err_drm_dev_unregister: + drm_dev_unregister(drm_dev); +err_kms_cleanup: + kirin_drm_kms_cleanup(drm_dev); +err_drm_dev_unref: + drm_dev_unref(drm_dev); + + return ret; +} + +static void kirin_drm_unbind(struct device *dev) +{ + drm_put_dev(dev_get_drvdata(dev)); +} + +static const struct component_master_ops kirin_drm_ops = { + .bind = kirin_drm_bind, + .unbind = kirin_drm_unbind, +}; + +static const struct of_device_id kirin_drm_dt_ids[] = { + { /* end node */ }, +}; +MODULE_DEVICE_TABLE(of, kirin_drm_dt_ids); + +static int kirin_get_dc_ops(struct device_node *np) +{ + const struct of_device_id *match_id; + + match_id = of_match_node(kirin_drm_dt_ids, np); + if (!match_id) { + DRM_ERROR("failed to match dt id\n"); + return -EINVAL; + } + + dc_ops = (struct kirin_dc_ops *)match_id->data; + if (!dc_ops) { + DRM_ERROR("dt id data is null\n"); + return -EINVAL; + } + + return 0; +} + +static struct device_node *kirin_get_remote_node(struct device_node *np) +{ + struct device_node *endpoint, *remote; + + /* get the first endpoint, in our case only one remote node + * is connected to display controller. + */ + endpoint = of_graph_get_next_endpoint(np, NULL); + if (!endpoint) { + DRM_ERROR("no valid endpoint node\n"); + return ERR_PTR(-ENODEV); + } + of_node_put(endpoint); + + remote = of_graph_get_remote_port_parent(endpoint); + if (!remote) { + DRM_ERROR("no valid remote node\n"); + return ERR_PTR(-ENODEV); + } + of_node_put(remote); + + if (!of_device_is_available(remote)) { + DRM_ERROR("not available for remote node\n"); + return ERR_PTR(-ENODEV); + } + + return remote; +} + +static int kirin_drm_platform_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct component_match *match = NULL; + struct device_node *remote; + int ret; + + ret = kirin_get_dc_ops(np); + if (ret) + return ret; + + remote = kirin_get_remote_node(np); + if (IS_ERR(remote)) + return PTR_ERR(remote); + + component_match_add(dev, &match, compare_of, remote); + + return component_master_add_with_match(dev, &kirin_drm_ops, match); + + return 0; +} + +static int kirin_drm_platform_remove(struct platform_device *pdev) +{ + component_master_del(&pdev->dev, &kirin_drm_ops); + dc_ops = NULL; + return 0; +} + +static struct platform_driver kirin_drm_platform_driver = { + .probe = kirin_drm_platform_probe, + .remove = kirin_drm_platform_remove, + .driver = { + .name = "kirin-drm", + .of_match_table = kirin_drm_dt_ids, + }, +}; + +module_platform_driver(kirin_drm_platform_driver); + +MODULE_AUTHOR("Xinliang Liu xinliang.liu@linaro.org"); +MODULE_AUTHOR("Xinliang Liu z.liuxinliang@hisilicon.com"); +MODULE_AUTHOR("Xinwei Kong kong.kongxinwei@hisilicon.com"); +MODULE_DESCRIPTION("hisilicon Kirin SoCs' DRM master driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h new file mode 100644 index 000000000000..75e9d82356d4 --- /dev/null +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2016 Linaro Limited. + * Copyright (c) 2014-2016 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef __KIRIN_DRM_DRV_H__ +#define __KIRIN_DRM_DRV_H__ + +/* display controller init/cleanup ops */ +struct kirin_dc_ops { + int (*init)(struct drm_device *dev); + void (*cleanup)(struct drm_device *dev); +}; + +#endif /* __KIRIN_DRM_DRV_H__ */
Hi,
I had some minor comments. Sorry about sharing this late. Otherwise, the looks good to me.
On 02/26/2016 02:10 PM, Xinliang Liu wrote:
Add kirin DRM master driver for hi6220 SoC which used in HiKey board. Add dumb buffer feature. Add prime dmabuf feature.
v6: None. v5: None. v4: None. v3:
- Move and rename all the files to kirin sub-directory. So that we could separate different seires SoCs' driver.
- Replace drm_platform_init, load, unload implementation.
v2:
- Remove abtraction layer.
Signed-off-by: Xinliang Liu xinliang.liu@linaro.org
drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/hisilicon/Kconfig | 5 + drivers/gpu/drm/hisilicon/Makefile | 5 + drivers/gpu/drm/hisilicon/kirin/Kconfig | 9 + drivers/gpu/drm/hisilicon/kirin/Makefile | 3 + drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c | 321 ++++++++++++++++++++++++ drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h | 20 ++ 8 files changed, 366 insertions(+) create mode 100644 drivers/gpu/drm/hisilicon/Kconfig create mode 100644 drivers/gpu/drm/hisilicon/Makefile create mode 100644 drivers/gpu/drm/hisilicon/kirin/Kconfig create mode 100644 drivers/gpu/drm/hisilicon/kirin/Makefile create mode 100644 drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c create mode 100644 drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index b50ae60f5f50..f5c5656e2547 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -277,3 +277,5 @@ source "drivers/gpu/drm/imx/Kconfig" source "drivers/gpu/drm/vc4/Kconfig"
source "drivers/gpu/drm/etnaviv/Kconfig"
+source "drivers/gpu/drm/hisilicon/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 61766dec6a8d..60554832079c 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -74,3 +74,4 @@ obj-y += panel/ obj-y += bridge/ obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu/ obj-$(CONFIG_DRM_ETNAVIV) += etnaviv/ +obj-y += hisilicon/ diff --git a/drivers/gpu/drm/hisilicon/Kconfig b/drivers/gpu/drm/hisilicon/Kconfig new file mode 100644 index 000000000000..558c61b1b8e8 --- /dev/null +++ b/drivers/gpu/drm/hisilicon/Kconfig @@ -0,0 +1,5 @@ +# +# hisilicon drm device configuration. +# Please keep this list sorted alphabetically
+source "drivers/gpu/drm/hisilicon/kirin/Kconfig" diff --git a/drivers/gpu/drm/hisilicon/Makefile b/drivers/gpu/drm/hisilicon/Makefile new file mode 100644 index 000000000000..e3f6d493c996 --- /dev/null +++ b/drivers/gpu/drm/hisilicon/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for hisilicon drm drivers. +# Please keep this list sorted alphabetically
+obj-$(CONFIG_DRM_HISI_KIRIN) += kirin/ diff --git a/drivers/gpu/drm/hisilicon/kirin/Kconfig b/drivers/gpu/drm/hisilicon/kirin/Kconfig new file mode 100644 index 000000000000..3ac4b8edeac1 --- /dev/null +++ b/drivers/gpu/drm/hisilicon/kirin/Kconfig @@ -0,0 +1,9 @@ +config DRM_HISI_KIRIN
- tristate "DRM Support for Hisilicon Kirin series SoCs Platform"
- depends on DRM
- select DRM_KMS_HELPER
- select DRM_GEM_CMA_HELPER
- select DRM_KMS_CMA_HELPER
- help
Choose this option if you have a hisilicon Kirin chipsets(hi6220).
If M is selected the module will be called kirin-drm.
diff --git a/drivers/gpu/drm/hisilicon/kirin/Makefile b/drivers/gpu/drm/hisilicon/kirin/Makefile new file mode 100644 index 000000000000..cb346de47d48 --- /dev/null +++ b/drivers/gpu/drm/hisilicon/kirin/Makefile @@ -0,0 +1,3 @@ +kirin-drm-y := kirin_drm_drv.o
+obj-$(CONFIG_DRM_HISI_KIRIN) += kirin-drm.o diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c new file mode 100644 index 000000000000..789ebd1f5922 --- /dev/null +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c @@ -0,0 +1,321 @@ +/*
- Hisilicon Kirin SoCs drm master driver
- Copyright (c) 2016 Linaro Limited.
- Copyright (c) 2014-2016 Hisilicon Limited.
- Author:
- Xinliang Liu z.liuxinliang@hisilicon.com
- Xinliang Liu xinliang.liu@linaro.org
- Xinwei Kong kong.kongxinwei@hisilicon.com
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 as
- published by the Free Software Foundation.
- */
+#include <linux/of_platform.h> +#include <linux/component.h> +#include <linux/of_graph.h>
+#include <drm/drmP.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_atomic_helper.h>
+#include "kirin_drm_drv.h"
+static struct kirin_dc_ops *dc_ops;
+static int kirin_drm_kms_cleanup(struct drm_device *dev) +{
- dc_ops->cleanup(dev);
- drm_mode_config_cleanup(dev);
- return 0;
+}
+static const struct drm_mode_config_funcs kirin_drm_mode_config_funcs = {
- .fb_create = drm_fb_cma_create,
- .atomic_check = drm_atomic_helper_check,
- .atomic_commit = drm_atomic_helper_commit,
+};
+static void kirin_drm_mode_config_init(struct drm_device *dev) +{
- dev->mode_config.min_width = 0;
- dev->mode_config.min_height = 0;
- dev->mode_config.max_width = 2048;
- dev->mode_config.max_height = 2048;
- dev->mode_config.funcs = &kirin_drm_mode_config_funcs;
+}
+static int kirin_drm_kms_init(struct drm_device *dev) +{
- int ret;
- dev_set_drvdata(dev->dev, dev);
- /* dev->mode_config initialization */
- drm_mode_config_init(dev);
- kirin_drm_mode_config_init(dev);
- /* display controller init */
- ret = dc_ops->init(dev);
- if (ret)
goto err_mode_config_cleanup;
- /* bind and init sub drivers */
- ret = component_bind_all(dev->dev, dev);
- if (ret) {
DRM_ERROR("failed to bind all component.\n");
goto err_dc_cleanup;
- }
- /* reset all the states of crtc/plane/encoder/connector */
- drm_mode_config_reset(dev);
- return 0;
+err_dc_cleanup:
- dc_ops->cleanup(dev);
+err_mode_config_cleanup:
- drm_mode_config_cleanup(dev);
- return ret;
+}
+static const struct file_operations kirin_drm_fops = {
- .owner = THIS_MODULE,
- .open = drm_open,
- .release = drm_release,
- .unlocked_ioctl = drm_ioctl,
+#ifdef CONFIG_COMPAT
- .compat_ioctl = drm_compat_ioctl,
+#endif
- .poll = drm_poll,
- .read = drm_read,
- .llseek = no_llseek,
- .mmap = drm_gem_cma_mmap,
+};
+static int kirin_gem_cma_dumb_create(struct drm_file *file,
struct drm_device *dev,
struct drm_mode_create_dumb *args)
+{
- return drm_gem_cma_dumb_create_internal(file, dev, args);
+}
+static struct drm_driver kirin_drm_driver = {
- .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
DRIVER_ATOMIC,
- .fops = &kirin_drm_fops,
- .set_busid = drm_platform_set_busid,
- .gem_free_object = drm_gem_cma_free_object,
- .gem_vm_ops = &drm_gem_cma_vm_ops,
- .dumb_create = kirin_gem_cma_dumb_create,
- .dumb_map_offset = drm_gem_cma_dumb_map_offset,
- .dumb_destroy = drm_gem_dumb_destroy,
- .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
- .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
- .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,
- .gem_prime_vmap = drm_gem_cma_prime_vmap,
- .gem_prime_vunmap = drm_gem_cma_prime_vunmap,
- .gem_prime_mmap = drm_gem_cma_prime_mmap,
- .name = "kirin",
- .desc = "Hisilicon Kirin SoCs' DRM Driver",
- .date = "20150718",
- .major = 1,
- .minor = 0,
+};
+static int compare_of(struct device *dev, void *data) +{
- return dev->of_node == data;
+}
+static int kirin_drm_connectors_register(struct drm_device *dev) +{
- struct drm_connector *connector;
- struct drm_connector *failed_connector;
- int ret;
This access needs to be protected by the dev->mode_config.mutex
- list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
The above can be replaced with:
drm_for_each_connector(connector, dev) {
ret = drm_connector_register(connector);
if (ret) {
failed_connector = connector;
goto err;
}
- }
- return 0;
+err:
- list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (failed_connector == connector)
break;
drm_connector_unregister(connector);
- }
Same points as above.
- return ret;
+}
+static int kirin_drm_bind(struct device *dev) +{
- struct drm_driver *driver = &kirin_drm_driver;
- struct drm_device *drm_dev;
- int ret;
- drm_dev = drm_dev_alloc(driver, dev);
- if (!drm_dev)
return -ENOMEM;
- drm_dev->platformdev = to_platform_device(dev);
- ret = kirin_drm_kms_init(drm_dev);
- if (ret)
goto err_drm_dev_unref;
- ret = drm_dev_register(drm_dev, 0);
- if (ret)
goto err_kms_cleanup;
- /* connectors should be registered after drm device register */
- ret = kirin_drm_connectors_register(drm_dev);
- if (ret)
goto err_drm_dev_unregister;
- DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
driver->name, driver->major, driver->minor, driver->patchlevel,
driver->date, drm_dev->primary->index);
- return 0;
+err_drm_dev_unregister:
- drm_dev_unregister(drm_dev);
+err_kms_cleanup:
- kirin_drm_kms_cleanup(drm_dev);
+err_drm_dev_unref:
- drm_dev_unref(drm_dev);
- return ret;
+}
+static void kirin_drm_unbind(struct device *dev) +{
- drm_put_dev(dev_get_drvdata(dev));
+}
+static const struct component_master_ops kirin_drm_ops = {
- .bind = kirin_drm_bind,
- .unbind = kirin_drm_unbind,
+};
+static const struct of_device_id kirin_drm_dt_ids[] = {
- { /* end node */ },
+}; +MODULE_DEVICE_TABLE(of, kirin_drm_dt_ids);
+static int kirin_get_dc_ops(struct device_node *np) +{
- const struct of_device_id *match_id;
- match_id = of_match_node(kirin_drm_dt_ids, np);
- if (!match_id) {
DRM_ERROR("failed to match dt id\n");
return -EINVAL;
- }
- dc_ops = (struct kirin_dc_ops *)match_id->data;
- if (!dc_ops) {
DRM_ERROR("dt id data is null\n");
return -EINVAL;
- }
We can use of_device_get_match_data to get the match data, and move the kirin_drm_dt_ids table down where the driver is defined.
<snip>
Thanks, Archit
Hi,
On 26 February 2016 at 16:54, Archit Taneja architt@codeaurora.org wrote:
Hi,
I had some minor comments. Sorry about sharing this late. Otherwise, the looks good to me.
Hi Archit, you are welcome :-) Thanks for review again.
On 02/26/2016 02:10 PM, Xinliang Liu wrote:
Add kirin DRM master driver for hi6220 SoC which used in HiKey board. Add dumb buffer feature. Add prime dmabuf feature.
v6: None. v5: None. v4: None. v3:
- Move and rename all the files to kirin sub-directory. So that we could separate different seires SoCs' driver.
- Replace drm_platform_init, load, unload implementation.
v2:
- Remove abtraction layer.
Signed-off-by: Xinliang Liu xinliang.liu@linaro.org
drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/hisilicon/Kconfig | 5 + drivers/gpu/drm/hisilicon/Makefile | 5 + drivers/gpu/drm/hisilicon/kirin/Kconfig | 9 + drivers/gpu/drm/hisilicon/kirin/Makefile | 3 + drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c | 321 ++++++++++++++++++++++++ drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h | 20 ++ 8 files changed, 366 insertions(+) create mode 100644 drivers/gpu/drm/hisilicon/Kconfig create mode 100644 drivers/gpu/drm/hisilicon/Makefile create mode 100644 drivers/gpu/drm/hisilicon/kirin/Kconfig create mode 100644 drivers/gpu/drm/hisilicon/kirin/Makefile create mode 100644 drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c create mode 100644 drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index b50ae60f5f50..f5c5656e2547 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -277,3 +277,5 @@ source "drivers/gpu/drm/imx/Kconfig" source "drivers/gpu/drm/vc4/Kconfig"
source "drivers/gpu/drm/etnaviv/Kconfig"
+source "drivers/gpu/drm/hisilicon/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 61766dec6a8d..60554832079c 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -74,3 +74,4 @@ obj-y += panel/ obj-y += bridge/ obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu/ obj-$(CONFIG_DRM_ETNAVIV) += etnaviv/ +obj-y += hisilicon/ diff --git a/drivers/gpu/drm/hisilicon/Kconfig b/drivers/gpu/drm/hisilicon/Kconfig new file mode 100644 index 000000000000..558c61b1b8e8 --- /dev/null +++ b/drivers/gpu/drm/hisilicon/Kconfig @@ -0,0 +1,5 @@ +# +# hisilicon drm device configuration. +# Please keep this list sorted alphabetically
+source "drivers/gpu/drm/hisilicon/kirin/Kconfig" diff --git a/drivers/gpu/drm/hisilicon/Makefile b/drivers/gpu/drm/hisilicon/Makefile new file mode 100644 index 000000000000..e3f6d493c996 --- /dev/null +++ b/drivers/gpu/drm/hisilicon/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for hisilicon drm drivers. +# Please keep this list sorted alphabetically
+obj-$(CONFIG_DRM_HISI_KIRIN) += kirin/ diff --git a/drivers/gpu/drm/hisilicon/kirin/Kconfig b/drivers/gpu/drm/hisilicon/kirin/Kconfig new file mode 100644 index 000000000000..3ac4b8edeac1 --- /dev/null +++ b/drivers/gpu/drm/hisilicon/kirin/Kconfig @@ -0,0 +1,9 @@ +config DRM_HISI_KIRIN
tristate "DRM Support for Hisilicon Kirin series SoCs Platform"
depends on DRM
select DRM_KMS_HELPER
select DRM_GEM_CMA_HELPER
select DRM_KMS_CMA_HELPER
help
Choose this option if you have a hisilicon Kirin
chipsets(hi6220).
If M is selected the module will be called kirin-drm.
diff --git a/drivers/gpu/drm/hisilicon/kirin/Makefile b/drivers/gpu/drm/hisilicon/kirin/Makefile new file mode 100644 index 000000000000..cb346de47d48 --- /dev/null +++ b/drivers/gpu/drm/hisilicon/kirin/Makefile @@ -0,0 +1,3 @@ +kirin-drm-y := kirin_drm_drv.o
+obj-$(CONFIG_DRM_HISI_KIRIN) += kirin-drm.o diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c new file mode 100644 index 000000000000..789ebd1f5922 --- /dev/null +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c @@ -0,0 +1,321 @@ +/*
- Hisilicon Kirin SoCs drm master driver
- Copyright (c) 2016 Linaro Limited.
- Copyright (c) 2014-2016 Hisilicon Limited.
- Author:
Xinliang Liu <z.liuxinliang@hisilicon.com>
Xinliang Liu <xinliang.liu@linaro.org>
Xinwei Kong <kong.kongxinwei@hisilicon.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 as
- published by the Free Software Foundation.
- */
+#include <linux/of_platform.h> +#include <linux/component.h> +#include <linux/of_graph.h>
+#include <drm/drmP.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_atomic_helper.h>
+#include "kirin_drm_drv.h"
+static struct kirin_dc_ops *dc_ops;
+static int kirin_drm_kms_cleanup(struct drm_device *dev) +{
dc_ops->cleanup(dev);
drm_mode_config_cleanup(dev);
return 0;
+}
+static const struct drm_mode_config_funcs kirin_drm_mode_config_funcs = {
.fb_create = drm_fb_cma_create,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
+};
+static void kirin_drm_mode_config_init(struct drm_device *dev) +{
dev->mode_config.min_width = 0;
dev->mode_config.min_height = 0;
dev->mode_config.max_width = 2048;
dev->mode_config.max_height = 2048;
dev->mode_config.funcs = &kirin_drm_mode_config_funcs;
+}
+static int kirin_drm_kms_init(struct drm_device *dev) +{
int ret;
dev_set_drvdata(dev->dev, dev);
/* dev->mode_config initialization */
drm_mode_config_init(dev);
kirin_drm_mode_config_init(dev);
/* display controller init */
ret = dc_ops->init(dev);
if (ret)
goto err_mode_config_cleanup;
/* bind and init sub drivers */
ret = component_bind_all(dev->dev, dev);
if (ret) {
DRM_ERROR("failed to bind all component.\n");
goto err_dc_cleanup;
}
/* reset all the states of crtc/plane/encoder/connector */
drm_mode_config_reset(dev);
return 0;
+err_dc_cleanup:
dc_ops->cleanup(dev);
+err_mode_config_cleanup:
drm_mode_config_cleanup(dev);
return ret;
+}
+static const struct file_operations kirin_drm_fops = {
.owner = THIS_MODULE,
.open = drm_open,
.release = drm_release,
.unlocked_ioctl = drm_ioctl,
+#ifdef CONFIG_COMPAT
.compat_ioctl = drm_compat_ioctl,
+#endif
.poll = drm_poll,
.read = drm_read,
.llseek = no_llseek,
.mmap = drm_gem_cma_mmap,
+};
+static int kirin_gem_cma_dumb_create(struct drm_file *file,
struct drm_device *dev,
struct drm_mode_create_dumb *args)
+{
return drm_gem_cma_dumb_create_internal(file, dev, args);
+}
+static struct drm_driver kirin_drm_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET |
DRIVER_PRIME |
DRIVER_ATOMIC,
.fops = &kirin_drm_fops,
.set_busid = drm_platform_set_busid,
.gem_free_object = drm_gem_cma_free_object,
.gem_vm_ops = &drm_gem_cma_vm_ops,
.dumb_create = kirin_gem_cma_dumb_create,
.dumb_map_offset = drm_gem_cma_dumb_map_offset,
.dumb_destroy = drm_gem_dumb_destroy,
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
.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,
.gem_prime_vmap = drm_gem_cma_prime_vmap,
.gem_prime_vunmap = drm_gem_cma_prime_vunmap,
.gem_prime_mmap = drm_gem_cma_prime_mmap,
.name = "kirin",
.desc = "Hisilicon Kirin SoCs' DRM Driver",
.date = "20150718",
.major = 1,
.minor = 0,
+};
+static int compare_of(struct device *dev, void *data) +{
return dev->of_node == data;
+}
+static int kirin_drm_connectors_register(struct drm_device *dev) +{
struct drm_connector *connector;
struct drm_connector *failed_connector;
int ret;
This access needs to be protected by the dev->mode_config.mutex
thanks for pointing out this. will fix in next version.
list_for_each_entry(connector, &dev->mode_config.connector_list,
head) {
The above can be replaced with:
drm_for_each_connector(connector, dev) {
Great, this makes things more clear. will change in next version.
ret = drm_connector_register(connector);
if (ret) {
failed_connector = connector;
goto err;
}
}
return 0;
+err:
list_for_each_entry(connector, &dev->mode_config.connector_list,
head) {
if (failed_connector == connector)
break;
drm_connector_unregister(connector);
}
Same points as above.
return ret;
+}
+static int kirin_drm_bind(struct device *dev) +{
struct drm_driver *driver = &kirin_drm_driver;
struct drm_device *drm_dev;
int ret;
drm_dev = drm_dev_alloc(driver, dev);
if (!drm_dev)
return -ENOMEM;
drm_dev->platformdev = to_platform_device(dev);
ret = kirin_drm_kms_init(drm_dev);
if (ret)
goto err_drm_dev_unref;
ret = drm_dev_register(drm_dev, 0);
if (ret)
goto err_kms_cleanup;
/* connectors should be registered after drm device register */
ret = kirin_drm_connectors_register(drm_dev);
if (ret)
goto err_drm_dev_unregister;
DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
driver->name, driver->major, driver->minor,
driver->patchlevel,
driver->date, drm_dev->primary->index);
return 0;
+err_drm_dev_unregister:
drm_dev_unregister(drm_dev);
+err_kms_cleanup:
kirin_drm_kms_cleanup(drm_dev);
+err_drm_dev_unref:
drm_dev_unref(drm_dev);
return ret;
+}
+static void kirin_drm_unbind(struct device *dev) +{
drm_put_dev(dev_get_drvdata(dev));
+}
+static const struct component_master_ops kirin_drm_ops = {
.bind = kirin_drm_bind,
.unbind = kirin_drm_unbind,
+};
+static const struct of_device_id kirin_drm_dt_ids[] = {
{ /* end node */ },
+}; +MODULE_DEVICE_TABLE(of, kirin_drm_dt_ids);
+static int kirin_get_dc_ops(struct device_node *np) +{
const struct of_device_id *match_id;
match_id = of_match_node(kirin_drm_dt_ids, np);
if (!match_id) {
DRM_ERROR("failed to match dt id\n");
return -EINVAL;
}
dc_ops = (struct kirin_dc_ops *)match_id->data;
if (!dc_ops) {
DRM_ERROR("dt id data is null\n");
return -EINVAL;
}
We can use of_device_get_match_data to get the match data, and move the kirin_drm_dt_ids table down where the driver is defined.
Same, it is better to do like you said. will change in next version.
Thanks, -xinliang
<snip>
Thanks, Archit
-- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, hosted by The Linux Foundation
Add crtc funcs and helper funcs for ADE.
v6: - Cleanup reg-names dt parsing. v5: - Use syscon to access ADE media NOC QoS registers instread of directly writing registers. - Use reset controller to reset ADE instead of directly writing registers. v4: None. v3: - Make ade as the master driver. - Use port to connect with encoder. - A few cleanup. v2: - Remove abtraction layer.
Signed-off-by: Xinliang Liu xinliang.liu@linaro.org --- drivers/gpu/drm/hisilicon/kirin/Makefile | 3 +- drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h | 290 +++++++++++++++ drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c | 452 ++++++++++++++++++++++++ drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c | 15 + drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h | 8 + 5 files changed, 767 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h create mode 100644 drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c
diff --git a/drivers/gpu/drm/hisilicon/kirin/Makefile b/drivers/gpu/drm/hisilicon/kirin/Makefile index cb346de47d48..2a61ab006ddb 100644 --- a/drivers/gpu/drm/hisilicon/kirin/Makefile +++ b/drivers/gpu/drm/hisilicon/kirin/Makefile @@ -1,3 +1,4 @@ -kirin-drm-y := kirin_drm_drv.o +kirin-drm-y := kirin_drm_drv.o \ + kirin_drm_ade.o
obj-$(CONFIG_DRM_HISI_KIRIN) += kirin-drm.o diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h b/drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h new file mode 100644 index 000000000000..eb444b899c7b --- /dev/null +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2016 Linaro Limited. + * Copyright (c) 2014-2016 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef __KIRIN_ADE_REG_H__ +#define __KIRIN_ADE_REG_H__ + +/* + * ADE Registers + */ +#define MASK(x) (BIT(x) - 1) + +#define ADE_CTRL 0x0004 +#define FRM_END_START_OFST 0 +#define FRM_END_START_MASK MASK(2) +#define ADE_CTRL1 0x008C +#define AUTO_CLK_GATE_EN_OFST 0 +#define AUTO_CLK_GATE_EN BIT(0) +#define ADE_ROT_SRC_CFG 0x0010 +#define ADE_DISP_SRC_CFG 0x0018 +#define ADE_WDMA2_SRC_CFG 0x001C +#define ADE_SEC_OVLY_SRC_CFG 0x0020 +#define ADE_WDMA3_SRC_CFG 0x0024 +#define ADE_OVLY1_TRANS_CFG 0x002C +#define ADE_EN 0x0100 +#define ADE_DISABLE 0 +#define ADE_ENABLE 1 +#define INTR_MASK_CPU(x) (0x0C10 + (x) * 0x4) +#define ADE_FRM_DISGARD_CTRL 0x00A4 +/* reset and reload regs */ +#define ADE_SOFT_RST_SEL(x) (0x0078 + (x) * 0x4) +#define ADE_RELOAD_DIS(x) (0x00AC + (x) * 0x4) +#define RDMA_OFST 0 +#define CLIP_OFST 15 +#define SCL_OFST 21 +#define CTRAN_OFST 24 +#define OVLY_OFST 37 /* 32+5 */ +/* channel regs */ +#define RD_CH_PE(x) (0x1000 + (x) * 0x80) +#define RD_CH_CTRL(x) (0x1004 + (x) * 0x80) +#define RD_CH_ADDR(x) (0x1008 + (x) * 0x80) +#define RD_CH_SIZE(x) (0x100C + (x) * 0x80) +#define RD_CH_STRIDE(x) (0x1010 + (x) * 0x80) +#define RD_CH_SPACE(x) (0x1014 + (x) * 0x80) +#define RD_CH_PARTIAL_SIZE(x) (0x1018 + (x) * 0x80) +#define RD_CH_PARTIAL_SPACE(x) (0x101C + (x) * 0x80) +#define RD_CH_EN(x) (0x1020 + (x) * 0x80) +#define RD_CH_STATUS(x) (0x1024 + (x) * 0x80) +#define RD_CH_DISP_CTRL 0x1404 +#define RD_CH_DISP_ADDR 0x1408 +#define RD_CH_DISP_SIZE 0x140C +#define RD_CH_DISP_STRIDE 0x1410 +#define RD_CH_DISP_SPACE 0x1414 +#define RD_CH_DISP_EN 0x142C +/* clip regs */ +#define ADE_CLIP_DISABLE(x) (0x6800 + (x) * 0x100) +#define ADE_CLIP_SIZE0(x) (0x6804 + (x) * 0x100) +#define ADE_CLIP_SIZE1(x) (0x6808 + (x) * 0x100) +#define ADE_CLIP_SIZE2(x) (0x680C + (x) * 0x100) +#define ADE_CLIP_CFG_OK(x) (0x6810 + (x) * 0x100) +/* scale regs */ +#define ADE_SCL1_MUX_CFG 0x000C +#define ADE_SCL2_SRC_CFG 0x0014 +#define ADE_SCL3_MUX_CFG 0x0008 +#define ADE_SCL_CTRL(x) (0x3000 + (x) * 0x800) +#define ADE_SCL_HSP(x) (0x3004 + (x) * 0x800) +#define ADE_SCL_UV_HSP(x) (0x3008 + (x) * 0x800) +#define ADE_SCL_VSP(x) (0x300C + (x) * 0x800) +#define ADE_SCL_UV_VSP(x) (0x3010 + (x) * 0x800) +#define ADE_SCL_ORES(x) (0x3014 + (x) * 0x800) +#define ADE_SCL_IRES(x) (0x3018 + (x) * 0x800) +#define ADE_SCL_START(x) (0x301C + (x) * 0x800) +#define ADE_SCL_ERR(x) (0x3020 + (x) * 0x800) +#define ADE_SCL_PIX_OFST(x) (0x3024 + (x) * 0x800) +#define ADE_SCL_UV_PIX_OFST(x) (0x3028 + (x) * 0x800) +#define ADE_SCL_COEF_CLR(x) (0x3030 + (x) * 0x800) +#define ADE_SCL_HCOEF(x, m, n) (0x3100 + (x) * 0x800 + \ + 12 * (m) + 4 * (n)) +#define ADE_SCL_VCOEF(x, i, j) (0x340C + (x) * 0x800 + \ + 12 * (i) + 4 * (j)) +/* ctran regs */ +#define ADE_CTRAN5_TRANS_CFG 0x0040 +#define ADE_CTRAN_DIS(x) (0x5004 + (x) * 0x100) +#define CTRAN_BYPASS_ON 1 +#define CTRAN_BYPASS_OFF 0 +#define ADE_CTRAN_MODE_CHOOSE(x) (0x5008 + (x) * 0x100) +#define ADE_CTRAN_STAT(x) (0x500C + (x) * 0x100) +#define ADE_CTRAN_CHDC0(x) (0x5010 + (x) * 0x100) +#define ADE_CTRAN_CHDC1(x) (0x5014 + (x) * 0x100) +#define ADE_CTRAN_CHDC2(x) (0x5018 + (x) * 0x100) +#define ADE_CTRAN_CHDC3(x) (0x501C + (x) * 0x100) +#define ADE_CTRAN_CHDC4(x) (0x5020 + (x) * 0x100) +#define ADE_CTRAN_CHDC5(x) (0x5024 + (x) * 0x100) +#define ADE_CTRAN_CSC0(x) (0x5028 + (x) * 0x100) +#define ADE_CTRAN_CSC1(x) (0x502C + (x) * 0x100) +#define ADE_CTRAN_CSC2(x) (0x5030 + (x) * 0x100) +#define ADE_CTRAN_CSC3(x) (0x5034 + (x) * 0x100) +#define ADE_CTRAN_CSC4(x) (0x5038 + (x) * 0x100) +#define ADE_CTRAN_IMAGE_SIZE(x) (0x503C + (x) * 0x100) +#define ADE_CTRAN_CFG_OK(x) (0x5040 + (x) * 0x100) +/* overlay regs */ +#define ADE_OVLY_ALPHA_ST 0x2000 +#define ADE_OVLY_CH_XY0(x) (0x2004 + (x) * 4) +#define ADE_OVLY_CH_XY1(x) (0x2024 + (x) * 4) +#define ADE_OVLY_CH_CTL(x) (0x204C + (x) * 4) +#define ADE_OVLY_OUTPUT_SIZE(x) (0x2070 + (x) * 8) +#define OUTPUT_XSIZE_OFST 16 +#define ADE_OVLY_BASE_COLOR(x) (0x2074 + (x) * 8) +#define ADE_OVLYX_CTL(x) (0x209C + (x) * 4) +#define ADE_OVLY_CTL 0x0098 +#define CH_OVLY_SEL_OFST(x) ((x) * 4) +#define CH_OVLY_SEL_MASK MASK(2) +#define CH_OVLY_SEL_VAL(x) ((x) + 1) +#define CH_ALP_MODE_OFST 0 +#define CH_ALP_SEL_OFST 2 +#define CH_UNDER_ALP_SEL_OFST 4 +#define CH_EN_OFST 6 +#define CH_ALP_GBL_OFST 15 +#define CH_SEL_OFST 28 + +/* + * LDI Registers + */ +#define LDI_HRZ_CTRL0 0x7400 +#define HBP_OFST 20 +#define LDI_HRZ_CTRL1 0x7404 +#define LDI_VRT_CTRL0 0x7408 +#define VBP_OFST 20 +#define LDI_VRT_CTRL1 0x740C +#define LDI_PLR_CTRL 0x7410 +#define FLAG_NVSYNC BIT(0) +#define FLAG_NHSYNC BIT(1) +#define FLAG_NPIXCLK BIT(2) +#define FLAG_NDE BIT(3) +#define LDI_DSP_SIZE 0x7414 +#define VSIZE_OFST 20 +#define LDI_INT_EN 0x741C +#define FRAME_END_INT_EN_OFST 1 +#define LDI_CTRL 0x7420 +#define BPP_OFST 3 +#define DATA_GATE_EN BIT(2) +#define LDI_EN BIT(0) +#define LDI_ORG_INT 0x7424 +#define LDI_MSK_INT 0x7428 +#define LDI_INT_CLR 0x742C +#define LDI_WORK_MODE 0x7430 +#define LDI_DE_SPACE_LOW 0x7438 +#define LDI_MCU_INTS 0x7450 +#define LDI_MCU_INTE 0x7454 +#define LDI_MCU_INTC 0x7458 +#define LDI_HDMI_DSI_GT 0x7434 + +/* + * ADE media bus service regs + */ +#define ADE0_QOSGENERATOR_MODE 0x010C +#define QOSGENERATOR_MODE_MASK MASK(2) +#define ADE0_QOSGENERATOR_EXTCONTROL 0x0118 +#define SOCKET_QOS_EN BIT(0) +#define ADE1_QOSGENERATOR_MODE 0x020C +#define ADE1_QOSGENERATOR_EXTCONTROL 0x0218 + +/* + * ADE regs relevant enums + */ +enum frame_end_start { + /* regs take effective in every vsync */ + REG_EFFECTIVE_IN_VSYNC = 0, + /* regs take effective in fist ade en and every frame end */ + REG_EFFECTIVE_IN_ADEEN_FRMEND, + /* regs take effective in ade en immediately */ + REG_EFFECTIVE_IN_ADEEN, + /* regs take effective in first vsync and every frame end */ + REG_EFFECTIVE_IN_VSYNC_FRMEND +}; + +enum ade_fb_format { + ADE_RGB_565 = 0, + ADE_BGR_565, + ADE_XRGB_8888, + ADE_XBGR_8888, + ADE_ARGB_8888, + ADE_ABGR_8888, + ADE_RGBA_8888, + ADE_BGRA_8888, + ADE_RGB_888, + ADE_BGR_888 = 9, + ADE_FORMAT_NOT_SUPPORT = 800 +}; + +enum ade_channel { + ADE_CH1 = 0, /* channel 1 for primary plane */ + ADE_CH_NUM +}; + +enum ade_scale { + ADE_SCL1 = 0, + ADE_SCL2, + ADE_SCL3, + ADE_SCL_NUM +}; + +enum ade_ctran { + ADE_CTRAN1 = 0, + ADE_CTRAN2, + ADE_CTRAN3, + ADE_CTRAN4, + ADE_CTRAN5, + ADE_CTRAN6, + ADE_CTRAN_NUM +}; + +enum ade_overlay { + ADE_OVLY1 = 0, + ADE_OVLY2, + ADE_OVLY3, + ADE_OVLY_NUM +}; + +enum ade_alpha_mode { + ADE_ALP_GLOBAL = 0, + ADE_ALP_PIXEL, + ADE_ALP_PIXEL_AND_GLB +}; + +enum ade_alpha_blending_mode { + ADE_ALP_MUL_COEFF_0 = 0, /* alpha */ + ADE_ALP_MUL_COEFF_1, /* 1-alpha */ + ADE_ALP_MUL_COEFF_2, /* 0 */ + ADE_ALP_MUL_COEFF_3 /* 1 */ +}; + +/* + * LDI regs relevant enums + */ +enum dsi_pclk_en { + DSI_PCLK_ON = 0, + DSI_PCLK_OFF +}; + +enum ldi_output_format { + LDI_OUT_RGB_565 = 0, + LDI_OUT_RGB_666, + LDI_OUT_RGB_888 +}; + +enum ldi_work_mode { + TEST_MODE = 0, + NORMAL_MODE +}; + +enum ldi_input_source { + DISP_SRC_NONE = 0, + DISP_SRC_OVLY2, + DISP_SRC_DISP, + DISP_SRC_ROT, + DISP_SRC_SCL2 +}; + +/* + * ADE media bus service relevant enums + */ +enum qos_generator_mode { + FIXED_MODE = 0, + LIMITER_MODE, + BYPASS_MODE, + REGULATOR_MODE +}; + +/* + * Register Write/Read Helper functions + */ +static inline void ade_update_bits(void __iomem *addr, u32 bit_start, + u32 mask, u32 val) +{ + u32 tmp, orig; + + orig = readl(addr); + tmp = orig & ~(mask << bit_start); + tmp |= (val & mask) << bit_start; + writel(tmp, addr); +} + +#endif diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c new file mode 100644 index 000000000000..bb93616dcf3d --- /dev/null +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c @@ -0,0 +1,452 @@ +/* + * Hisilicon Hi6220 SoC ADE(Advanced Display Engine)'s crtc&plane driver + * + * Copyright (c) 2016 Linaro Limited. + * Copyright (c) 2014-2016 Hisilicon Limited. + * + * Author: + * Xinliang Liu z.liuxinliang@hisilicon.com + * Xinliang Liu xinliang.liu@linaro.org + * Xinwei Kong kong.kongxinwei@hisilicon.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/bitops.h> +#include <linux/clk.h> +#include <video/display_timing.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> +#include <linux/reset.h> + +#include <drm/drmP.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> + +#include "kirin_drm_drv.h" +#include "kirin_ade_reg.h" + +#define to_ade_crtc(crtc) \ + container_of(crtc, struct ade_crtc, base) + +struct ade_hw_ctx { + void __iomem *base; + struct regmap *noc_regmap; + struct clk *ade_core_clk; + struct clk *media_noc_clk; + struct clk *ade_pix_clk; + struct reset_control *reset; + bool power_on; + int irq; +}; + +struct ade_crtc { + struct drm_crtc base; + struct ade_hw_ctx *ctx; + bool enable; + u32 out_format; +}; + +struct ade_data { + struct ade_crtc acrtc; + struct ade_hw_ctx ctx; +}; + +static void ade_update_reload_bit(void __iomem *base, u32 bit_num, u32 val) +{ + u32 bit_ofst, reg_num; + + bit_ofst = bit_num % 32; + reg_num = bit_num / 32; + + ade_update_bits(base + ADE_RELOAD_DIS(reg_num), bit_ofst, + MASK(1), !!val); +} + +static u32 ade_read_reload_bit(void __iomem *base, u32 bit_num) +{ + u32 tmp, bit_ofst, reg_num; + + bit_ofst = bit_num % 32; + reg_num = bit_num / 32; + + tmp = readl(base + ADE_RELOAD_DIS(reg_num)); + return !!(BIT(bit_ofst) & tmp); +} + +static void ade_init(struct ade_hw_ctx *ctx) +{ + void __iomem *base = ctx->base; + + /* enable clk gate */ + ade_update_bits(base + ADE_CTRL1, AUTO_CLK_GATE_EN_OFST, + AUTO_CLK_GATE_EN, ADE_ENABLE); + /* clear overlay */ + writel(0, base + ADE_OVLY1_TRANS_CFG); + writel(0, base + ADE_OVLY_CTL); + writel(0, base + ADE_OVLYX_CTL(ADE_OVLY2)); + /* clear reset and reload regs */ + writel(MASK(32), base + ADE_SOFT_RST_SEL(0)); + writel(MASK(32), base + ADE_SOFT_RST_SEL(1)); + writel(MASK(32), base + ADE_RELOAD_DIS(0)); + writel(MASK(32), base + ADE_RELOAD_DIS(1)); + /* for video mode, that ade registers + * became effective at frame end + */ + ade_update_bits(base + ADE_CTRL, FRM_END_START_OFST, + FRM_END_START_MASK, REG_EFFECTIVE_IN_ADEEN_FRMEND); +} + +static void ade_ldi_set_mode(struct ade_crtc *acrtc, + struct drm_display_mode *mode, + struct drm_display_mode *adj_mode) +{ + struct ade_hw_ctx *ctx = acrtc->ctx; + void __iomem *base = ctx->base; + u32 width = mode->hdisplay; + u32 height = mode->vdisplay; + u32 hfp, hbp, hsw, vfp, vbp, vsw; + u32 plr_flags; + int ret; + + plr_flags = (mode->flags & DRM_MODE_FLAG_NVSYNC) ? FLAG_NVSYNC : 0; + plr_flags |= (mode->flags & DRM_MODE_FLAG_NHSYNC) ? FLAG_NHSYNC : 0; + hfp = mode->hsync_start - mode->hdisplay; + hbp = mode->htotal - mode->hsync_end; + hsw = mode->hsync_end - mode->hsync_start; + vfp = mode->vsync_start - mode->vdisplay; + vbp = mode->vtotal - mode->vsync_end; + vsw = mode->vsync_end - mode->vsync_start; + if (vsw > 15) { + DRM_DEBUG_DRIVER("vsw exceeded 15\n"); + vsw = 15; + } + + writel((hbp << HBP_OFST) | hfp, base + LDI_HRZ_CTRL0); + /* the configured value is actual value - 1 */ + writel(hsw - 1, base + LDI_HRZ_CTRL1); + writel((vbp << VBP_OFST) | vfp, base + LDI_VRT_CTRL0); + /* the configured value is actual value - 1 */ + writel(vsw - 1, base + LDI_VRT_CTRL1); + /* the configured value is actual value - 1 */ + writel(((height - 1) << VSIZE_OFST) | (width - 1), + base + LDI_DSP_SIZE); + writel(plr_flags, base + LDI_PLR_CTRL); + + /* Success should be guaranteed in mode_valid call back, + * so failer shouldn't happen here + */ + ret = clk_set_rate(ctx->ade_pix_clk, mode->clock * 1000); + if (ret) + DRM_ERROR("failed to set pixel clk %dHz (%d)\n", + mode->clock * 1000, ret); + adj_mode->clock = clk_get_rate(ctx->ade_pix_clk) / 1000; + + /* ctran6 setting */ + writel(CTRAN_BYPASS_ON, base + ADE_CTRAN_DIS(ADE_CTRAN6)); + /* the configured value is actual value - 1 */ + writel(width * height - 1, base + ADE_CTRAN_IMAGE_SIZE(ADE_CTRAN6)); + ade_update_reload_bit(base, CTRAN_OFST + ADE_CTRAN6, 0); + + DRM_DEBUG_DRIVER("set mode: %dx%d\n", width, height); +} + +static int ade_power_up(struct ade_hw_ctx *ctx) +{ + int ret; + + ret = clk_prepare_enable(ctx->media_noc_clk); + if (ret) { + DRM_ERROR("failed to enable media_noc_clk (%d)\n", ret); + return ret; + } + + ret = reset_control_deassert(ctx->reset); + if (ret) { + DRM_ERROR("failed to deassert reset\n"); + return ret; + } + + ret = clk_prepare_enable(ctx->ade_core_clk); + if (ret) { + DRM_ERROR("failed to enable ade_core_clk (%d)\n", ret); + return ret; + } + + ade_init(ctx); + ctx->power_on = true; + return 0; +} + +static void ade_power_down(struct ade_hw_ctx *ctx) +{ + void __iomem *base = ctx->base; + + writel(ADE_DISABLE, base + LDI_CTRL); + /* dsi pixel off */ + writel(DSI_PCLK_OFF, base + LDI_HDMI_DSI_GT); + + clk_disable_unprepare(ctx->ade_core_clk); + reset_control_assert(ctx->reset); + clk_disable_unprepare(ctx->media_noc_clk); + ctx->power_on = false; +} + +static void ade_set_medianoc_qos(struct ade_crtc *acrtc) +{ + struct ade_hw_ctx *ctx = acrtc->ctx; + struct regmap *map = ctx->noc_regmap; + + regmap_update_bits(map, ADE0_QOSGENERATOR_MODE, + QOSGENERATOR_MODE_MASK, BYPASS_MODE); + regmap_update_bits(map, ADE0_QOSGENERATOR_EXTCONTROL, + SOCKET_QOS_EN, SOCKET_QOS_EN); + + regmap_update_bits(map, ADE1_QOSGENERATOR_MODE, + QOSGENERATOR_MODE_MASK, BYPASS_MODE); + regmap_update_bits(map, ADE1_QOSGENERATOR_EXTCONTROL, + SOCKET_QOS_EN, SOCKET_QOS_EN); +} + +static void ade_display_enable(struct ade_crtc *acrtc) +{ + struct ade_hw_ctx *ctx = acrtc->ctx; + void __iomem *base = ctx->base; + u32 out_fmt = acrtc->out_format; + + /* display source setting */ + writel(DISP_SRC_OVLY2, base + ADE_DISP_SRC_CFG); + + /* enable ade */ + writel(ADE_ENABLE, base + ADE_EN); + /* enable ldi */ + writel(NORMAL_MODE, base + LDI_WORK_MODE); + writel((out_fmt << BPP_OFST) | DATA_GATE_EN | LDI_EN, + base + LDI_CTRL); + /* dsi pixel on */ + writel(DSI_PCLK_ON, base + LDI_HDMI_DSI_GT); +} + +static void ade_crtc_enable(struct drm_crtc *crtc) +{ + struct ade_crtc *acrtc = to_ade_crtc(crtc); + struct ade_hw_ctx *ctx = acrtc->ctx; + int ret; + + if (acrtc->enable) + return; + + if (!ctx->power_on) { + ret = ade_power_up(ctx); + if (ret) + return; + } + + ade_set_medianoc_qos(acrtc); + ade_display_enable(acrtc); + acrtc->enable = true; +} + +static void ade_crtc_disable(struct drm_crtc *crtc) +{ + struct ade_crtc *acrtc = to_ade_crtc(crtc); + struct ade_hw_ctx *ctx = acrtc->ctx; + + if (!acrtc->enable) + return; + + ade_power_down(ctx); + acrtc->enable = false; +} + +static int ade_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + /* do nothing */ + return 0; +} + +static void ade_crtc_mode_set_nofb(struct drm_crtc *crtc) +{ + struct ade_crtc *acrtc = to_ade_crtc(crtc); + struct ade_hw_ctx *ctx = acrtc->ctx; + struct drm_display_mode *mode = &crtc->state->mode; + struct drm_display_mode *adj_mode = &crtc->state->adjusted_mode; + + if (!ctx->power_on) + (void)ade_power_up(ctx); + ade_ldi_set_mode(acrtc, mode, adj_mode); +} + +static void ade_crtc_atomic_begin(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + struct ade_crtc *acrtc = to_ade_crtc(crtc); + struct ade_hw_ctx *ctx = acrtc->ctx; + + if (!ctx->power_on) + (void)ade_power_up(ctx); +} + +static void ade_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) + +{ + struct ade_crtc *acrtc = to_ade_crtc(crtc); + struct ade_hw_ctx *ctx = acrtc->ctx; + void __iomem *base = ctx->base; + + /* only crtc is eanbled regs take effect */ + if (acrtc->enable) { + /* flush ade regitsters */ + writel(ADE_ENABLE, base + ADE_EN); + } +} + +static const struct drm_crtc_helper_funcs ade_crtc_helper_funcs = { + .enable = ade_crtc_enable, + .disable = ade_crtc_disable, + .atomic_check = ade_crtc_atomic_check, + .mode_set_nofb = ade_crtc_mode_set_nofb, + .atomic_begin = ade_crtc_atomic_begin, + .atomic_flush = ade_crtc_atomic_flush, +}; + +static const struct drm_crtc_funcs ade_crtc_funcs = { + .destroy = drm_crtc_cleanup, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .reset = drm_atomic_helper_crtc_reset, + .set_property = drm_atomic_helper_crtc_set_property, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, +}; + +static int ade_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, + struct drm_plane *plane) +{ + struct kirin_drm_private *priv = dev->dev_private; + struct device_node *port; + int ret; + + /* set crtc port so that + * drm_of_find_possible_crtcs call works + */ + port = of_get_child_by_name(dev->dev->of_node, "port"); + if (!port) { + DRM_ERROR("no port node found in %s\n", + dev->dev->of_node->full_name); + return -EINVAL; + } + of_node_put(port); + crtc->port = port; + + ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL, + &ade_crtc_funcs, NULL); + if (ret) { + DRM_ERROR("failed to init crtc.\n"); + return ret; + } + + drm_crtc_helper_add(crtc, &ade_crtc_helper_funcs); + priv->crtc[drm_crtc_index(crtc)] = crtc; + + return 0; +} + +static int ade_dts_parse(struct platform_device *pdev, struct ade_hw_ctx *ctx) +{ + struct resource *res; + struct device *dev = &pdev->dev; + struct device_node *np = pdev->dev.of_node; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ctx->base = devm_ioremap_resource(dev, res); + if (IS_ERR(ctx->base)) { + DRM_ERROR("failed to remap ade io base\n"); + return PTR_ERR(ctx->base); + } + + ctx->reset = devm_reset_control_get(dev, NULL); + if (IS_ERR(ctx->reset)) + return PTR_ERR(ctx->reset); + + ctx->noc_regmap = + syscon_regmap_lookup_by_phandle(np, "hisilicon,noc-syscon"); + if (IS_ERR(ctx->noc_regmap)) { + DRM_ERROR("failed to get noc regmap\n"); + return PTR_ERR(ctx->noc_regmap); + } + + ctx->irq = platform_get_irq(pdev, 0); + if (ctx->irq < 0) { + DRM_ERROR("failed to get irq\n"); + return -ENODEV; + } + + ctx->ade_core_clk = devm_clk_get(dev, "clk_ade_core"); + if (!ctx->ade_core_clk) { + DRM_ERROR("failed to parse clk ADE_CORE\n"); + return -ENODEV; + } + + ctx->media_noc_clk = devm_clk_get(dev, "clk_codec_jpeg"); + if (!ctx->media_noc_clk) { + DRM_ERROR("failed to parse clk CODEC_JPEG\n"); + return -ENODEV; + } + + ctx->ade_pix_clk = devm_clk_get(dev, "clk_ade_pix"); + if (!ctx->ade_pix_clk) { + DRM_ERROR("failed to parse clk ADE_PIX\n"); + return -ENODEV; + } + + return 0; +} + +int ade_drm_init(struct drm_device *dev) +{ + struct platform_device *pdev = dev->platformdev; + struct ade_data *ade; + struct ade_hw_ctx *ctx; + struct ade_crtc *acrtc; + int ret; + + ade = devm_kzalloc(dev->dev, sizeof(*ade), GFP_KERNEL); + if (!ade) { + DRM_ERROR("failed to alloc ade_data\n"); + return -ENOMEM; + } + platform_set_drvdata(pdev, ade); + + ctx = &ade->ctx; + acrtc = &ade->acrtc; + acrtc->ctx = ctx; + acrtc->out_format = LDI_OUT_RGB_888; + + ret = ade_dts_parse(pdev, ctx); + if (ret) + return ret; + + return 0; +} + +void ade_drm_cleanup(struct drm_device *dev) +{ + struct platform_device *pdev = dev->platformdev; + struct ade_data *ade = platform_get_drvdata(pdev); + struct drm_crtc *crtc = &ade->acrtc.base; + + drm_crtc_cleanup(crtc); +} + +const struct kirin_dc_ops ade_dc_ops = { + .init = ade_drm_init, + .cleanup = ade_drm_cleanup +}; diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c index 789ebd1f5922..055729c1889c 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c @@ -30,8 +30,12 @@ static struct kirin_dc_ops *dc_ops;
static int kirin_drm_kms_cleanup(struct drm_device *dev) { + struct kirin_drm_private *priv = dev->dev_private; + dc_ops->cleanup(dev); drm_mode_config_cleanup(dev); + devm_kfree(dev->dev, priv); + dev->dev_private = NULL;
return 0; } @@ -55,8 +59,14 @@ static void kirin_drm_mode_config_init(struct drm_device *dev)
static int kirin_drm_kms_init(struct drm_device *dev) { + struct kirin_drm_private *priv; int ret;
+ priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dev->dev_private = priv; dev_set_drvdata(dev->dev, dev);
/* dev->mode_config initialization */ @@ -84,6 +94,8 @@ err_dc_cleanup: dc_ops->cleanup(dev); err_mode_config_cleanup: drm_mode_config_cleanup(dev); + devm_kfree(dev->dev, priv); + dev->dev_private = NULL;
return ret; } @@ -221,6 +233,9 @@ static const struct component_master_ops kirin_drm_ops = { };
static const struct of_device_id kirin_drm_dt_ids[] = { + { .compatible = "hisilicon,hi6220-ade", + .data = &ade_dc_ops, + }, { /* end node */ }, }; MODULE_DEVICE_TABLE(of, kirin_drm_dt_ids); diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h index 75e9d82356d4..5a05ad6a81db 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h @@ -11,10 +11,18 @@ #ifndef __KIRIN_DRM_DRV_H__ #define __KIRIN_DRM_DRV_H__
+#define MAX_CRTC 2 + /* display controller init/cleanup ops */ struct kirin_dc_ops { int (*init)(struct drm_device *dev); void (*cleanup)(struct drm_device *dev); };
+struct kirin_drm_private { + struct drm_crtc *crtc[MAX_CRTC]; +}; + +extern const struct kirin_dc_ops ade_dc_ops; + #endif /* __KIRIN_DRM_DRV_H__ */
On 2/26/2016 2:10 PM, Xinliang Liu wrote:
Add crtc funcs and helper funcs for ADE.
v6:
- Cleanup reg-names dt parsing.
v5:
- Use syscon to access ADE media NOC QoS registers instread of directly writing registers.
- Use reset controller to reset ADE instead of directly writing registers.
v4: None. v3:
- Make ade as the master driver.
- Use port to connect with encoder.
- A few cleanup.
v2:
- Remove abtraction layer.
Signed-off-by: Xinliang Liu xinliang.liu@linaro.org
drivers/gpu/drm/hisilicon/kirin/Makefile | 3 +- drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h | 290 +++++++++++++++ drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c | 452 ++++++++++++++++++++++++ drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c | 15 + drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h | 8 + 5 files changed, 767 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h create mode 100644 drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c
diff --git a/drivers/gpu/drm/hisilicon/kirin/Makefile b/drivers/gpu/drm/hisilicon/kirin/Makefile index cb346de47d48..2a61ab006ddb 100644 --- a/drivers/gpu/drm/hisilicon/kirin/Makefile +++ b/drivers/gpu/drm/hisilicon/kirin/Makefile @@ -1,3 +1,4 @@ -kirin-drm-y := kirin_drm_drv.o +kirin-drm-y := kirin_drm_drv.o \
kirin_drm_ade.o
obj-$(CONFIG_DRM_HISI_KIRIN) += kirin-drm.o
diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h b/drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h new file mode 100644 index 000000000000..eb444b899c7b --- /dev/null +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h @@ -0,0 +1,290 @@ +/*
- Copyright (c) 2016 Linaro Limited.
- Copyright (c) 2014-2016 Hisilicon Limited.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 as
- published by the Free Software Foundation.
- */
+#ifndef __KIRIN_ADE_REG_H__ +#define __KIRIN_ADE_REG_H__
+/*
- ADE Registers
- */
+#define MASK(x) (BIT(x) - 1)
+#define ADE_CTRL 0x0004 +#define FRM_END_START_OFST 0 +#define FRM_END_START_MASK MASK(2) +#define ADE_CTRL1 0x008C +#define AUTO_CLK_GATE_EN_OFST 0 +#define AUTO_CLK_GATE_EN BIT(0) +#define ADE_ROT_SRC_CFG 0x0010 +#define ADE_DISP_SRC_CFG 0x0018 +#define ADE_WDMA2_SRC_CFG 0x001C +#define ADE_SEC_OVLY_SRC_CFG 0x0020 +#define ADE_WDMA3_SRC_CFG 0x0024 +#define ADE_OVLY1_TRANS_CFG 0x002C +#define ADE_EN 0x0100 +#define ADE_DISABLE 0 +#define ADE_ENABLE 1 +#define INTR_MASK_CPU(x) (0x0C10 + (x) * 0x4) +#define ADE_FRM_DISGARD_CTRL 0x00A4 +/* reset and reload regs */ +#define ADE_SOFT_RST_SEL(x) (0x0078 + (x) * 0x4) +#define ADE_RELOAD_DIS(x) (0x00AC + (x) * 0x4) +#define RDMA_OFST 0 +#define CLIP_OFST 15 +#define SCL_OFST 21 +#define CTRAN_OFST 24 +#define OVLY_OFST 37 /* 32+5 */ +/* channel regs */ +#define RD_CH_PE(x) (0x1000 + (x) * 0x80) +#define RD_CH_CTRL(x) (0x1004 + (x) * 0x80) +#define RD_CH_ADDR(x) (0x1008 + (x) * 0x80) +#define RD_CH_SIZE(x) (0x100C + (x) * 0x80) +#define RD_CH_STRIDE(x) (0x1010 + (x) * 0x80) +#define RD_CH_SPACE(x) (0x1014 + (x) * 0x80) +#define RD_CH_PARTIAL_SIZE(x) (0x1018 + (x) * 0x80) +#define RD_CH_PARTIAL_SPACE(x) (0x101C + (x) * 0x80) +#define RD_CH_EN(x) (0x1020 + (x) * 0x80) +#define RD_CH_STATUS(x) (0x1024 + (x) * 0x80) +#define RD_CH_DISP_CTRL 0x1404 +#define RD_CH_DISP_ADDR 0x1408 +#define RD_CH_DISP_SIZE 0x140C +#define RD_CH_DISP_STRIDE 0x1410 +#define RD_CH_DISP_SPACE 0x1414 +#define RD_CH_DISP_EN 0x142C +/* clip regs */ +#define ADE_CLIP_DISABLE(x) (0x6800 + (x) * 0x100) +#define ADE_CLIP_SIZE0(x) (0x6804 + (x) * 0x100) +#define ADE_CLIP_SIZE1(x) (0x6808 + (x) * 0x100) +#define ADE_CLIP_SIZE2(x) (0x680C + (x) * 0x100) +#define ADE_CLIP_CFG_OK(x) (0x6810 + (x) * 0x100) +/* scale regs */ +#define ADE_SCL1_MUX_CFG 0x000C +#define ADE_SCL2_SRC_CFG 0x0014 +#define ADE_SCL3_MUX_CFG 0x0008 +#define ADE_SCL_CTRL(x) (0x3000 + (x) * 0x800) +#define ADE_SCL_HSP(x) (0x3004 + (x) * 0x800) +#define ADE_SCL_UV_HSP(x) (0x3008 + (x) * 0x800) +#define ADE_SCL_VSP(x) (0x300C + (x) * 0x800) +#define ADE_SCL_UV_VSP(x) (0x3010 + (x) * 0x800) +#define ADE_SCL_ORES(x) (0x3014 + (x) * 0x800) +#define ADE_SCL_IRES(x) (0x3018 + (x) * 0x800) +#define ADE_SCL_START(x) (0x301C + (x) * 0x800) +#define ADE_SCL_ERR(x) (0x3020 + (x) * 0x800) +#define ADE_SCL_PIX_OFST(x) (0x3024 + (x) * 0x800) +#define ADE_SCL_UV_PIX_OFST(x) (0x3028 + (x) * 0x800) +#define ADE_SCL_COEF_CLR(x) (0x3030 + (x) * 0x800) +#define ADE_SCL_HCOEF(x, m, n) (0x3100 + (x) * 0x800 + \
12 * (m) + 4 * (n))
+#define ADE_SCL_VCOEF(x, i, j) (0x340C + (x) * 0x800 + \
12 * (i) + 4 * (j))
+/* ctran regs */ +#define ADE_CTRAN5_TRANS_CFG 0x0040 +#define ADE_CTRAN_DIS(x) (0x5004 + (x) * 0x100) +#define CTRAN_BYPASS_ON 1 +#define CTRAN_BYPASS_OFF 0 +#define ADE_CTRAN_MODE_CHOOSE(x) (0x5008 + (x) * 0x100) +#define ADE_CTRAN_STAT(x) (0x500C + (x) * 0x100) +#define ADE_CTRAN_CHDC0(x) (0x5010 + (x) * 0x100) +#define ADE_CTRAN_CHDC1(x) (0x5014 + (x) * 0x100) +#define ADE_CTRAN_CHDC2(x) (0x5018 + (x) * 0x100) +#define ADE_CTRAN_CHDC3(x) (0x501C + (x) * 0x100) +#define ADE_CTRAN_CHDC4(x) (0x5020 + (x) * 0x100) +#define ADE_CTRAN_CHDC5(x) (0x5024 + (x) * 0x100) +#define ADE_CTRAN_CSC0(x) (0x5028 + (x) * 0x100) +#define ADE_CTRAN_CSC1(x) (0x502C + (x) * 0x100) +#define ADE_CTRAN_CSC2(x) (0x5030 + (x) * 0x100) +#define ADE_CTRAN_CSC3(x) (0x5034 + (x) * 0x100) +#define ADE_CTRAN_CSC4(x) (0x5038 + (x) * 0x100) +#define ADE_CTRAN_IMAGE_SIZE(x) (0x503C + (x) * 0x100) +#define ADE_CTRAN_CFG_OK(x) (0x5040 + (x) * 0x100) +/* overlay regs */ +#define ADE_OVLY_ALPHA_ST 0x2000 +#define ADE_OVLY_CH_XY0(x) (0x2004 + (x) * 4) +#define ADE_OVLY_CH_XY1(x) (0x2024 + (x) * 4) +#define ADE_OVLY_CH_CTL(x) (0x204C + (x) * 4) +#define ADE_OVLY_OUTPUT_SIZE(x) (0x2070 + (x) * 8) +#define OUTPUT_XSIZE_OFST 16 +#define ADE_OVLY_BASE_COLOR(x) (0x2074 + (x) * 8) +#define ADE_OVLYX_CTL(x) (0x209C + (x) * 4) +#define ADE_OVLY_CTL 0x0098 +#define CH_OVLY_SEL_OFST(x) ((x) * 4) +#define CH_OVLY_SEL_MASK MASK(2) +#define CH_OVLY_SEL_VAL(x) ((x) + 1) +#define CH_ALP_MODE_OFST 0 +#define CH_ALP_SEL_OFST 2 +#define CH_UNDER_ALP_SEL_OFST 4 +#define CH_EN_OFST 6 +#define CH_ALP_GBL_OFST 15 +#define CH_SEL_OFST 28
+/*
- LDI Registers
- */
+#define LDI_HRZ_CTRL0 0x7400 +#define HBP_OFST 20 +#define LDI_HRZ_CTRL1 0x7404 +#define LDI_VRT_CTRL0 0x7408 +#define VBP_OFST 20 +#define LDI_VRT_CTRL1 0x740C +#define LDI_PLR_CTRL 0x7410 +#define FLAG_NVSYNC BIT(0) +#define FLAG_NHSYNC BIT(1) +#define FLAG_NPIXCLK BIT(2) +#define FLAG_NDE BIT(3) +#define LDI_DSP_SIZE 0x7414 +#define VSIZE_OFST 20 +#define LDI_INT_EN 0x741C +#define FRAME_END_INT_EN_OFST 1 +#define LDI_CTRL 0x7420 +#define BPP_OFST 3 +#define DATA_GATE_EN BIT(2) +#define LDI_EN BIT(0) +#define LDI_ORG_INT 0x7424 +#define LDI_MSK_INT 0x7428 +#define LDI_INT_CLR 0x742C +#define LDI_WORK_MODE 0x7430 +#define LDI_DE_SPACE_LOW 0x7438 +#define LDI_MCU_INTS 0x7450 +#define LDI_MCU_INTE 0x7454 +#define LDI_MCU_INTC 0x7458 +#define LDI_HDMI_DSI_GT 0x7434
Maybe place this one after LDI_WORK_MODE to maintain the increasing order?
+/*
- ADE media bus service regs
- */
+#define ADE0_QOSGENERATOR_MODE 0x010C +#define QOSGENERATOR_MODE_MASK MASK(2) +#define ADE0_QOSGENERATOR_EXTCONTROL 0x0118 +#define SOCKET_QOS_EN BIT(0) +#define ADE1_QOSGENERATOR_MODE 0x020C +#define ADE1_QOSGENERATOR_EXTCONTROL 0x0218
+/*
- ADE regs relevant enums
- */
+enum frame_end_start {
- /* regs take effective in every vsync */
s/effective/effect
- REG_EFFECTIVE_IN_VSYNC = 0,
- /* regs take effective in fist ade en and every frame end */
- REG_EFFECTIVE_IN_ADEEN_FRMEND,
- /* regs take effective in ade en immediately */
- REG_EFFECTIVE_IN_ADEEN,
- /* regs take effective in first vsync and every frame end */
- REG_EFFECTIVE_IN_VSYNC_FRMEND
+};
+enum ade_fb_format {
- ADE_RGB_565 = 0,
- ADE_BGR_565,
- ADE_XRGB_8888,
- ADE_XBGR_8888,
- ADE_ARGB_8888,
- ADE_ABGR_8888,
- ADE_RGBA_8888,
- ADE_BGRA_8888,
- ADE_RGB_888,
- ADE_BGR_888 = 9,
- ADE_FORMAT_NOT_SUPPORT = 800
ADE_FORMAT_UNSUPPORTED would be better here.
+};
+enum ade_channel {
- ADE_CH1 = 0, /* channel 1 for primary plane */
- ADE_CH_NUM
+};
+enum ade_scale {
- ADE_SCL1 = 0,
- ADE_SCL2,
- ADE_SCL3,
- ADE_SCL_NUM
+};
+enum ade_ctran {
- ADE_CTRAN1 = 0,
- ADE_CTRAN2,
- ADE_CTRAN3,
- ADE_CTRAN4,
- ADE_CTRAN5,
- ADE_CTRAN6,
- ADE_CTRAN_NUM
+};
+enum ade_overlay {
- ADE_OVLY1 = 0,
- ADE_OVLY2,
- ADE_OVLY3,
- ADE_OVLY_NUM
+};
+enum ade_alpha_mode {
- ADE_ALP_GLOBAL = 0,
- ADE_ALP_PIXEL,
- ADE_ALP_PIXEL_AND_GLB
+};
+enum ade_alpha_blending_mode {
- ADE_ALP_MUL_COEFF_0 = 0, /* alpha */
- ADE_ALP_MUL_COEFF_1, /* 1-alpha */
- ADE_ALP_MUL_COEFF_2, /* 0 */
- ADE_ALP_MUL_COEFF_3 /* 1 */
+};
+/*
- LDI regs relevant enums
- */
+enum dsi_pclk_en {
- DSI_PCLK_ON = 0,
- DSI_PCLK_OFF
+};
+enum ldi_output_format {
- LDI_OUT_RGB_565 = 0,
- LDI_OUT_RGB_666,
- LDI_OUT_RGB_888
+};
+enum ldi_work_mode {
- TEST_MODE = 0,
- NORMAL_MODE
+};
+enum ldi_input_source {
- DISP_SRC_NONE = 0,
- DISP_SRC_OVLY2,
- DISP_SRC_DISP,
- DISP_SRC_ROT,
- DISP_SRC_SCL2
+};
+/*
- ADE media bus service relevant enums
- */
+enum qos_generator_mode {
- FIXED_MODE = 0,
- LIMITER_MODE,
- BYPASS_MODE,
- REGULATOR_MODE
+};
+/*
- Register Write/Read Helper functions
- */
+static inline void ade_update_bits(void __iomem *addr, u32 bit_start,
u32 mask, u32 val)
+{
- u32 tmp, orig;
- orig = readl(addr);
- tmp = orig & ~(mask << bit_start);
- tmp |= (val & mask) << bit_start;
- writel(tmp, addr);
+}
+#endif diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c new file mode 100644 index 000000000000..bb93616dcf3d --- /dev/null +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c @@ -0,0 +1,452 @@ +/*
- Hisilicon Hi6220 SoC ADE(Advanced Display Engine)'s crtc&plane driver
- Copyright (c) 2016 Linaro Limited.
- Copyright (c) 2014-2016 Hisilicon Limited.
- Author:
- Xinliang Liu z.liuxinliang@hisilicon.com
- Xinliang Liu xinliang.liu@linaro.org
- Xinwei Kong kong.kongxinwei@hisilicon.com
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 as
- published by the Free Software Foundation.
- */
+#include <linux/bitops.h> +#include <linux/clk.h> +#include <video/display_timing.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> +#include <linux/reset.h>
+#include <drm/drmP.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h>
+#include "kirin_drm_drv.h" +#include "kirin_ade_reg.h"
+#define to_ade_crtc(crtc) \
- container_of(crtc, struct ade_crtc, base)
+struct ade_hw_ctx {
- void __iomem *base;
- struct regmap *noc_regmap;
- struct clk *ade_core_clk;
- struct clk *media_noc_clk;
- struct clk *ade_pix_clk;
- struct reset_control *reset;
- bool power_on;
- int irq;
+};
+struct ade_crtc {
- struct drm_crtc base;
- struct ade_hw_ctx *ctx;
- bool enable;
- u32 out_format;
+};
+struct ade_data {
- struct ade_crtc acrtc;
- struct ade_hw_ctx ctx;
+};
+static void ade_update_reload_bit(void __iomem *base, u32 bit_num, u32 val) +{
- u32 bit_ofst, reg_num;
- bit_ofst = bit_num % 32;
- reg_num = bit_num / 32;
- ade_update_bits(base + ADE_RELOAD_DIS(reg_num), bit_ofst,
MASK(1), !!val);
+}
+static u32 ade_read_reload_bit(void __iomem *base, u32 bit_num) +{
- u32 tmp, bit_ofst, reg_num;
- bit_ofst = bit_num % 32;
- reg_num = bit_num / 32;
- tmp = readl(base + ADE_RELOAD_DIS(reg_num));
- return !!(BIT(bit_ofst) & tmp);
+}
+static void ade_init(struct ade_hw_ctx *ctx) +{
- void __iomem *base = ctx->base;
- /* enable clk gate */
- ade_update_bits(base + ADE_CTRL1, AUTO_CLK_GATE_EN_OFST,
AUTO_CLK_GATE_EN, ADE_ENABLE);
- /* clear overlay */
- writel(0, base + ADE_OVLY1_TRANS_CFG);
- writel(0, base + ADE_OVLY_CTL);
- writel(0, base + ADE_OVLYX_CTL(ADE_OVLY2));
- /* clear reset and reload regs */
- writel(MASK(32), base + ADE_SOFT_RST_SEL(0));
- writel(MASK(32), base + ADE_SOFT_RST_SEL(1));
- writel(MASK(32), base + ADE_RELOAD_DIS(0));
- writel(MASK(32), base + ADE_RELOAD_DIS(1));
- /* for video mode, that ade registers
* became effective at frame end
s/became/become
Consider re-writing this comment
*/
- ade_update_bits(base + ADE_CTRL, FRM_END_START_OFST,
FRM_END_START_MASK, REG_EFFECTIVE_IN_ADEEN_FRMEND);
+}
+static void ade_ldi_set_mode(struct ade_crtc *acrtc,
struct drm_display_mode *mode,
struct drm_display_mode *adj_mode)
+{
- struct ade_hw_ctx *ctx = acrtc->ctx;
- void __iomem *base = ctx->base;
- u32 width = mode->hdisplay;
- u32 height = mode->vdisplay;
- u32 hfp, hbp, hsw, vfp, vbp, vsw;
- u32 plr_flags;
- int ret;
- plr_flags = (mode->flags & DRM_MODE_FLAG_NVSYNC) ? FLAG_NVSYNC : 0;
- plr_flags |= (mode->flags & DRM_MODE_FLAG_NHSYNC) ? FLAG_NHSYNC : 0;
- hfp = mode->hsync_start - mode->hdisplay;
- hbp = mode->htotal - mode->hsync_end;
- hsw = mode->hsync_end - mode->hsync_start;
- vfp = mode->vsync_start - mode->vdisplay;
- vbp = mode->vtotal - mode->vsync_end;
- vsw = mode->vsync_end - mode->vsync_start;
- if (vsw > 15) {
DRM_DEBUG_DRIVER("vsw exceeded 15\n");
vsw = 15;
- }
- writel((hbp << HBP_OFST) | hfp, base + LDI_HRZ_CTRL0);
/* the configured value is actual value - 1 */
- writel(hsw - 1, base + LDI_HRZ_CTRL1);
- writel((vbp << VBP_OFST) | vfp, base + LDI_VRT_CTRL0);
/* the configured value is actual value - 1 */
- writel(vsw - 1, base + LDI_VRT_CTRL1);
/* the configured value is actual value - 1 */
- writel(((height - 1) << VSIZE_OFST) | (width - 1),
base + LDI_DSP_SIZE);
- writel(plr_flags, base + LDI_PLR_CTRL);
- /* Success should be guaranteed in mode_valid call back,
* so failer shouldn't happen here
s/failer/failure
Also, please use the standard way of writing multiple lines of comments here and elsewhere in the driver.
*/
- ret = clk_set_rate(ctx->ade_pix_clk, mode->clock * 1000);
- if (ret)
DRM_ERROR("failed to set pixel clk %dHz (%d)\n",
mode->clock * 1000, ret);
- adj_mode->clock = clk_get_rate(ctx->ade_pix_clk) / 1000;
- /* ctran6 setting */
- writel(CTRAN_BYPASS_ON, base + ADE_CTRAN_DIS(ADE_CTRAN6));
/* the configured value is actual value - 1 */
- writel(width * height - 1, base + ADE_CTRAN_IMAGE_SIZE(ADE_CTRAN6));
- ade_update_reload_bit(base, CTRAN_OFST + ADE_CTRAN6, 0);
- DRM_DEBUG_DRIVER("set mode: %dx%d\n", width, height);
+}
+static int ade_power_up(struct ade_hw_ctx *ctx) +{
- int ret;
- ret = clk_prepare_enable(ctx->media_noc_clk);
- if (ret) {
DRM_ERROR("failed to enable media_noc_clk (%d)\n", ret);
return ret;
- }
- ret = reset_control_deassert(ctx->reset);
- if (ret) {
DRM_ERROR("failed to deassert reset\n");
return ret;
- }
- ret = clk_prepare_enable(ctx->ade_core_clk);
- if (ret) {
DRM_ERROR("failed to enable ade_core_clk (%d)\n", ret);
return ret;
- }
- ade_init(ctx);
- ctx->power_on = true;
- return 0;
+}
+static void ade_power_down(struct ade_hw_ctx *ctx) +{
- void __iomem *base = ctx->base;
- writel(ADE_DISABLE, base + LDI_CTRL);
- /* dsi pixel off */
- writel(DSI_PCLK_OFF, base + LDI_HDMI_DSI_GT);
- clk_disable_unprepare(ctx->ade_core_clk);
- reset_control_assert(ctx->reset);
- clk_disable_unprepare(ctx->media_noc_clk);
- ctx->power_on = false;
+}
+static void ade_set_medianoc_qos(struct ade_crtc *acrtc) +{
- struct ade_hw_ctx *ctx = acrtc->ctx;
- struct regmap *map = ctx->noc_regmap;
- regmap_update_bits(map, ADE0_QOSGENERATOR_MODE,
QOSGENERATOR_MODE_MASK, BYPASS_MODE);
- regmap_update_bits(map, ADE0_QOSGENERATOR_EXTCONTROL,
SOCKET_QOS_EN, SOCKET_QOS_EN);
- regmap_update_bits(map, ADE1_QOSGENERATOR_MODE,
QOSGENERATOR_MODE_MASK, BYPASS_MODE);
- regmap_update_bits(map, ADE1_QOSGENERATOR_EXTCONTROL,
SOCKET_QOS_EN, SOCKET_QOS_EN);
+}
+static void ade_display_enable(struct ade_crtc *acrtc) +{
- struct ade_hw_ctx *ctx = acrtc->ctx;
- void __iomem *base = ctx->base;
- u32 out_fmt = acrtc->out_format;
- /* display source setting */
- writel(DISP_SRC_OVLY2, base + ADE_DISP_SRC_CFG);
- /* enable ade */
- writel(ADE_ENABLE, base + ADE_EN);
- /* enable ldi */
- writel(NORMAL_MODE, base + LDI_WORK_MODE);
- writel((out_fmt << BPP_OFST) | DATA_GATE_EN | LDI_EN,
base + LDI_CTRL);
- /* dsi pixel on */
- writel(DSI_PCLK_ON, base + LDI_HDMI_DSI_GT);
+}
+static void ade_crtc_enable(struct drm_crtc *crtc) +{
- struct ade_crtc *acrtc = to_ade_crtc(crtc);
- struct ade_hw_ctx *ctx = acrtc->ctx;
- int ret;
- if (acrtc->enable)
return;
- if (!ctx->power_on) {
ret = ade_power_up(ctx);
if (ret)
return;
- }
- ade_set_medianoc_qos(acrtc);
- ade_display_enable(acrtc);
- acrtc->enable = true;
+}
+static void ade_crtc_disable(struct drm_crtc *crtc) +{
- struct ade_crtc *acrtc = to_ade_crtc(crtc);
- struct ade_hw_ctx *ctx = acrtc->ctx;
- if (!acrtc->enable)
return;
- ade_power_down(ctx);
- acrtc->enable = false;
+}
+static int ade_crtc_atomic_check(struct drm_crtc *crtc,
struct drm_crtc_state *state)
+{
- /* do nothing */
- return 0;
+}
+static void ade_crtc_mode_set_nofb(struct drm_crtc *crtc) +{
- struct ade_crtc *acrtc = to_ade_crtc(crtc);
- struct ade_hw_ctx *ctx = acrtc->ctx;
- struct drm_display_mode *mode = &crtc->state->mode;
- struct drm_display_mode *adj_mode = &crtc->state->adjusted_mode;
- if (!ctx->power_on)
(void)ade_power_up(ctx);
- ade_ldi_set_mode(acrtc, mode, adj_mode);
+}
+static void ade_crtc_atomic_begin(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
+{
- struct ade_crtc *acrtc = to_ade_crtc(crtc);
- struct ade_hw_ctx *ctx = acrtc->ctx;
- if (!ctx->power_on)
(void)ade_power_up(ctx);
+}
+static void ade_crtc_atomic_flush(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
+{
- struct ade_crtc *acrtc = to_ade_crtc(crtc);
- struct ade_hw_ctx *ctx = acrtc->ctx;
- void __iomem *base = ctx->base;
- /* only crtc is eanbled regs take effect */
s/eanbled/enabled
- if (acrtc->enable) {
/* flush ade regitsters */
s/regitsters/registers
Other than the minor comments:
Reviewed-by: Archit Taneja architt@codeaurora.org
writel(ADE_ENABLE, base + ADE_EN);
- }
+}
+static const struct drm_crtc_helper_funcs ade_crtc_helper_funcs = {
- .enable = ade_crtc_enable,
- .disable = ade_crtc_disable,
- .atomic_check = ade_crtc_atomic_check,
- .mode_set_nofb = ade_crtc_mode_set_nofb,
- .atomic_begin = ade_crtc_atomic_begin,
- .atomic_flush = ade_crtc_atomic_flush,
+};
+static const struct drm_crtc_funcs ade_crtc_funcs = {
- .destroy = drm_crtc_cleanup,
- .set_config = drm_atomic_helper_set_config,
- .page_flip = drm_atomic_helper_page_flip,
- .reset = drm_atomic_helper_crtc_reset,
- .set_property = drm_atomic_helper_crtc_set_property,
- .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+static int ade_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
struct drm_plane *plane)
+{
- struct kirin_drm_private *priv = dev->dev_private;
- struct device_node *port;
- int ret;
- /* set crtc port so that
* drm_of_find_possible_crtcs call works
*/
- port = of_get_child_by_name(dev->dev->of_node, "port");
- if (!port) {
DRM_ERROR("no port node found in %s\n",
dev->dev->of_node->full_name);
return -EINVAL;
- }
- of_node_put(port);
- crtc->port = port;
- ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
&ade_crtc_funcs, NULL);
- if (ret) {
DRM_ERROR("failed to init crtc.\n");
return ret;
- }
- drm_crtc_helper_add(crtc, &ade_crtc_helper_funcs);
- priv->crtc[drm_crtc_index(crtc)] = crtc;
- return 0;
+}
+static int ade_dts_parse(struct platform_device *pdev, struct ade_hw_ctx *ctx) +{
- struct resource *res;
- struct device *dev = &pdev->dev;
- struct device_node *np = pdev->dev.of_node;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- ctx->base = devm_ioremap_resource(dev, res);
- if (IS_ERR(ctx->base)) {
DRM_ERROR("failed to remap ade io base\n");
return PTR_ERR(ctx->base);
- }
- ctx->reset = devm_reset_control_get(dev, NULL);
- if (IS_ERR(ctx->reset))
return PTR_ERR(ctx->reset);
- ctx->noc_regmap =
syscon_regmap_lookup_by_phandle(np, "hisilicon,noc-syscon");
- if (IS_ERR(ctx->noc_regmap)) {
DRM_ERROR("failed to get noc regmap\n");
return PTR_ERR(ctx->noc_regmap);
- }
- ctx->irq = platform_get_irq(pdev, 0);
- if (ctx->irq < 0) {
DRM_ERROR("failed to get irq\n");
return -ENODEV;
- }
- ctx->ade_core_clk = devm_clk_get(dev, "clk_ade_core");
- if (!ctx->ade_core_clk) {
DRM_ERROR("failed to parse clk ADE_CORE\n");
return -ENODEV;
- }
- ctx->media_noc_clk = devm_clk_get(dev, "clk_codec_jpeg");
- if (!ctx->media_noc_clk) {
DRM_ERROR("failed to parse clk CODEC_JPEG\n");
return -ENODEV;
- }
- ctx->ade_pix_clk = devm_clk_get(dev, "clk_ade_pix");
- if (!ctx->ade_pix_clk) {
DRM_ERROR("failed to parse clk ADE_PIX\n");
return -ENODEV;
- }
- return 0;
+}
+int ade_drm_init(struct drm_device *dev) +{
- struct platform_device *pdev = dev->platformdev;
- struct ade_data *ade;
- struct ade_hw_ctx *ctx;
- struct ade_crtc *acrtc;
- int ret;
- ade = devm_kzalloc(dev->dev, sizeof(*ade), GFP_KERNEL);
- if (!ade) {
DRM_ERROR("failed to alloc ade_data\n");
return -ENOMEM;
- }
- platform_set_drvdata(pdev, ade);
- ctx = &ade->ctx;
- acrtc = &ade->acrtc;
- acrtc->ctx = ctx;
- acrtc->out_format = LDI_OUT_RGB_888;
- ret = ade_dts_parse(pdev, ctx);
- if (ret)
return ret;
- return 0;
+}
+void ade_drm_cleanup(struct drm_device *dev) +{
- struct platform_device *pdev = dev->platformdev;
- struct ade_data *ade = platform_get_drvdata(pdev);
- struct drm_crtc *crtc = &ade->acrtc.base;
- drm_crtc_cleanup(crtc);
+}
+const struct kirin_dc_ops ade_dc_ops = {
- .init = ade_drm_init,
- .cleanup = ade_drm_cleanup
+}; diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c index 789ebd1f5922..055729c1889c 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c @@ -30,8 +30,12 @@ static struct kirin_dc_ops *dc_ops;
static int kirin_drm_kms_cleanup(struct drm_device *dev) {
struct kirin_drm_private *priv = dev->dev_private;
dc_ops->cleanup(dev); drm_mode_config_cleanup(dev);
devm_kfree(dev->dev, priv);
dev->dev_private = NULL;
return 0; }
@@ -55,8 +59,14 @@ static void kirin_drm_mode_config_init(struct drm_device *dev)
static int kirin_drm_kms_init(struct drm_device *dev) {
struct kirin_drm_private *priv; int ret;
priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
dev->dev_private = priv; dev_set_drvdata(dev->dev, dev);
/* dev->mode_config initialization */
@@ -84,6 +94,8 @@ err_dc_cleanup: dc_ops->cleanup(dev); err_mode_config_cleanup: drm_mode_config_cleanup(dev);
devm_kfree(dev->dev, priv);
dev->dev_private = NULL;
return ret; }
@@ -221,6 +233,9 @@ static const struct component_master_ops kirin_drm_ops = { };
static const struct of_device_id kirin_drm_dt_ids[] = {
- { .compatible = "hisilicon,hi6220-ade",
.data = &ade_dc_ops,
- }, { /* end node */ }, }; MODULE_DEVICE_TABLE(of, kirin_drm_dt_ids);
diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h index 75e9d82356d4..5a05ad6a81db 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h @@ -11,10 +11,18 @@ #ifndef __KIRIN_DRM_DRV_H__ #define __KIRIN_DRM_DRV_H__
+#define MAX_CRTC 2
- /* display controller init/cleanup ops */ struct kirin_dc_ops { int (*init)(struct drm_device *dev); void (*cleanup)(struct drm_device *dev); };
+struct kirin_drm_private {
- struct drm_crtc *crtc[MAX_CRTC];
+};
+extern const struct kirin_dc_ops ade_dc_ops;
- #endif /* __KIRIN_DRM_DRV_H__ */
Hi,
On 1 March 2016 at 02:48, Archit Taneja architt@codeaurora.org wrote:
On 2/26/2016 2:10 PM, Xinliang Liu wrote:
Add crtc funcs and helper funcs for ADE.
v6:
- Cleanup reg-names dt parsing.
v5:
- Use syscon to access ADE media NOC QoS registers instread of directly writing registers.
- Use reset controller to reset ADE instead of directly writing registers.
v4: None. v3:
- Make ade as the master driver.
- Use port to connect with encoder.
- A few cleanup.
v2:
- Remove abtraction layer.
Signed-off-by: Xinliang Liu xinliang.liu@linaro.org
drivers/gpu/drm/hisilicon/kirin/Makefile | 3 +- drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h | 290 +++++++++++++++ drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c | 452 ++++++++++++++++++++++++ drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c | 15 + drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h | 8 + 5 files changed, 767 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h create mode 100644 drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c
diff --git a/drivers/gpu/drm/hisilicon/kirin/Makefile b/drivers/gpu/drm/hisilicon/kirin/Makefile index cb346de47d48..2a61ab006ddb 100644 --- a/drivers/gpu/drm/hisilicon/kirin/Makefile +++ b/drivers/gpu/drm/hisilicon/kirin/Makefile @@ -1,3 +1,4 @@ -kirin-drm-y := kirin_drm_drv.o +kirin-drm-y := kirin_drm_drv.o \
kirin_drm_ade.o
obj-$(CONFIG_DRM_HISI_KIRIN) += kirin-drm.o
diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h b/drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h new file mode 100644 index 000000000000..eb444b899c7b --- /dev/null +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h @@ -0,0 +1,290 @@ +/*
- Copyright (c) 2016 Linaro Limited.
- Copyright (c) 2014-2016 Hisilicon Limited.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 as
- published by the Free Software Foundation.
- */
+#ifndef __KIRIN_ADE_REG_H__ +#define __KIRIN_ADE_REG_H__
+/*
- ADE Registers
- */
+#define MASK(x) (BIT(x) - 1)
+#define ADE_CTRL 0x0004 +#define FRM_END_START_OFST 0 +#define FRM_END_START_MASK MASK(2) +#define ADE_CTRL1 0x008C +#define AUTO_CLK_GATE_EN_OFST 0 +#define AUTO_CLK_GATE_EN BIT(0) +#define ADE_ROT_SRC_CFG 0x0010 +#define ADE_DISP_SRC_CFG 0x0018 +#define ADE_WDMA2_SRC_CFG 0x001C +#define ADE_SEC_OVLY_SRC_CFG 0x0020 +#define ADE_WDMA3_SRC_CFG 0x0024 +#define ADE_OVLY1_TRANS_CFG 0x002C +#define ADE_EN 0x0100 +#define ADE_DISABLE 0 +#define ADE_ENABLE 1 +#define INTR_MASK_CPU(x) (0x0C10 + (x) * 0x4) +#define ADE_FRM_DISGARD_CTRL 0x00A4 +/* reset and reload regs */ +#define ADE_SOFT_RST_SEL(x) (0x0078 + (x) * 0x4) +#define ADE_RELOAD_DIS(x) (0x00AC + (x) * 0x4) +#define RDMA_OFST 0 +#define CLIP_OFST 15 +#define SCL_OFST 21 +#define CTRAN_OFST 24 +#define OVLY_OFST 37 /* 32+5 */ +/* channel regs */ +#define RD_CH_PE(x) (0x1000 + (x) * 0x80) +#define RD_CH_CTRL(x) (0x1004 + (x) * 0x80) +#define RD_CH_ADDR(x) (0x1008 + (x) * 0x80) +#define RD_CH_SIZE(x) (0x100C + (x) * 0x80) +#define RD_CH_STRIDE(x) (0x1010 + (x) * 0x80) +#define RD_CH_SPACE(x) (0x1014 + (x) * 0x80) +#define RD_CH_PARTIAL_SIZE(x) (0x1018 + (x) * 0x80) +#define RD_CH_PARTIAL_SPACE(x) (0x101C + (x) * 0x80) +#define RD_CH_EN(x) (0x1020 + (x) * 0x80) +#define RD_CH_STATUS(x) (0x1024 + (x) * 0x80) +#define RD_CH_DISP_CTRL 0x1404 +#define RD_CH_DISP_ADDR 0x1408 +#define RD_CH_DISP_SIZE 0x140C +#define RD_CH_DISP_STRIDE 0x1410 +#define RD_CH_DISP_SPACE 0x1414 +#define RD_CH_DISP_EN 0x142C +/* clip regs */ +#define ADE_CLIP_DISABLE(x) (0x6800 + (x) * 0x100) +#define ADE_CLIP_SIZE0(x) (0x6804 + (x) * 0x100) +#define ADE_CLIP_SIZE1(x) (0x6808 + (x) * 0x100) +#define ADE_CLIP_SIZE2(x) (0x680C + (x) * 0x100) +#define ADE_CLIP_CFG_OK(x) (0x6810 + (x) * 0x100) +/* scale regs */ +#define ADE_SCL1_MUX_CFG 0x000C +#define ADE_SCL2_SRC_CFG 0x0014 +#define ADE_SCL3_MUX_CFG 0x0008 +#define ADE_SCL_CTRL(x) (0x3000 + (x) * 0x800) +#define ADE_SCL_HSP(x) (0x3004 + (x) * 0x800) +#define ADE_SCL_UV_HSP(x) (0x3008 + (x) * 0x800) +#define ADE_SCL_VSP(x) (0x300C + (x) * 0x800) +#define ADE_SCL_UV_VSP(x) (0x3010 + (x) * 0x800) +#define ADE_SCL_ORES(x) (0x3014 + (x) * 0x800) +#define ADE_SCL_IRES(x) (0x3018 + (x) * 0x800) +#define ADE_SCL_START(x) (0x301C + (x) * 0x800) +#define ADE_SCL_ERR(x) (0x3020 + (x) * 0x800) +#define ADE_SCL_PIX_OFST(x) (0x3024 + (x) * 0x800) +#define ADE_SCL_UV_PIX_OFST(x) (0x3028 + (x) * 0x800) +#define ADE_SCL_COEF_CLR(x) (0x3030 + (x) * 0x800) +#define ADE_SCL_HCOEF(x, m, n) (0x3100 + (x) * 0x800 + \
12 * (m) + 4 * (n))
+#define ADE_SCL_VCOEF(x, i, j) (0x340C + (x) * 0x800 + \
12 * (i) + 4 * (j))
+/* ctran regs */ +#define ADE_CTRAN5_TRANS_CFG 0x0040 +#define ADE_CTRAN_DIS(x) (0x5004 + (x) * 0x100) +#define CTRAN_BYPASS_ON 1 +#define CTRAN_BYPASS_OFF 0 +#define ADE_CTRAN_MODE_CHOOSE(x) (0x5008 + (x) * 0x100) +#define ADE_CTRAN_STAT(x) (0x500C + (x) * 0x100) +#define ADE_CTRAN_CHDC0(x) (0x5010 + (x) * 0x100) +#define ADE_CTRAN_CHDC1(x) (0x5014 + (x) * 0x100) +#define ADE_CTRAN_CHDC2(x) (0x5018 + (x) * 0x100) +#define ADE_CTRAN_CHDC3(x) (0x501C + (x) * 0x100) +#define ADE_CTRAN_CHDC4(x) (0x5020 + (x) * 0x100) +#define ADE_CTRAN_CHDC5(x) (0x5024 + (x) * 0x100) +#define ADE_CTRAN_CSC0(x) (0x5028 + (x) * 0x100) +#define ADE_CTRAN_CSC1(x) (0x502C + (x) * 0x100) +#define ADE_CTRAN_CSC2(x) (0x5030 + (x) * 0x100) +#define ADE_CTRAN_CSC3(x) (0x5034 + (x) * 0x100) +#define ADE_CTRAN_CSC4(x) (0x5038 + (x) * 0x100) +#define ADE_CTRAN_IMAGE_SIZE(x) (0x503C + (x) * 0x100) +#define ADE_CTRAN_CFG_OK(x) (0x5040 + (x) * 0x100) +/* overlay regs */ +#define ADE_OVLY_ALPHA_ST 0x2000 +#define ADE_OVLY_CH_XY0(x) (0x2004 + (x) * 4) +#define ADE_OVLY_CH_XY1(x) (0x2024 + (x) * 4) +#define ADE_OVLY_CH_CTL(x) (0x204C + (x) * 4) +#define ADE_OVLY_OUTPUT_SIZE(x) (0x2070 + (x) * 8) +#define OUTPUT_XSIZE_OFST 16 +#define ADE_OVLY_BASE_COLOR(x) (0x2074 + (x) * 8) +#define ADE_OVLYX_CTL(x) (0x209C + (x) * 4) +#define ADE_OVLY_CTL 0x0098 +#define CH_OVLY_SEL_OFST(x) ((x) * 4) +#define CH_OVLY_SEL_MASK MASK(2) +#define CH_OVLY_SEL_VAL(x) ((x) + 1) +#define CH_ALP_MODE_OFST 0 +#define CH_ALP_SEL_OFST 2 +#define CH_UNDER_ALP_SEL_OFST 4 +#define CH_EN_OFST 6 +#define CH_ALP_GBL_OFST 15 +#define CH_SEL_OFST 28
+/*
- LDI Registers
- */
+#define LDI_HRZ_CTRL0 0x7400 +#define HBP_OFST 20 +#define LDI_HRZ_CTRL1 0x7404 +#define LDI_VRT_CTRL0 0x7408 +#define VBP_OFST 20 +#define LDI_VRT_CTRL1 0x740C +#define LDI_PLR_CTRL 0x7410 +#define FLAG_NVSYNC BIT(0) +#define FLAG_NHSYNC BIT(1) +#define FLAG_NPIXCLK BIT(2) +#define FLAG_NDE BIT(3) +#define LDI_DSP_SIZE 0x7414 +#define VSIZE_OFST 20 +#define LDI_INT_EN 0x741C +#define FRAME_END_INT_EN_OFST 1 +#define LDI_CTRL 0x7420 +#define BPP_OFST 3 +#define DATA_GATE_EN BIT(2) +#define LDI_EN BIT(0) +#define LDI_ORG_INT 0x7424 +#define LDI_MSK_INT 0x7428 +#define LDI_INT_CLR 0x742C +#define LDI_WORK_MODE 0x7430 +#define LDI_DE_SPACE_LOW 0x7438 +#define LDI_MCU_INTS 0x7450 +#define LDI_MCU_INTE 0x7454 +#define LDI_MCU_INTC 0x7458 +#define LDI_HDMI_DSI_GT 0x7434
Maybe place this one after LDI_WORK_MODE to maintain the increasing order?
Agree, will fix in next version.
+/*
- ADE media bus service regs
- */
+#define ADE0_QOSGENERATOR_MODE 0x010C +#define QOSGENERATOR_MODE_MASK MASK(2) +#define ADE0_QOSGENERATOR_EXTCONTROL 0x0118 +#define SOCKET_QOS_EN BIT(0) +#define ADE1_QOSGENERATOR_MODE 0x020C +#define ADE1_QOSGENERATOR_EXTCONTROL 0x0218
+/*
- ADE regs relevant enums
- */
+enum frame_end_start {
/* regs take effective in every vsync */
s/effective/effect
will fix in next version.
REG_EFFECTIVE_IN_VSYNC = 0,
/* regs take effective in fist ade en and every frame end */
REG_EFFECTIVE_IN_ADEEN_FRMEND,
/* regs take effective in ade en immediately */
REG_EFFECTIVE_IN_ADEEN,
/* regs take effective in first vsync and every frame end */
REG_EFFECTIVE_IN_VSYNC_FRMEND
+};
+enum ade_fb_format {
ADE_RGB_565 = 0,
ADE_BGR_565,
ADE_XRGB_8888,
ADE_XBGR_8888,
ADE_ARGB_8888,
ADE_ABGR_8888,
ADE_RGBA_8888,
ADE_BGRA_8888,
ADE_RGB_888,
ADE_BGR_888 = 9,
ADE_FORMAT_NOT_SUPPORT = 800
ADE_FORMAT_UNSUPPORTED would be better here.
Agree, will fix in next version.
+};
+enum ade_channel {
ADE_CH1 = 0, /* channel 1 for primary plane */
ADE_CH_NUM
+};
+enum ade_scale {
ADE_SCL1 = 0,
ADE_SCL2,
ADE_SCL3,
ADE_SCL_NUM
+};
+enum ade_ctran {
ADE_CTRAN1 = 0,
ADE_CTRAN2,
ADE_CTRAN3,
ADE_CTRAN4,
ADE_CTRAN5,
ADE_CTRAN6,
ADE_CTRAN_NUM
+};
+enum ade_overlay {
ADE_OVLY1 = 0,
ADE_OVLY2,
ADE_OVLY3,
ADE_OVLY_NUM
+};
+enum ade_alpha_mode {
ADE_ALP_GLOBAL = 0,
ADE_ALP_PIXEL,
ADE_ALP_PIXEL_AND_GLB
+};
+enum ade_alpha_blending_mode {
ADE_ALP_MUL_COEFF_0 = 0, /* alpha */
ADE_ALP_MUL_COEFF_1, /* 1-alpha */
ADE_ALP_MUL_COEFF_2, /* 0 */
ADE_ALP_MUL_COEFF_3 /* 1 */
+};
+/*
- LDI regs relevant enums
- */
+enum dsi_pclk_en {
DSI_PCLK_ON = 0,
DSI_PCLK_OFF
+};
+enum ldi_output_format {
LDI_OUT_RGB_565 = 0,
LDI_OUT_RGB_666,
LDI_OUT_RGB_888
+};
+enum ldi_work_mode {
TEST_MODE = 0,
NORMAL_MODE
+};
+enum ldi_input_source {
DISP_SRC_NONE = 0,
DISP_SRC_OVLY2,
DISP_SRC_DISP,
DISP_SRC_ROT,
DISP_SRC_SCL2
+};
+/*
- ADE media bus service relevant enums
- */
+enum qos_generator_mode {
FIXED_MODE = 0,
LIMITER_MODE,
BYPASS_MODE,
REGULATOR_MODE
+};
+/*
- Register Write/Read Helper functions
- */
+static inline void ade_update_bits(void __iomem *addr, u32 bit_start,
u32 mask, u32 val)
+{
u32 tmp, orig;
orig = readl(addr);
tmp = orig & ~(mask << bit_start);
tmp |= (val & mask) << bit_start;
writel(tmp, addr);
+}
+#endif diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c new file mode 100644 index 000000000000..bb93616dcf3d --- /dev/null +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c @@ -0,0 +1,452 @@ +/*
- Hisilicon Hi6220 SoC ADE(Advanced Display Engine)'s crtc&plane driver
- Copyright (c) 2016 Linaro Limited.
- Copyright (c) 2014-2016 Hisilicon Limited.
- Author:
Xinliang Liu <z.liuxinliang@hisilicon.com>
Xinliang Liu <xinliang.liu@linaro.org>
Xinwei Kong <kong.kongxinwei@hisilicon.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 as
- published by the Free Software Foundation.
- */
+#include <linux/bitops.h> +#include <linux/clk.h> +#include <video/display_timing.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> +#include <linux/reset.h>
+#include <drm/drmP.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h>
+#include "kirin_drm_drv.h" +#include "kirin_ade_reg.h"
+#define to_ade_crtc(crtc) \
container_of(crtc, struct ade_crtc, base)
+struct ade_hw_ctx {
void __iomem *base;
struct regmap *noc_regmap;
struct clk *ade_core_clk;
struct clk *media_noc_clk;
struct clk *ade_pix_clk;
struct reset_control *reset;
bool power_on;
int irq;
+};
+struct ade_crtc {
struct drm_crtc base;
struct ade_hw_ctx *ctx;
bool enable;
u32 out_format;
+};
+struct ade_data {
struct ade_crtc acrtc;
struct ade_hw_ctx ctx;
+};
+static void ade_update_reload_bit(void __iomem *base, u32 bit_num, u32 val) +{
u32 bit_ofst, reg_num;
bit_ofst = bit_num % 32;
reg_num = bit_num / 32;
ade_update_bits(base + ADE_RELOAD_DIS(reg_num), bit_ofst,
MASK(1), !!val);
+}
+static u32 ade_read_reload_bit(void __iomem *base, u32 bit_num) +{
u32 tmp, bit_ofst, reg_num;
bit_ofst = bit_num % 32;
reg_num = bit_num / 32;
tmp = readl(base + ADE_RELOAD_DIS(reg_num));
return !!(BIT(bit_ofst) & tmp);
+}
+static void ade_init(struct ade_hw_ctx *ctx) +{
void __iomem *base = ctx->base;
/* enable clk gate */
ade_update_bits(base + ADE_CTRL1, AUTO_CLK_GATE_EN_OFST,
AUTO_CLK_GATE_EN, ADE_ENABLE);
/* clear overlay */
writel(0, base + ADE_OVLY1_TRANS_CFG);
writel(0, base + ADE_OVLY_CTL);
writel(0, base + ADE_OVLYX_CTL(ADE_OVLY2));
/* clear reset and reload regs */
writel(MASK(32), base + ADE_SOFT_RST_SEL(0));
writel(MASK(32), base + ADE_SOFT_RST_SEL(1));
writel(MASK(32), base + ADE_RELOAD_DIS(0));
writel(MASK(32), base + ADE_RELOAD_DIS(1));
/* for video mode, that ade registers
* became effective at frame end
s/became/become
Consider re-writing this comment
Agree, will fix in next version.
*/
ade_update_bits(base + ADE_CTRL, FRM_END_START_OFST,
FRM_END_START_MASK,
REG_EFFECTIVE_IN_ADEEN_FRMEND); +}
+static void ade_ldi_set_mode(struct ade_crtc *acrtc,
struct drm_display_mode *mode,
struct drm_display_mode *adj_mode)
+{
struct ade_hw_ctx *ctx = acrtc->ctx;
void __iomem *base = ctx->base;
u32 width = mode->hdisplay;
u32 height = mode->vdisplay;
u32 hfp, hbp, hsw, vfp, vbp, vsw;
u32 plr_flags;
int ret;
plr_flags = (mode->flags & DRM_MODE_FLAG_NVSYNC) ? FLAG_NVSYNC :
0;
plr_flags |= (mode->flags & DRM_MODE_FLAG_NHSYNC) ? FLAG_NHSYNC :
0;
hfp = mode->hsync_start - mode->hdisplay;
hbp = mode->htotal - mode->hsync_end;
hsw = mode->hsync_end - mode->hsync_start;
vfp = mode->vsync_start - mode->vdisplay;
vbp = mode->vtotal - mode->vsync_end;
vsw = mode->vsync_end - mode->vsync_start;
if (vsw > 15) {
DRM_DEBUG_DRIVER("vsw exceeded 15\n");
vsw = 15;
}
writel((hbp << HBP_OFST) | hfp, base + LDI_HRZ_CTRL0);
/* the configured value is actual value - 1 */
writel(hsw - 1, base + LDI_HRZ_CTRL1);
writel((vbp << VBP_OFST) | vfp, base + LDI_VRT_CTRL0);
/* the configured value is actual value - 1 */
writel(vsw - 1, base + LDI_VRT_CTRL1);
/* the configured value is actual value - 1 */
writel(((height - 1) << VSIZE_OFST) | (width - 1),
base + LDI_DSP_SIZE);
writel(plr_flags, base + LDI_PLR_CTRL);
/* Success should be guaranteed in mode_valid call back,
* so failer shouldn't happen here
s/failer/failure
Also, please use the standard way of writing multiple lines of comments here and elsewhere in the driver.
The standard way should be:
/* * Some comments. */
right?
*/
ret = clk_set_rate(ctx->ade_pix_clk, mode->clock * 1000);
if (ret)
DRM_ERROR("failed to set pixel clk %dHz (%d)\n",
mode->clock * 1000, ret);
adj_mode->clock = clk_get_rate(ctx->ade_pix_clk) / 1000;
/* ctran6 setting */
writel(CTRAN_BYPASS_ON, base + ADE_CTRAN_DIS(ADE_CTRAN6));
/* the configured value is actual value - 1 */
writel(width * height - 1, base +
ADE_CTRAN_IMAGE_SIZE(ADE_CTRAN6));
ade_update_reload_bit(base, CTRAN_OFST + ADE_CTRAN6, 0);
DRM_DEBUG_DRIVER("set mode: %dx%d\n", width, height);
+}
+static int ade_power_up(struct ade_hw_ctx *ctx) +{
int ret;
ret = clk_prepare_enable(ctx->media_noc_clk);
if (ret) {
DRM_ERROR("failed to enable media_noc_clk (%d)\n", ret);
return ret;
}
ret = reset_control_deassert(ctx->reset);
if (ret) {
DRM_ERROR("failed to deassert reset\n");
return ret;
}
ret = clk_prepare_enable(ctx->ade_core_clk);
if (ret) {
DRM_ERROR("failed to enable ade_core_clk (%d)\n", ret);
return ret;
}
ade_init(ctx);
ctx->power_on = true;
return 0;
+}
+static void ade_power_down(struct ade_hw_ctx *ctx) +{
void __iomem *base = ctx->base;
writel(ADE_DISABLE, base + LDI_CTRL);
/* dsi pixel off */
writel(DSI_PCLK_OFF, base + LDI_HDMI_DSI_GT);
clk_disable_unprepare(ctx->ade_core_clk);
reset_control_assert(ctx->reset);
clk_disable_unprepare(ctx->media_noc_clk);
ctx->power_on = false;
+}
+static void ade_set_medianoc_qos(struct ade_crtc *acrtc) +{
struct ade_hw_ctx *ctx = acrtc->ctx;
struct regmap *map = ctx->noc_regmap;
regmap_update_bits(map, ADE0_QOSGENERATOR_MODE,
QOSGENERATOR_MODE_MASK, BYPASS_MODE);
regmap_update_bits(map, ADE0_QOSGENERATOR_EXTCONTROL,
SOCKET_QOS_EN, SOCKET_QOS_EN);
regmap_update_bits(map, ADE1_QOSGENERATOR_MODE,
QOSGENERATOR_MODE_MASK, BYPASS_MODE);
regmap_update_bits(map, ADE1_QOSGENERATOR_EXTCONTROL,
SOCKET_QOS_EN, SOCKET_QOS_EN);
+}
+static void ade_display_enable(struct ade_crtc *acrtc) +{
struct ade_hw_ctx *ctx = acrtc->ctx;
void __iomem *base = ctx->base;
u32 out_fmt = acrtc->out_format;
/* display source setting */
writel(DISP_SRC_OVLY2, base + ADE_DISP_SRC_CFG);
/* enable ade */
writel(ADE_ENABLE, base + ADE_EN);
/* enable ldi */
writel(NORMAL_MODE, base + LDI_WORK_MODE);
writel((out_fmt << BPP_OFST) | DATA_GATE_EN | LDI_EN,
base + LDI_CTRL);
/* dsi pixel on */
writel(DSI_PCLK_ON, base + LDI_HDMI_DSI_GT);
+}
+static void ade_crtc_enable(struct drm_crtc *crtc) +{
struct ade_crtc *acrtc = to_ade_crtc(crtc);
struct ade_hw_ctx *ctx = acrtc->ctx;
int ret;
if (acrtc->enable)
return;
if (!ctx->power_on) {
ret = ade_power_up(ctx);
if (ret)
return;
}
ade_set_medianoc_qos(acrtc);
ade_display_enable(acrtc);
acrtc->enable = true;
+}
+static void ade_crtc_disable(struct drm_crtc *crtc) +{
struct ade_crtc *acrtc = to_ade_crtc(crtc);
struct ade_hw_ctx *ctx = acrtc->ctx;
if (!acrtc->enable)
return;
ade_power_down(ctx);
acrtc->enable = false;
+}
+static int ade_crtc_atomic_check(struct drm_crtc *crtc,
struct drm_crtc_state *state)
+{
/* do nothing */
return 0;
+}
+static void ade_crtc_mode_set_nofb(struct drm_crtc *crtc) +{
struct ade_crtc *acrtc = to_ade_crtc(crtc);
struct ade_hw_ctx *ctx = acrtc->ctx;
struct drm_display_mode *mode = &crtc->state->mode;
struct drm_display_mode *adj_mode = &crtc->state->adjusted_mode;
if (!ctx->power_on)
(void)ade_power_up(ctx);
ade_ldi_set_mode(acrtc, mode, adj_mode);
+}
+static void ade_crtc_atomic_begin(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
+{
struct ade_crtc *acrtc = to_ade_crtc(crtc);
struct ade_hw_ctx *ctx = acrtc->ctx;
if (!ctx->power_on)
(void)ade_power_up(ctx);
+}
+static void ade_crtc_atomic_flush(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
+{
struct ade_crtc *acrtc = to_ade_crtc(crtc);
struct ade_hw_ctx *ctx = acrtc->ctx;
void __iomem *base = ctx->base;
/* only crtc is eanbled regs take effect */
s/eanbled/enabled
will fix in next version.
if (acrtc->enable) {
/* flush ade regitsters */
s/regitsters/registers
Other than the minor comments:
Reviewed-by: Archit Taneja architt@codeaurora.org
Thanks -:)
writel(ADE_ENABLE, base + ADE_EN);
}
+}
+static const struct drm_crtc_helper_funcs ade_crtc_helper_funcs = {
.enable = ade_crtc_enable,
.disable = ade_crtc_disable,
.atomic_check = ade_crtc_atomic_check,
.mode_set_nofb = ade_crtc_mode_set_nofb,
.atomic_begin = ade_crtc_atomic_begin,
.atomic_flush = ade_crtc_atomic_flush,
+};
+static const struct drm_crtc_funcs ade_crtc_funcs = {
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
.reset = drm_atomic_helper_crtc_reset,
.set_property = drm_atomic_helper_crtc_set_property,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+static int ade_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
struct drm_plane *plane)
+{
struct kirin_drm_private *priv = dev->dev_private;
struct device_node *port;
int ret;
/* set crtc port so that
* drm_of_find_possible_crtcs call works
*/
port = of_get_child_by_name(dev->dev->of_node, "port");
if (!port) {
DRM_ERROR("no port node found in %s\n",
dev->dev->of_node->full_name);
return -EINVAL;
}
of_node_put(port);
crtc->port = port;
ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
&ade_crtc_funcs, NULL);
if (ret) {
DRM_ERROR("failed to init crtc.\n");
return ret;
}
drm_crtc_helper_add(crtc, &ade_crtc_helper_funcs);
priv->crtc[drm_crtc_index(crtc)] = crtc;
return 0;
+}
+static int ade_dts_parse(struct platform_device *pdev, struct ade_hw_ctx *ctx) +{
struct resource *res;
struct device *dev = &pdev->dev;
struct device_node *np = pdev->dev.of_node;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ctx->base = devm_ioremap_resource(dev, res);
if (IS_ERR(ctx->base)) {
DRM_ERROR("failed to remap ade io base\n");
return PTR_ERR(ctx->base);
}
ctx->reset = devm_reset_control_get(dev, NULL);
if (IS_ERR(ctx->reset))
return PTR_ERR(ctx->reset);
ctx->noc_regmap =
syscon_regmap_lookup_by_phandle(np,
"hisilicon,noc-syscon");
if (IS_ERR(ctx->noc_regmap)) {
DRM_ERROR("failed to get noc regmap\n");
return PTR_ERR(ctx->noc_regmap);
}
ctx->irq = platform_get_irq(pdev, 0);
if (ctx->irq < 0) {
DRM_ERROR("failed to get irq\n");
return -ENODEV;
}
ctx->ade_core_clk = devm_clk_get(dev, "clk_ade_core");
if (!ctx->ade_core_clk) {
DRM_ERROR("failed to parse clk ADE_CORE\n");
return -ENODEV;
}
ctx->media_noc_clk = devm_clk_get(dev, "clk_codec_jpeg");
if (!ctx->media_noc_clk) {
DRM_ERROR("failed to parse clk CODEC_JPEG\n");
return -ENODEV;
}
ctx->ade_pix_clk = devm_clk_get(dev, "clk_ade_pix");
if (!ctx->ade_pix_clk) {
DRM_ERROR("failed to parse clk ADE_PIX\n");
return -ENODEV;
}
return 0;
+}
+int ade_drm_init(struct drm_device *dev) +{
struct platform_device *pdev = dev->platformdev;
struct ade_data *ade;
struct ade_hw_ctx *ctx;
struct ade_crtc *acrtc;
int ret;
ade = devm_kzalloc(dev->dev, sizeof(*ade), GFP_KERNEL);
if (!ade) {
DRM_ERROR("failed to alloc ade_data\n");
return -ENOMEM;
}
platform_set_drvdata(pdev, ade);
ctx = &ade->ctx;
acrtc = &ade->acrtc;
acrtc->ctx = ctx;
acrtc->out_format = LDI_OUT_RGB_888;
ret = ade_dts_parse(pdev, ctx);
if (ret)
return ret;
return 0;
+}
+void ade_drm_cleanup(struct drm_device *dev) +{
struct platform_device *pdev = dev->platformdev;
struct ade_data *ade = platform_get_drvdata(pdev);
struct drm_crtc *crtc = &ade->acrtc.base;
drm_crtc_cleanup(crtc);
+}
+const struct kirin_dc_ops ade_dc_ops = {
.init = ade_drm_init,
.cleanup = ade_drm_cleanup
+}; diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c index 789ebd1f5922..055729c1889c 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c @@ -30,8 +30,12 @@ static struct kirin_dc_ops *dc_ops;
static int kirin_drm_kms_cleanup(struct drm_device *dev) {
struct kirin_drm_private *priv = dev->dev_private;
dc_ops->cleanup(dev); drm_mode_config_cleanup(dev);
devm_kfree(dev->dev, priv);
dev->dev_private = NULL; return 0;
}
@@ -55,8 +59,14 @@ static void kirin_drm_mode_config_init(struct drm_device *dev)
static int kirin_drm_kms_init(struct drm_device *dev) {
struct kirin_drm_private *priv; int ret;
priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
dev->dev_private = priv; dev_set_drvdata(dev->dev, dev); /* dev->mode_config initialization */
@@ -84,6 +94,8 @@ err_dc_cleanup: dc_ops->cleanup(dev); err_mode_config_cleanup: drm_mode_config_cleanup(dev);
devm_kfree(dev->dev, priv);
dev->dev_private = NULL; return ret;
}
@@ -221,6 +233,9 @@ static const struct component_master_ops kirin_drm_ops = { };
static const struct of_device_id kirin_drm_dt_ids[] = {
{ .compatible = "hisilicon,hi6220-ade",
.data = &ade_dc_ops,
}; MODULE_DEVICE_TABLE(of, kirin_drm_dt_ids);}, { /* end node */ },
diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h index 75e9d82356d4..5a05ad6a81db 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h @@ -11,10 +11,18 @@ #ifndef __KIRIN_DRM_DRV_H__ #define __KIRIN_DRM_DRV_H__
+#define MAX_CRTC 2
- /* display controller init/cleanup ops */ struct kirin_dc_ops { int (*init)(struct drm_device *dev); void (*cleanup)(struct drm_device *dev); };
+struct kirin_drm_private {
struct drm_crtc *crtc[MAX_CRTC];
+};
+extern const struct kirin_dc_ops ade_dc_ops;
- #endif /* __KIRIN_DRM_DRV_H__ */
-- Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
Add plane funcs and helper funcs for ADE.
v6: None. v5: None. v4: None. v3: - A few cleanup. v2: - Remove abtraction layer.
Signed-off-by: Xinliang Liu xinliang.liu@linaro.org --- drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c | 535 +++++++++++++++++++++++- 1 file changed, 534 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c index bb93616dcf3d..aa2cf75cab39 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c @@ -27,13 +27,23 @@ #include <drm/drm_crtc_helper.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_plane_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_fb_cma_helper.h>
#include "kirin_drm_drv.h" #include "kirin_ade_reg.h"
+#define PRIMARY_CH ADE_CH1 /* primary plane */ +#define OUT_OVLY ADE_OVLY2 /* output overlay compositor */ +#define ADE_DEBUG 1 + #define to_ade_crtc(crtc) \ container_of(crtc, struct ade_crtc, base)
+#define to_ade_plane(plane) \ + container_of(plane, struct ade_plane, base) + struct ade_hw_ctx { void __iomem *base; struct regmap *noc_regmap; @@ -52,11 +62,76 @@ struct ade_crtc { u32 out_format; };
+struct ade_plane { + struct drm_plane base; + void *ctx; + u8 ch; /* channel */ +}; + struct ade_data { struct ade_crtc acrtc; + struct ade_plane aplane[ADE_CH_NUM]; struct ade_hw_ctx ctx; };
+/* ade-format info: */ +struct ade_format { + u32 pixel_format; + enum ade_fb_format ade_format; +}; + +static const struct ade_format ade_formats[] = { + /* 16bpp RGB: */ + { DRM_FORMAT_RGB565, ADE_RGB_565 }, + { DRM_FORMAT_BGR565, ADE_BGR_565 }, + /* 24bpp RGB: */ + { DRM_FORMAT_RGB888, ADE_RGB_888 }, + { DRM_FORMAT_BGR888, ADE_BGR_888 }, + /* 32bpp [A]RGB: */ + { DRM_FORMAT_XRGB8888, ADE_XRGB_8888 }, + { DRM_FORMAT_XBGR8888, ADE_XBGR_8888 }, + { DRM_FORMAT_RGBA8888, ADE_RGBA_8888 }, + { DRM_FORMAT_BGRA8888, ADE_BGRA_8888 }, + { DRM_FORMAT_ARGB8888, ADE_ARGB_8888 }, + { DRM_FORMAT_ABGR8888, ADE_ABGR_8888 }, +}; + +static const u32 channel_formats1[] = { + /* channel 1,2,3,4 */ + DRM_FORMAT_RGB565, DRM_FORMAT_BGR565, DRM_FORMAT_RGB888, + DRM_FORMAT_BGR888, DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888, + DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888, DRM_FORMAT_ARGB8888, + DRM_FORMAT_ABGR8888 +}; + +u32 ade_get_channel_formats(u8 ch, const u32 **formats) +{ + switch (ch) { + case ADE_CH1: + *formats = channel_formats1; + return ARRAY_SIZE(channel_formats1); + default: + DRM_ERROR("no this channel %d\n", ch); + *formats = NULL; + return 0; + } +} + +/* convert from fourcc format to ade format */ +static u32 ade_get_format(u32 pixel_format) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ade_formats); i++) + if (ade_formats[i].pixel_format == pixel_format) + return ade_formats[i].ade_format; + + /* not found */ + DRM_ERROR("Not found pixel format!!fourcc_format= %d\n", + pixel_format); + return ADE_FORMAT_NOT_SUPPORT; +} + static void ade_update_reload_bit(void __iomem *base, u32 bit_num, u32 val) { u32 bit_ofst, reg_num; @@ -89,7 +164,7 @@ static void ade_init(struct ade_hw_ctx *ctx) /* clear overlay */ writel(0, base + ADE_OVLY1_TRANS_CFG); writel(0, base + ADE_OVLY_CTL); - writel(0, base + ADE_OVLYX_CTL(ADE_OVLY2)); + writel(0, base + ADE_OVLYX_CTL(OUT_OVLY)); /* clear reset and reload regs */ writel(MASK(32), base + ADE_SOFT_RST_SEL(0)); writel(MASK(32), base + ADE_SOFT_RST_SEL(1)); @@ -147,6 +222,10 @@ static void ade_ldi_set_mode(struct ade_crtc *acrtc, mode->clock * 1000, ret); adj_mode->clock = clk_get_rate(ctx->ade_pix_clk) / 1000;
+ /* set overlay compositor output size */ + writel(((width - 1) << OUTPUT_XSIZE_OFST) | (height - 1), + base + ADE_OVLY_OUTPUT_SIZE(OUT_OVLY)); + /* ctran6 setting */ writel(CTRAN_BYPASS_ON, base + ADE_CTRAN_DIS(ADE_CTRAN6)); /* the configured value is actual value - 1 */ @@ -219,6 +298,10 @@ static void ade_display_enable(struct ade_crtc *acrtc) void __iomem *base = ctx->base; u32 out_fmt = acrtc->out_format;
+ /* enable output overlay compositor */ + writel(ADE_ENABLE, base + ADE_OVLYX_CTL(OUT_OVLY)); + ade_update_reload_bit(base, OVLY_OFST + OUT_OVLY, 0); + /* display source setting */ writel(DISP_SRC_OVLY2, base + ADE_DISP_SRC_CFG);
@@ -232,6 +315,97 @@ static void ade_display_enable(struct ade_crtc *acrtc) writel(DSI_PCLK_ON, base + LDI_HDMI_DSI_GT); }
+#if ADE_DEBUG +static void ade_rdma_dump_regs(void __iomem *base, u32 ch) +{ + u32 reg_ctrl, reg_addr, reg_size, reg_stride, reg_space, reg_en; + u32 val; + + reg_ctrl = RD_CH_CTRL(ch); + reg_addr = RD_CH_ADDR(ch); + reg_size = RD_CH_SIZE(ch); + reg_stride = RD_CH_STRIDE(ch); + reg_space = RD_CH_SPACE(ch); + reg_en = RD_CH_EN(ch); + + val = ade_read_reload_bit(base, RDMA_OFST + ch); + DRM_DEBUG_DRIVER("[rdma%d]: reload(%d)\n", ch + 1, val); + val = readl(base + reg_ctrl); + DRM_DEBUG_DRIVER("[rdma%d]: reg_ctrl(0x%08x)\n", ch + 1, val); + val = readl(base + reg_addr); + DRM_DEBUG_DRIVER("[rdma%d]: reg_addr(0x%08x)\n", ch + 1, val); + val = readl(base + reg_size); + DRM_DEBUG_DRIVER("[rdma%d]: reg_size(0x%08x)\n", ch + 1, val); + val = readl(base + reg_stride); + DRM_DEBUG_DRIVER("[rdma%d]: reg_stride(0x%08x)\n", ch + 1, val); + val = readl(base + reg_space); + DRM_DEBUG_DRIVER("[rdma%d]: reg_space(0x%08x)\n", ch + 1, val); + val = readl(base + reg_en); + DRM_DEBUG_DRIVER("[rdma%d]: reg_en(0x%08x)\n", ch + 1, val); +} + +static void ade_clip_dump_regs(void __iomem *base, u32 ch) +{ + u32 val; + + val = ade_read_reload_bit(base, CLIP_OFST + ch); + DRM_DEBUG_DRIVER("[clip%d]: reload(%d)\n", ch + 1, val); + val = readl(base + ADE_CLIP_DISABLE(ch)); + DRM_DEBUG_DRIVER("[clip%d]: reg_clip_disable(0x%08x)\n", ch + 1, val); + val = readl(base + ADE_CLIP_SIZE0(ch)); + DRM_DEBUG_DRIVER("[clip%d]: reg_clip_size0(0x%08x)\n", ch + 1, val); + val = readl(base + ADE_CLIP_SIZE1(ch)); + DRM_DEBUG_DRIVER("[clip%d]: reg_clip_size1(0x%08x)\n", ch + 1, val); +} + +static void ade_compositor_routing_dump_regs(void __iomem *base, u32 ch) +{ + u8 ovly_ch = 0; /* TODO: Only primary plane now */ + u32 val; + + val = readl(base + ADE_OVLY_CH_XY0(ovly_ch)); + DRM_DEBUG_DRIVER("[overlay ch%d]: reg_ch_xy0(0x%08x)\n", ovly_ch, val); + val = readl(base + ADE_OVLY_CH_XY1(ovly_ch)); + DRM_DEBUG_DRIVER("[overlay ch%d]: reg_ch_xy1(0x%08x)\n", ovly_ch, val); + val = readl(base + ADE_OVLY_CH_CTL(ovly_ch)); + DRM_DEBUG_DRIVER("[overlay ch%d]: reg_ch_ctl(0x%08x)\n", ovly_ch, val); +} + +static void ade_dump_overlay_compositor_regs(void __iomem *base, u32 comp) +{ + u32 val; + + val = ade_read_reload_bit(base, OVLY_OFST + comp); + DRM_DEBUG_DRIVER("[overlay%d]: reload(%d)\n", comp + 1, val); + writel(ADE_ENABLE, base + ADE_OVLYX_CTL(comp)); + DRM_DEBUG_DRIVER("[overlay%d]: reg_ctl(0x%08x)\n", comp + 1, val); + val = readl(base + ADE_OVLY_CTL); + DRM_DEBUG_DRIVER("ovly_ctl(0x%08x)\n", val); +} + +static void ade_dump_regs(void __iomem *base) +{ + u32 i; + + /* dump channel regs */ + for (i = 0; i < ADE_CH_NUM; i++) { + /* dump rdma regs */ + ade_rdma_dump_regs(base, i); + + /* dump clip regs */ + ade_clip_dump_regs(base, i); + + /* dump compositor routing regs */ + ade_compositor_routing_dump_regs(base, i); + } + + /* dump overlay compositor regs */ + ade_dump_overlay_compositor_regs(base, OUT_OVLY); +} +#else +static void ade_dump_regs(void __iomem *base) { } +#endif + static void ade_crtc_enable(struct drm_crtc *crtc) { struct ade_crtc *acrtc = to_ade_crtc(crtc); @@ -249,6 +423,7 @@ static void ade_crtc_enable(struct drm_crtc *crtc)
ade_set_medianoc_qos(acrtc); ade_display_enable(acrtc); + ade_dump_regs(ctx->base); acrtc->enable = true; }
@@ -303,6 +478,7 @@ static void ade_crtc_atomic_flush(struct drm_crtc *crtc,
/* only crtc is eanbled regs take effect */ if (acrtc->enable) { + ade_dump_regs(base); /* flush ade regitsters */ writel(ADE_ENABLE, base + ADE_EN); } @@ -359,6 +535,338 @@ static int ade_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, return 0; }
+static void ade_rdma_set(void __iomem *base, struct drm_framebuffer *fb, + u32 ch, u32 y, u32 in_h, u32 fmt) +{ + struct drm_gem_cma_object *obj = drm_fb_cma_get_gem_obj(fb, 0); + u32 reg_ctrl, reg_addr, reg_size, reg_stride, reg_space, reg_en; + u32 stride = fb->pitches[0]; + u32 addr = (u32)obj->paddr + y * stride; + + DRM_DEBUG_DRIVER("rdma%d: (y=%d, height=%d), stride=%d, paddr=0x%x\n", + ch + 1, y, in_h, stride, (u32)obj->paddr); + DRM_DEBUG_DRIVER("addr=0x%x, fb:%dx%d, pixel_format=%d(%s)\n", + addr, fb->width, fb->height, fmt, + drm_get_format_name(fb->pixel_format)); + + /* get reg offset */ + reg_ctrl = RD_CH_CTRL(ch); + reg_addr = RD_CH_ADDR(ch); + reg_size = RD_CH_SIZE(ch); + reg_stride = RD_CH_STRIDE(ch); + reg_space = RD_CH_SPACE(ch); + reg_en = RD_CH_EN(ch); + + /* + * TODO: set rotation + */ + writel((fmt << 16) & 0x1f0000, base + reg_ctrl); + writel(addr, base + reg_addr); + writel((in_h << 16) | stride, base + reg_size); + writel(stride, base + reg_stride); + writel(in_h * stride, base + reg_space); + writel(ADE_ENABLE, base + reg_en); + ade_update_reload_bit(base, RDMA_OFST + ch, 0); +} + +static void ade_rdma_disable(void __iomem *base, u32 ch) +{ + u32 reg_en; + + /* get reg offset */ + reg_en = RD_CH_EN(ch); + writel(0, base + reg_en); + ade_update_reload_bit(base, RDMA_OFST + ch, 1); +} + +static void ade_clip_set(void __iomem *base, u32 ch, u32 fb_w, u32 x, + u32 in_w, u32 in_h) +{ + u32 disable_val; + u32 clip_left; + u32 clip_right; + + /* + * clip width, no need to clip height + */ + if (fb_w == in_w) { /* bypass */ + disable_val = 1; + clip_left = 0; + clip_right = 0; + } else { + disable_val = 0; + clip_left = x; + clip_right = fb_w - (x + in_w) - 1; + } + + DRM_DEBUG_DRIVER("clip%d: clip_left=%d, clip_right=%d\n", + ch + 1, clip_left, clip_right); + + writel(disable_val, base + ADE_CLIP_DISABLE(ch)); + writel((fb_w - 1) << 16 | (in_h - 1), base + ADE_CLIP_SIZE0(ch)); + writel(clip_left << 16 | clip_right, base + ADE_CLIP_SIZE1(ch)); + ade_update_reload_bit(base, CLIP_OFST + ch, 0); +} + +static void ade_clip_disable(void __iomem *base, u32 ch) +{ + writel(1, base + ADE_CLIP_DISABLE(ch)); + ade_update_reload_bit(base, CLIP_OFST + ch, 1); +} + +static bool has_Alpha_channel(int format) +{ + switch (format) { + case ADE_ARGB_8888: + case ADE_ABGR_8888: + case ADE_RGBA_8888: + case ADE_BGRA_8888: + return true; + default: + return false; + } +} + +static void ade_get_blending_params(u32 fmt, u8 glb_alpha, u8 *alp_mode, + u8 *alp_sel, u8 *under_alp_sel) +{ + bool has_alpha = has_Alpha_channel(fmt); + + /* + * get alp_mode + */ + if (has_alpha && glb_alpha < 255) + *alp_mode = ADE_ALP_PIXEL_AND_GLB; + else if (has_alpha) + *alp_mode = ADE_ALP_PIXEL; + else + *alp_mode = ADE_ALP_GLOBAL; + + /* + * get alp sel + */ + *alp_sel = ADE_ALP_MUL_COEFF_3; /* 1 */ + *under_alp_sel = ADE_ALP_MUL_COEFF_2; /* 0 */ +} + +static void ade_compositor_routing_set(void __iomem *base, u8 ch, + u32 x0, u32 y0, + u32 in_w, u32 in_h, u32 fmt) +{ + u8 ovly_ch = 0; /* TODO: This is the zpos, only one plane now */ + u8 glb_alpha = 255; + u32 x1 = x0 + in_w - 1; + u32 y1 = y0 + in_h - 1; + u32 val; + u8 alp_sel; + u8 under_alp_sel; + u8 alp_mode; + + ade_get_blending_params(fmt, glb_alpha, &alp_mode, &alp_sel, + &under_alp_sel); + + /* overlay routing setting + */ + writel(x0 << 16 | y0, base + ADE_OVLY_CH_XY0(ovly_ch)); + writel(x1 << 16 | y1, base + ADE_OVLY_CH_XY1(ovly_ch)); + val = (ch + 1) << CH_SEL_OFST | BIT(CH_EN_OFST) | + alp_sel << CH_ALP_SEL_OFST | + under_alp_sel << CH_UNDER_ALP_SEL_OFST | + glb_alpha << CH_ALP_GBL_OFST | + alp_mode << CH_ALP_MODE_OFST; + writel(val, base + ADE_OVLY_CH_CTL(ovly_ch)); + /* connect this plane/channel to overlay2 compositor */ + ade_update_bits(base + ADE_OVLY_CTL, CH_OVLY_SEL_OFST(ovly_ch), + CH_OVLY_SEL_MASK, CH_OVLY_SEL_VAL(OUT_OVLY)); +} + +static void ade_compositor_routing_disable(void __iomem *base, u32 ch) +{ + u8 ovly_ch = 0; /* TODO: Only primary plane now */ + + /* disable this plane/channel */ + ade_update_bits(base + ADE_OVLY_CH_CTL(ovly_ch), CH_EN_OFST, + MASK(1), 0); + /* dis-connect this plane/channel of overlay2 compositor */ + ade_update_bits(base + ADE_OVLY_CTL, CH_OVLY_SEL_OFST(ovly_ch), + CH_OVLY_SEL_MASK, 0); +} + +/* + * Typicaly, a channel looks like: DMA-->clip-->scale-->ctrans-->compositor + */ +static void ade_update_channel(struct ade_plane *aplane, + struct drm_framebuffer *fb, int crtc_x, + int crtc_y, unsigned int crtc_w, + unsigned int crtc_h, u32 src_x, + u32 src_y, u32 src_w, u32 src_h) +{ + struct ade_hw_ctx *ctx = aplane->ctx; + void __iomem *base = ctx->base; + u32 fmt = ade_get_format(fb->pixel_format); + u32 ch = aplane->ch; + u32 in_w; + u32 in_h; + + DRM_DEBUG_DRIVER("channel%d: src:(%d, %d)-%dx%d, crtc:(%d, %d)-%dx%d", + ch + 1, src_x, src_y, src_w, src_h, + crtc_x, crtc_y, crtc_w, crtc_h); + + /* 1) DMA setting */ + in_w = src_w; + in_h = src_h; + ade_rdma_set(base, fb, ch, src_y, in_h, fmt); + + /* 2) clip setting */ + ade_clip_set(base, ch, fb->width, src_x, in_w, in_h); + + /* 3) TODO: scale setting for overlay planes */ + + /* 4) TODO: ctran/csc setting for overlay planes */ + + /* 5) compositor routing setting */ + ade_compositor_routing_set(base, ch, crtc_x, crtc_y, in_w, in_h, fmt); +} + +static void ade_disable_channel(struct ade_plane *aplane) +{ + struct ade_hw_ctx *ctx = aplane->ctx; + void __iomem *base = ctx->base; + u32 ch = aplane->ch; + + DRM_DEBUG_DRIVER("disable channel%d\n", ch + 1); + + /* disable read DMA */ + ade_rdma_disable(base, ch); + + /* disable clip */ + ade_clip_disable(base, ch); + + /* disable compositor routing */ + ade_compositor_routing_disable(base, ch); +} + +static int ade_plane_prepare_fb(struct drm_plane *plane, + const struct drm_plane_state *new_state) +{ + /* do nothing */ + return 0; +} + +static void ade_plane_cleanup_fb(struct drm_plane *plane, + const struct drm_plane_state *old_state) +{ + /* do nothing */ +} + +static int ade_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct drm_framebuffer *fb = state->fb; + struct drm_crtc *crtc = state->crtc; + struct drm_crtc_state *crtc_state; + u32 src_x = state->src_x >> 16; + u32 src_y = state->src_y >> 16; + u32 src_w = state->src_w >> 16; + u32 src_h = state->src_h >> 16; + int crtc_x = state->crtc_x; + int crtc_y = state->crtc_y; + u32 crtc_w = state->crtc_w; + u32 crtc_h = state->crtc_h; + u32 fmt; + + if (!crtc || !fb) + return 0; + + fmt = ade_get_format(fb->pixel_format); + if (fmt == ADE_FORMAT_NOT_SUPPORT) + return -EINVAL; + + crtc_state = drm_atomic_get_crtc_state(state->state, crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + if (src_w != crtc_w || src_h != crtc_h) { + DRM_ERROR("Scale not support!!!\n"); + return -EINVAL; + } + + if (src_x + src_w > fb->width || + src_y + src_h > fb->height) + return -EINVAL; + + if (crtc_x < 0 || crtc_y < 0) + return -EINVAL; + + if (crtc_x + crtc_w > crtc_state->adjusted_mode.hdisplay || + crtc_y + crtc_h > crtc_state->adjusted_mode.vdisplay) + return -EINVAL; + + return 0; +} + +static void ade_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct drm_plane_state *state = plane->state; + struct ade_plane *aplane = to_ade_plane(plane); + + ade_update_channel(aplane, state->fb, state->crtc_x, state->crtc_y, + state->crtc_w, state->crtc_h, + state->src_x >> 16, state->src_y >> 16, + state->src_w >> 16, state->src_h >> 16); +} + +static void ade_plane_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct ade_plane *aplane = to_ade_plane(plane); + + ade_disable_channel(aplane); +} + +static const struct drm_plane_helper_funcs ade_plane_helper_funcs = { + .prepare_fb = ade_plane_prepare_fb, + .cleanup_fb = ade_plane_cleanup_fb, + .atomic_check = ade_plane_atomic_check, + .atomic_update = ade_plane_atomic_update, + .atomic_disable = ade_plane_atomic_disable, +}; + +static struct drm_plane_funcs ade_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .set_property = drm_atomic_helper_plane_set_property, + .destroy = drm_plane_cleanup, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, +}; + +static int ade_plane_init(struct drm_device *dev, struct ade_plane *aplane, + enum drm_plane_type type) +{ + const u32 *fmts; + u32 fmts_cnt; + int ret = 0; + + /* get properties */ + fmts_cnt = ade_get_channel_formats(aplane->ch, &fmts); + if (ret) + return ret; + + ret = drm_universal_plane_init(dev, &aplane->base, 1, &ade_plane_funcs, + fmts, fmts_cnt, type, NULL); + if (ret) { + DRM_ERROR("fail to init plane, ch=%d\n", aplane->ch); + return ret; + } + + drm_plane_helper_add(&aplane->base, &ade_plane_helper_funcs); + + return 0; +} + static int ade_dts_parse(struct platform_device *pdev, struct ade_hw_ctx *ctx) { struct resource *res; @@ -416,7 +924,10 @@ int ade_drm_init(struct drm_device *dev) struct ade_data *ade; struct ade_hw_ctx *ctx; struct ade_crtc *acrtc; + struct ade_plane *aplane; + enum drm_plane_type type; int ret; + int i;
ade = devm_kzalloc(dev->dev, sizeof(*ade), GFP_KERNEL); if (!ade) { @@ -434,6 +945,28 @@ int ade_drm_init(struct drm_device *dev) if (ret) return ret;
+ /* + * plane init + * TODO: Now only support primary plane, overlay planes + * need to do. + */ + for (i = 0; i < ADE_CH_NUM; i++) { + aplane = &ade->aplane[i]; + aplane->ch = i; + aplane->ctx = ctx; + type = i == PRIMARY_CH ? DRM_PLANE_TYPE_PRIMARY : + DRM_PLANE_TYPE_OVERLAY; + + ret = ade_plane_init(dev, aplane, type); + if (ret) + return ret; + } + + /* crtc init */ + ret = ade_crtc_init(dev, &acrtc->base, &ade->aplane[PRIMARY_CH].base); + if (ret) + return ret; + return 0; }
On 2/26/2016 2:10 PM, Xinliang Liu wrote:
Add plane funcs and helper funcs for ADE.
v6: None. v5: None. v4: None. v3:
- A few cleanup.
v2:
- Remove abtraction layer.
Signed-off-by: Xinliang Liu xinliang.liu@linaro.org
drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c | 535 +++++++++++++++++++++++- 1 file changed, 534 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c index bb93616dcf3d..aa2cf75cab39 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c @@ -27,13 +27,23 @@ #include <drm/drm_crtc_helper.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_plane_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_fb_cma_helper.h>
#include "kirin_drm_drv.h" #include "kirin_ade_reg.h"
+#define PRIMARY_CH ADE_CH1 /* primary plane */ +#define OUT_OVLY ADE_OVLY2 /* output overlay compositor */
Could you briefly explain this overlay/channel mapping? It looks like it's something that is hard coded at the moment.
Do channels map to planes, and OVLs map to crtcs?
+#define ADE_DEBUG 1
- #define to_ade_crtc(crtc) \ container_of(crtc, struct ade_crtc, base)
+#define to_ade_plane(plane) \
- container_of(plane, struct ade_plane, base)
- struct ade_hw_ctx { void __iomem *base; struct regmap *noc_regmap;
@@ -52,11 +62,76 @@ struct ade_crtc { u32 out_format; };
+struct ade_plane {
- struct drm_plane base;
- void *ctx;
- u8 ch; /* channel */
+};
- struct ade_data { struct ade_crtc acrtc;
- struct ade_plane aplane[ADE_CH_NUM]; struct ade_hw_ctx ctx; };
+/* ade-format info: */ +struct ade_format {
- u32 pixel_format;
- enum ade_fb_format ade_format;
+};
+static const struct ade_format ade_formats[] = {
- /* 16bpp RGB: */
- { DRM_FORMAT_RGB565, ADE_RGB_565 },
- { DRM_FORMAT_BGR565, ADE_BGR_565 },
- /* 24bpp RGB: */
- { DRM_FORMAT_RGB888, ADE_RGB_888 },
- { DRM_FORMAT_BGR888, ADE_BGR_888 },
- /* 32bpp [A]RGB: */
- { DRM_FORMAT_XRGB8888, ADE_XRGB_8888 },
- { DRM_FORMAT_XBGR8888, ADE_XBGR_8888 },
- { DRM_FORMAT_RGBA8888, ADE_RGBA_8888 },
- { DRM_FORMAT_BGRA8888, ADE_BGRA_8888 },
- { DRM_FORMAT_ARGB8888, ADE_ARGB_8888 },
- { DRM_FORMAT_ABGR8888, ADE_ABGR_8888 },
+};
+static const u32 channel_formats1[] = {
- /* channel 1,2,3,4 */
- DRM_FORMAT_RGB565, DRM_FORMAT_BGR565, DRM_FORMAT_RGB888,
- DRM_FORMAT_BGR888, DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888,
- DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888, DRM_FORMAT_ARGB8888,
- DRM_FORMAT_ABGR8888
+};
+u32 ade_get_channel_formats(u8 ch, const u32 **formats) +{
- switch (ch) {
- case ADE_CH1:
*formats = channel_formats1;
return ARRAY_SIZE(channel_formats1);
- default:
DRM_ERROR("no this channel %d\n", ch);
*formats = NULL;
return 0;
- }
+}
+/* convert from fourcc format to ade format */ +static u32 ade_get_format(u32 pixel_format) +{
- int i;
- for (i = 0; i < ARRAY_SIZE(ade_formats); i++)
if (ade_formats[i].pixel_format == pixel_format)
return ade_formats[i].ade_format;
- /* not found */
- DRM_ERROR("Not found pixel format!!fourcc_format= %d\n",
pixel_format);
- return ADE_FORMAT_NOT_SUPPORT;
+}
- static void ade_update_reload_bit(void __iomem *base, u32 bit_num, u32 val) { u32 bit_ofst, reg_num;
@@ -89,7 +164,7 @@ static void ade_init(struct ade_hw_ctx *ctx) /* clear overlay */ writel(0, base + ADE_OVLY1_TRANS_CFG); writel(0, base + ADE_OVLY_CTL);
- writel(0, base + ADE_OVLYX_CTL(ADE_OVLY2));
- writel(0, base + ADE_OVLYX_CTL(OUT_OVLY)); /* clear reset and reload regs */ writel(MASK(32), base + ADE_SOFT_RST_SEL(0)); writel(MASK(32), base + ADE_SOFT_RST_SEL(1));
@@ -147,6 +222,10 @@ static void ade_ldi_set_mode(struct ade_crtc *acrtc, mode->clock * 1000, ret); adj_mode->clock = clk_get_rate(ctx->ade_pix_clk) / 1000;
- /* set overlay compositor output size */
- writel(((width - 1) << OUTPUT_XSIZE_OFST) | (height - 1),
base + ADE_OVLY_OUTPUT_SIZE(OUT_OVLY));
- /* ctran6 setting */ writel(CTRAN_BYPASS_ON, base + ADE_CTRAN_DIS(ADE_CTRAN6)); /* the configured value is actual value - 1 */
@@ -219,6 +298,10 @@ static void ade_display_enable(struct ade_crtc *acrtc) void __iomem *base = ctx->base; u32 out_fmt = acrtc->out_format;
- /* enable output overlay compositor */
- writel(ADE_ENABLE, base + ADE_OVLYX_CTL(OUT_OVLY));
- ade_update_reload_bit(base, OVLY_OFST + OUT_OVLY, 0);
- /* display source setting */ writel(DISP_SRC_OVLY2, base + ADE_DISP_SRC_CFG);
@@ -232,6 +315,97 @@ static void ade_display_enable(struct ade_crtc *acrtc) writel(DSI_PCLK_ON, base + LDI_HDMI_DSI_GT); }
+#if ADE_DEBUG +static void ade_rdma_dump_regs(void __iomem *base, u32 ch) +{
- u32 reg_ctrl, reg_addr, reg_size, reg_stride, reg_space, reg_en;
- u32 val;
- reg_ctrl = RD_CH_CTRL(ch);
- reg_addr = RD_CH_ADDR(ch);
- reg_size = RD_CH_SIZE(ch);
- reg_stride = RD_CH_STRIDE(ch);
- reg_space = RD_CH_SPACE(ch);
- reg_en = RD_CH_EN(ch);
- val = ade_read_reload_bit(base, RDMA_OFST + ch);
- DRM_DEBUG_DRIVER("[rdma%d]: reload(%d)\n", ch + 1, val);
- val = readl(base + reg_ctrl);
- DRM_DEBUG_DRIVER("[rdma%d]: reg_ctrl(0x%08x)\n", ch + 1, val);
- val = readl(base + reg_addr);
- DRM_DEBUG_DRIVER("[rdma%d]: reg_addr(0x%08x)\n", ch + 1, val);
- val = readl(base + reg_size);
- DRM_DEBUG_DRIVER("[rdma%d]: reg_size(0x%08x)\n", ch + 1, val);
- val = readl(base + reg_stride);
- DRM_DEBUG_DRIVER("[rdma%d]: reg_stride(0x%08x)\n", ch + 1, val);
- val = readl(base + reg_space);
- DRM_DEBUG_DRIVER("[rdma%d]: reg_space(0x%08x)\n", ch + 1, val);
- val = readl(base + reg_en);
- DRM_DEBUG_DRIVER("[rdma%d]: reg_en(0x%08x)\n", ch + 1, val);
+}
+static void ade_clip_dump_regs(void __iomem *base, u32 ch) +{
- u32 val;
- val = ade_read_reload_bit(base, CLIP_OFST + ch);
- DRM_DEBUG_DRIVER("[clip%d]: reload(%d)\n", ch + 1, val);
- val = readl(base + ADE_CLIP_DISABLE(ch));
- DRM_DEBUG_DRIVER("[clip%d]: reg_clip_disable(0x%08x)\n", ch + 1, val);
- val = readl(base + ADE_CLIP_SIZE0(ch));
- DRM_DEBUG_DRIVER("[clip%d]: reg_clip_size0(0x%08x)\n", ch + 1, val);
- val = readl(base + ADE_CLIP_SIZE1(ch));
- DRM_DEBUG_DRIVER("[clip%d]: reg_clip_size1(0x%08x)\n", ch + 1, val);
+}
+static void ade_compositor_routing_dump_regs(void __iomem *base, u32 ch) +{
- u8 ovly_ch = 0; /* TODO: Only primary plane now */
- u32 val;
- val = readl(base + ADE_OVLY_CH_XY0(ovly_ch));
- DRM_DEBUG_DRIVER("[overlay ch%d]: reg_ch_xy0(0x%08x)\n", ovly_ch, val);
- val = readl(base + ADE_OVLY_CH_XY1(ovly_ch));
- DRM_DEBUG_DRIVER("[overlay ch%d]: reg_ch_xy1(0x%08x)\n", ovly_ch, val);
- val = readl(base + ADE_OVLY_CH_CTL(ovly_ch));
- DRM_DEBUG_DRIVER("[overlay ch%d]: reg_ch_ctl(0x%08x)\n", ovly_ch, val);
+}
+static void ade_dump_overlay_compositor_regs(void __iomem *base, u32 comp) +{
- u32 val;
- val = ade_read_reload_bit(base, OVLY_OFST + comp);
- DRM_DEBUG_DRIVER("[overlay%d]: reload(%d)\n", comp + 1, val);
- writel(ADE_ENABLE, base + ADE_OVLYX_CTL(comp));
- DRM_DEBUG_DRIVER("[overlay%d]: reg_ctl(0x%08x)\n", comp + 1, val);
- val = readl(base + ADE_OVLY_CTL);
- DRM_DEBUG_DRIVER("ovly_ctl(0x%08x)\n", val);
+}
+static void ade_dump_regs(void __iomem *base) +{
- u32 i;
- /* dump channel regs */
- for (i = 0; i < ADE_CH_NUM; i++) {
/* dump rdma regs */
ade_rdma_dump_regs(base, i);
/* dump clip regs */
ade_clip_dump_regs(base, i);
/* dump compositor routing regs */
ade_compositor_routing_dump_regs(base, i);
- }
- /* dump overlay compositor regs */
- ade_dump_overlay_compositor_regs(base, OUT_OVLY);
+} +#else +static void ade_dump_regs(void __iomem *base) { } +#endif
- static void ade_crtc_enable(struct drm_crtc *crtc) { struct ade_crtc *acrtc = to_ade_crtc(crtc);
@@ -249,6 +423,7 @@ static void ade_crtc_enable(struct drm_crtc *crtc)
ade_set_medianoc_qos(acrtc); ade_display_enable(acrtc);
- ade_dump_regs(ctx->base); acrtc->enable = true; }
@@ -303,6 +478,7 @@ static void ade_crtc_atomic_flush(struct drm_crtc *crtc,
/* only crtc is eanbled regs take effect */ if (acrtc->enable) {
/* flush ade regitsters */ writel(ADE_ENABLE, base + ADE_EN); }ade_dump_regs(base);
@@ -359,6 +535,338 @@ static int ade_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, return 0; }
+static void ade_rdma_set(void __iomem *base, struct drm_framebuffer *fb,
u32 ch, u32 y, u32 in_h, u32 fmt)
+{
- struct drm_gem_cma_object *obj = drm_fb_cma_get_gem_obj(fb, 0);
- u32 reg_ctrl, reg_addr, reg_size, reg_stride, reg_space, reg_en;
- u32 stride = fb->pitches[0];
- u32 addr = (u32)obj->paddr + y * stride;
- DRM_DEBUG_DRIVER("rdma%d: (y=%d, height=%d), stride=%d, paddr=0x%x\n",
ch + 1, y, in_h, stride, (u32)obj->paddr);
- DRM_DEBUG_DRIVER("addr=0x%x, fb:%dx%d, pixel_format=%d(%s)\n",
addr, fb->width, fb->height, fmt,
drm_get_format_name(fb->pixel_format));
- /* get reg offset */
- reg_ctrl = RD_CH_CTRL(ch);
- reg_addr = RD_CH_ADDR(ch);
- reg_size = RD_CH_SIZE(ch);
- reg_stride = RD_CH_STRIDE(ch);
- reg_space = RD_CH_SPACE(ch);
- reg_en = RD_CH_EN(ch);
- /*
* TODO: set rotation
*/
- writel((fmt << 16) & 0x1f0000, base + reg_ctrl);
- writel(addr, base + reg_addr);
- writel((in_h << 16) | stride, base + reg_size);
- writel(stride, base + reg_stride);
- writel(in_h * stride, base + reg_space);
- writel(ADE_ENABLE, base + reg_en);
- ade_update_reload_bit(base, RDMA_OFST + ch, 0);
+}
+static void ade_rdma_disable(void __iomem *base, u32 ch) +{
- u32 reg_en;
- /* get reg offset */
- reg_en = RD_CH_EN(ch);
- writel(0, base + reg_en);
- ade_update_reload_bit(base, RDMA_OFST + ch, 1);
+}
+static void ade_clip_set(void __iomem *base, u32 ch, u32 fb_w, u32 x,
u32 in_w, u32 in_h)
+{
- u32 disable_val;
- u32 clip_left;
- u32 clip_right;
- /*
* clip width, no need to clip height
*/
- if (fb_w == in_w) { /* bypass */
disable_val = 1;
clip_left = 0;
clip_right = 0;
- } else {
disable_val = 0;
clip_left = x;
clip_right = fb_w - (x + in_w) - 1;
- }
- DRM_DEBUG_DRIVER("clip%d: clip_left=%d, clip_right=%d\n",
ch + 1, clip_left, clip_right);
- writel(disable_val, base + ADE_CLIP_DISABLE(ch));
- writel((fb_w - 1) << 16 | (in_h - 1), base + ADE_CLIP_SIZE0(ch));
- writel(clip_left << 16 | clip_right, base + ADE_CLIP_SIZE1(ch));
- ade_update_reload_bit(base, CLIP_OFST + ch, 0);
+}
+static void ade_clip_disable(void __iomem *base, u32 ch) +{
- writel(1, base + ADE_CLIP_DISABLE(ch));
- ade_update_reload_bit(base, CLIP_OFST + ch, 1);
+}
+static bool has_Alpha_channel(int format) +{
- switch (format) {
- case ADE_ARGB_8888:
- case ADE_ABGR_8888:
- case ADE_RGBA_8888:
- case ADE_BGRA_8888:
return true;
- default:
return false;
- }
+}
+static void ade_get_blending_params(u32 fmt, u8 glb_alpha, u8 *alp_mode,
u8 *alp_sel, u8 *under_alp_sel)
+{
- bool has_alpha = has_Alpha_channel(fmt);
- /*
* get alp_mode
*/
- if (has_alpha && glb_alpha < 255)
*alp_mode = ADE_ALP_PIXEL_AND_GLB;
- else if (has_alpha)
*alp_mode = ADE_ALP_PIXEL;
- else
*alp_mode = ADE_ALP_GLOBAL;
- /*
* get alp sel
*/
- *alp_sel = ADE_ALP_MUL_COEFF_3; /* 1 */
- *under_alp_sel = ADE_ALP_MUL_COEFF_2; /* 0 */
+}
+static void ade_compositor_routing_set(void __iomem *base, u8 ch,
u32 x0, u32 y0,
u32 in_w, u32 in_h, u32 fmt)
+{
- u8 ovly_ch = 0; /* TODO: This is the zpos, only one plane now */
Does the ovly_ch map to the crtc here? If so, maybe instead of hard coding it here, you could extract the channel number via plane->crtc?
Other than the hard coding, it looks fine to me.
Thanks, Archit
Hi,
On 1 March 2016 at 02:48, Archit Taneja architt@codeaurora.org wrote:
On 2/26/2016 2:10 PM, Xinliang Liu wrote:
Add plane funcs and helper funcs for ADE.
v6: None. v5: None. v4: None. v3:
- A few cleanup.
v2:
- Remove abtraction layer.
Signed-off-by: Xinliang Liu xinliang.liu@linaro.org
drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c | 535 +++++++++++++++++++++++- 1 file changed, 534 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c index bb93616dcf3d..aa2cf75cab39 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c @@ -27,13 +27,23 @@ #include <drm/drm_crtc_helper.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_plane_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_fb_cma_helper.h>
#include "kirin_drm_drv.h" #include "kirin_ade_reg.h"
+#define PRIMARY_CH ADE_CH1 /* primary plane */ +#define OUT_OVLY ADE_OVLY2 /* output overlay compositor */
Could you briefly explain this overlay/channel mapping? It looks like it's something that is hard coded at the moment.
Do channels map to planes, and OVLs map to crtcs?
Yes, channels map to planes. But not sure if OVLs is map to crtcs. IMO, crtc mean an individual display pipe line.
Here, OVLs are the overlay compositor. There three OVLs in this chip, we choose OVLY2 for online display now. Also see the Hardware Detail part of cover patch.
+#define ADE_DEBUG 1
- #define to_ade_crtc(crtc) \ container_of(crtc, struct ade_crtc, base)
+#define to_ade_plane(plane) \
container_of(plane, struct ade_plane, base)
- struct ade_hw_ctx { void __iomem *base; struct regmap *noc_regmap;
@@ -52,11 +62,76 @@ struct ade_crtc { u32 out_format; };
+struct ade_plane {
struct drm_plane base;
void *ctx;
u8 ch; /* channel */
+};
- struct ade_data { struct ade_crtc acrtc;
};struct ade_plane aplane[ADE_CH_NUM]; struct ade_hw_ctx ctx;
+/* ade-format info: */ +struct ade_format {
u32 pixel_format;
enum ade_fb_format ade_format;
+};
+static const struct ade_format ade_formats[] = {
/* 16bpp RGB: */
{ DRM_FORMAT_RGB565, ADE_RGB_565 },
{ DRM_FORMAT_BGR565, ADE_BGR_565 },
/* 24bpp RGB: */
{ DRM_FORMAT_RGB888, ADE_RGB_888 },
{ DRM_FORMAT_BGR888, ADE_BGR_888 },
/* 32bpp [A]RGB: */
{ DRM_FORMAT_XRGB8888, ADE_XRGB_8888 },
{ DRM_FORMAT_XBGR8888, ADE_XBGR_8888 },
{ DRM_FORMAT_RGBA8888, ADE_RGBA_8888 },
{ DRM_FORMAT_BGRA8888, ADE_BGRA_8888 },
{ DRM_FORMAT_ARGB8888, ADE_ARGB_8888 },
{ DRM_FORMAT_ABGR8888, ADE_ABGR_8888 },
+};
+static const u32 channel_formats1[] = {
/* channel 1,2,3,4 */
DRM_FORMAT_RGB565, DRM_FORMAT_BGR565, DRM_FORMAT_RGB888,
DRM_FORMAT_BGR888, DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888,
DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888, DRM_FORMAT_ARGB8888,
DRM_FORMAT_ABGR8888
+};
+u32 ade_get_channel_formats(u8 ch, const u32 **formats) +{
switch (ch) {
case ADE_CH1:
*formats = channel_formats1;
return ARRAY_SIZE(channel_formats1);
default:
DRM_ERROR("no this channel %d\n", ch);
*formats = NULL;
return 0;
}
+}
+/* convert from fourcc format to ade format */ +static u32 ade_get_format(u32 pixel_format) +{
int i;
for (i = 0; i < ARRAY_SIZE(ade_formats); i++)
if (ade_formats[i].pixel_format == pixel_format)
return ade_formats[i].ade_format;
/* not found */
DRM_ERROR("Not found pixel format!!fourcc_format= %d\n",
pixel_format);
return ADE_FORMAT_NOT_SUPPORT;
+}
- static void ade_update_reload_bit(void __iomem *base, u32 bit_num, u32
val) { u32 bit_ofst, reg_num; @@ -89,7 +164,7 @@ static void ade_init(struct ade_hw_ctx *ctx) /* clear overlay */ writel(0, base + ADE_OVLY1_TRANS_CFG); writel(0, base + ADE_OVLY_CTL);
writel(0, base + ADE_OVLYX_CTL(ADE_OVLY2));
writel(0, base + ADE_OVLYX_CTL(OUT_OVLY)); /* clear reset and reload regs */ writel(MASK(32), base + ADE_SOFT_RST_SEL(0)); writel(MASK(32), base + ADE_SOFT_RST_SEL(1));
@@ -147,6 +222,10 @@ static void ade_ldi_set_mode(struct ade_crtc *acrtc, mode->clock * 1000, ret); adj_mode->clock = clk_get_rate(ctx->ade_pix_clk) / 1000;
/* set overlay compositor output size */
writel(((width - 1) << OUTPUT_XSIZE_OFST) | (height - 1),
base + ADE_OVLY_OUTPUT_SIZE(OUT_OVLY));
/* ctran6 setting */ writel(CTRAN_BYPASS_ON, base + ADE_CTRAN_DIS(ADE_CTRAN6)); /* the configured value is actual value - 1 */
@@ -219,6 +298,10 @@ static void ade_display_enable(struct ade_crtc *acrtc) void __iomem *base = ctx->base; u32 out_fmt = acrtc->out_format;
/* enable output overlay compositor */
writel(ADE_ENABLE, base + ADE_OVLYX_CTL(OUT_OVLY));
ade_update_reload_bit(base, OVLY_OFST + OUT_OVLY, 0);
/* display source setting */ writel(DISP_SRC_OVLY2, base + ADE_DISP_SRC_CFG);
@@ -232,6 +315,97 @@ static void ade_display_enable(struct ade_crtc *acrtc) writel(DSI_PCLK_ON, base + LDI_HDMI_DSI_GT); }
+#if ADE_DEBUG +static void ade_rdma_dump_regs(void __iomem *base, u32 ch) +{
u32 reg_ctrl, reg_addr, reg_size, reg_stride, reg_space, reg_en;
u32 val;
reg_ctrl = RD_CH_CTRL(ch);
reg_addr = RD_CH_ADDR(ch);
reg_size = RD_CH_SIZE(ch);
reg_stride = RD_CH_STRIDE(ch);
reg_space = RD_CH_SPACE(ch);
reg_en = RD_CH_EN(ch);
val = ade_read_reload_bit(base, RDMA_OFST + ch);
DRM_DEBUG_DRIVER("[rdma%d]: reload(%d)\n", ch + 1, val);
val = readl(base + reg_ctrl);
DRM_DEBUG_DRIVER("[rdma%d]: reg_ctrl(0x%08x)\n", ch + 1, val);
val = readl(base + reg_addr);
DRM_DEBUG_DRIVER("[rdma%d]: reg_addr(0x%08x)\n", ch + 1, val);
val = readl(base + reg_size);
DRM_DEBUG_DRIVER("[rdma%d]: reg_size(0x%08x)\n", ch + 1, val);
val = readl(base + reg_stride);
DRM_DEBUG_DRIVER("[rdma%d]: reg_stride(0x%08x)\n", ch + 1, val);
val = readl(base + reg_space);
DRM_DEBUG_DRIVER("[rdma%d]: reg_space(0x%08x)\n", ch + 1, val);
val = readl(base + reg_en);
DRM_DEBUG_DRIVER("[rdma%d]: reg_en(0x%08x)\n", ch + 1, val);
+}
+static void ade_clip_dump_regs(void __iomem *base, u32 ch) +{
u32 val;
val = ade_read_reload_bit(base, CLIP_OFST + ch);
DRM_DEBUG_DRIVER("[clip%d]: reload(%d)\n", ch + 1, val);
val = readl(base + ADE_CLIP_DISABLE(ch));
DRM_DEBUG_DRIVER("[clip%d]: reg_clip_disable(0x%08x)\n", ch + 1,
val);
val = readl(base + ADE_CLIP_SIZE0(ch));
DRM_DEBUG_DRIVER("[clip%d]: reg_clip_size0(0x%08x)\n", ch + 1,
val);
val = readl(base + ADE_CLIP_SIZE1(ch));
DRM_DEBUG_DRIVER("[clip%d]: reg_clip_size1(0x%08x)\n", ch + 1,
val); +}
+static void ade_compositor_routing_dump_regs(void __iomem *base, u32 ch) +{
u8 ovly_ch = 0; /* TODO: Only primary plane now */
u32 val;
val = readl(base + ADE_OVLY_CH_XY0(ovly_ch));
DRM_DEBUG_DRIVER("[overlay ch%d]: reg_ch_xy0(0x%08x)\n", ovly_ch,
val);
val = readl(base + ADE_OVLY_CH_XY1(ovly_ch));
DRM_DEBUG_DRIVER("[overlay ch%d]: reg_ch_xy1(0x%08x)\n", ovly_ch,
val);
val = readl(base + ADE_OVLY_CH_CTL(ovly_ch));
DRM_DEBUG_DRIVER("[overlay ch%d]: reg_ch_ctl(0x%08x)\n", ovly_ch,
val); +}
+static void ade_dump_overlay_compositor_regs(void __iomem *base, u32 comp) +{
u32 val;
val = ade_read_reload_bit(base, OVLY_OFST + comp);
DRM_DEBUG_DRIVER("[overlay%d]: reload(%d)\n", comp + 1, val);
writel(ADE_ENABLE, base + ADE_OVLYX_CTL(comp));
DRM_DEBUG_DRIVER("[overlay%d]: reg_ctl(0x%08x)\n", comp + 1, val);
val = readl(base + ADE_OVLY_CTL);
DRM_DEBUG_DRIVER("ovly_ctl(0x%08x)\n", val);
+}
+static void ade_dump_regs(void __iomem *base) +{
u32 i;
/* dump channel regs */
for (i = 0; i < ADE_CH_NUM; i++) {
/* dump rdma regs */
ade_rdma_dump_regs(base, i);
/* dump clip regs */
ade_clip_dump_regs(base, i);
/* dump compositor routing regs */
ade_compositor_routing_dump_regs(base, i);
}
/* dump overlay compositor regs */
ade_dump_overlay_compositor_regs(base, OUT_OVLY);
+} +#else +static void ade_dump_regs(void __iomem *base) { } +#endif
- static void ade_crtc_enable(struct drm_crtc *crtc) { struct ade_crtc *acrtc = to_ade_crtc(crtc);
@@ -249,6 +423,7 @@ static void ade_crtc_enable(struct drm_crtc *crtc)
ade_set_medianoc_qos(acrtc); ade_display_enable(acrtc);
}ade_dump_regs(ctx->base); acrtc->enable = true;
@@ -303,6 +478,7 @@ static void ade_crtc_atomic_flush(struct drm_crtc *crtc,
/* only crtc is eanbled regs take effect */ if (acrtc->enable) {
ade_dump_regs(base); /* flush ade regitsters */ writel(ADE_ENABLE, base + ADE_EN); }
@@ -359,6 +535,338 @@ static int ade_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, return 0; }
+static void ade_rdma_set(void __iomem *base, struct drm_framebuffer *fb,
u32 ch, u32 y, u32 in_h, u32 fmt)
+{
struct drm_gem_cma_object *obj = drm_fb_cma_get_gem_obj(fb, 0);
u32 reg_ctrl, reg_addr, reg_size, reg_stride, reg_space, reg_en;
u32 stride = fb->pitches[0];
u32 addr = (u32)obj->paddr + y * stride;
DRM_DEBUG_DRIVER("rdma%d: (y=%d, height=%d), stride=%d,
paddr=0x%x\n",
ch + 1, y, in_h, stride, (u32)obj->paddr);
DRM_DEBUG_DRIVER("addr=0x%x, fb:%dx%d, pixel_format=%d(%s)\n",
addr, fb->width, fb->height, fmt,
drm_get_format_name(fb->pixel_format));
/* get reg offset */
reg_ctrl = RD_CH_CTRL(ch);
reg_addr = RD_CH_ADDR(ch);
reg_size = RD_CH_SIZE(ch);
reg_stride = RD_CH_STRIDE(ch);
reg_space = RD_CH_SPACE(ch);
reg_en = RD_CH_EN(ch);
/*
* TODO: set rotation
*/
writel((fmt << 16) & 0x1f0000, base + reg_ctrl);
writel(addr, base + reg_addr);
writel((in_h << 16) | stride, base + reg_size);
writel(stride, base + reg_stride);
writel(in_h * stride, base + reg_space);
writel(ADE_ENABLE, base + reg_en);
ade_update_reload_bit(base, RDMA_OFST + ch, 0);
+}
+static void ade_rdma_disable(void __iomem *base, u32 ch) +{
u32 reg_en;
/* get reg offset */
reg_en = RD_CH_EN(ch);
writel(0, base + reg_en);
ade_update_reload_bit(base, RDMA_OFST + ch, 1);
+}
+static void ade_clip_set(void __iomem *base, u32 ch, u32 fb_w, u32 x,
u32 in_w, u32 in_h)
+{
u32 disable_val;
u32 clip_left;
u32 clip_right;
/*
* clip width, no need to clip height
*/
if (fb_w == in_w) { /* bypass */
disable_val = 1;
clip_left = 0;
clip_right = 0;
} else {
disable_val = 0;
clip_left = x;
clip_right = fb_w - (x + in_w) - 1;
}
DRM_DEBUG_DRIVER("clip%d: clip_left=%d, clip_right=%d\n",
ch + 1, clip_left, clip_right);
writel(disable_val, base + ADE_CLIP_DISABLE(ch));
writel((fb_w - 1) << 16 | (in_h - 1), base + ADE_CLIP_SIZE0(ch));
writel(clip_left << 16 | clip_right, base + ADE_CLIP_SIZE1(ch));
ade_update_reload_bit(base, CLIP_OFST + ch, 0);
+}
+static void ade_clip_disable(void __iomem *base, u32 ch) +{
writel(1, base + ADE_CLIP_DISABLE(ch));
ade_update_reload_bit(base, CLIP_OFST + ch, 1);
+}
+static bool has_Alpha_channel(int format) +{
switch (format) {
case ADE_ARGB_8888:
case ADE_ABGR_8888:
case ADE_RGBA_8888:
case ADE_BGRA_8888:
return true;
default:
return false;
}
+}
+static void ade_get_blending_params(u32 fmt, u8 glb_alpha, u8 *alp_mode,
u8 *alp_sel, u8 *under_alp_sel)
+{
bool has_alpha = has_Alpha_channel(fmt);
/*
* get alp_mode
*/
if (has_alpha && glb_alpha < 255)
*alp_mode = ADE_ALP_PIXEL_AND_GLB;
else if (has_alpha)
*alp_mode = ADE_ALP_PIXEL;
else
*alp_mode = ADE_ALP_GLOBAL;
/*
* get alp sel
*/
*alp_sel = ADE_ALP_MUL_COEFF_3; /* 1 */
*under_alp_sel = ADE_ALP_MUL_COEFF_2; /* 0 */
+}
+static void ade_compositor_routing_set(void __iomem *base, u8 ch,
u32 x0, u32 y0,
u32 in_w, u32 in_h, u32 fmt)
+{
u8 ovly_ch = 0; /* TODO: This is the zpos, only one plane now */
Does the ovly_ch map to the crtc here? If so, maybe instead of hard coding it here, you could extract the channel number via plane->crtc?
This is the Zpos property of plane not the plane number. It is hard code because currently, only one primary plane is supported. Will support muti-planes and add up Zpos property in the future.
Thanks, -xinliang
Other than the hard coding, it looks fine to me.
Thanks, Archit
-- Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
Add vblank irq handle.
v6: None. v5: None. v4: None. v3: - Remove hisi_get_crtc_from_index func. - A few cleanup. v2: - Remove abtraction layer.
Signed-off-by: Xinliang Liu xinliang.liu@linaro.org --- drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c | 62 +++++++++++++++++++++++++ drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c | 14 +++++- 2 files changed, 75 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c index aa2cf75cab39..749382952ab7 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c @@ -292,6 +292,59 @@ static void ade_set_medianoc_qos(struct ade_crtc *acrtc) SOCKET_QOS_EN, SOCKET_QOS_EN); }
+static int ade_enable_vblank(struct drm_device *dev, unsigned int pipe) +{ + struct kirin_drm_private *priv = dev->dev_private; + struct ade_crtc *acrtc = to_ade_crtc(priv->crtc[pipe]); + struct ade_hw_ctx *ctx = acrtc->ctx; + void __iomem *base = ctx->base; + + if (!ctx->power_on) + (void)ade_power_up(ctx); + + ade_update_bits(base + LDI_INT_EN, FRAME_END_INT_EN_OFST, + MASK(1), 1); + + return 0; +} + +static void ade_disable_vblank(struct drm_device *dev, unsigned int pipe) +{ + struct kirin_drm_private *priv = dev->dev_private; + struct ade_crtc *acrtc = to_ade_crtc(priv->crtc[pipe]); + struct ade_hw_ctx *ctx = acrtc->ctx; + void __iomem *base = ctx->base; + + if (!ctx->power_on) { + DRM_ERROR("power is down! vblank disable fail\n"); + return; + } + + ade_update_bits(base + LDI_INT_EN, FRAME_END_INT_EN_OFST, + MASK(1), 0); +} + +static irqreturn_t ade_irq_handler(int irq, void *data) +{ + struct ade_crtc *acrtc = data; + struct ade_hw_ctx *ctx = acrtc->ctx; + struct drm_crtc *crtc = &acrtc->base; + void __iomem *base = ctx->base; + u32 status; + + status = readl(base + LDI_MSK_INT); + DRM_DEBUG_VBL("LDI IRQ: status=0x%X\n", status); + + /* vblank irq */ + if (status & BIT(FRAME_END_INT_EN_OFST)) { + ade_update_bits(base + LDI_INT_CLR, FRAME_END_INT_EN_OFST, + MASK(1), 1); + drm_crtc_handle_vblank(crtc); + } + + return IRQ_HANDLED; +} + static void ade_display_enable(struct ade_crtc *acrtc) { struct ade_hw_ctx *ctx = acrtc->ctx; @@ -967,6 +1020,15 @@ int ade_drm_init(struct drm_device *dev) if (ret) return ret;
+ /* vblank irq init */ + ret = devm_request_irq(dev->dev, ctx->irq, ade_irq_handler, + DRIVER_IRQ_SHARED, dev->driver->name, acrtc); + if (ret) + return ret; + dev->driver->get_vblank_counter = drm_vblank_no_hw_counter; + dev->driver->enable_vblank = ade_enable_vblank; + dev->driver->disable_vblank = ade_disable_vblank; + return 0; }
diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c index 055729c1889c..723888feb760 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c @@ -32,6 +32,7 @@ static int kirin_drm_kms_cleanup(struct drm_device *dev) { struct kirin_drm_private *priv = dev->dev_private;
+ drm_vblank_cleanup(dev); dc_ops->cleanup(dev); drm_mode_config_cleanup(dev); devm_kfree(dev->dev, priv); @@ -85,11 +86,22 @@ static int kirin_drm_kms_init(struct drm_device *dev) goto err_dc_cleanup; }
+ /* vblank init */ + ret = drm_vblank_init(dev, dev->mode_config.num_crtc); + if (ret) { + DRM_ERROR("failed to initialize vblank.\n"); + goto err_unbind_all; + } + /* with irq_enabled = true, we can use the vblank feature. */ + dev->irq_enabled = true; + /* reset all the states of crtc/plane/encoder/connector */ drm_mode_config_reset(dev);
return 0;
+err_unbind_all: + component_unbind_all(dev->dev, dev); err_dc_cleanup: dc_ops->cleanup(dev); err_mode_config_cleanup: @@ -123,7 +135,7 @@ static int kirin_gem_cma_dumb_create(struct drm_file *file,
static struct drm_driver kirin_drm_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | - DRIVER_ATOMIC, + DRIVER_ATOMIC | DRIVER_HAVE_IRQ, .fops = &kirin_drm_fops, .set_busid = drm_platform_set_busid,
On 2/26/2016 2:10 PM, Xinliang Liu wrote:
Add vblank irq handle.
v6: None. v5: None. v4: None. v3:
- Remove hisi_get_crtc_from_index func.
- A few cleanup.
v2:
- Remove abtraction layer.
Signed-off-by: Xinliang Liu xinliang.liu@linaro.org
drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c | 62 +++++++++++++++++++++++++ drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c | 14 +++++- 2 files changed, 75 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c index aa2cf75cab39..749382952ab7 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c @@ -292,6 +292,59 @@ static void ade_set_medianoc_qos(struct ade_crtc *acrtc) SOCKET_QOS_EN, SOCKET_QOS_EN); }
+static int ade_enable_vblank(struct drm_device *dev, unsigned int pipe) +{
- struct kirin_drm_private *priv = dev->dev_private;
- struct ade_crtc *acrtc = to_ade_crtc(priv->crtc[pipe]);
- struct ade_hw_ctx *ctx = acrtc->ctx;
- void __iomem *base = ctx->base;
- if (!ctx->power_on)
(void)ade_power_up(ctx);
- ade_update_bits(base + LDI_INT_EN, FRAME_END_INT_EN_OFST,
MASK(1), 1);
- return 0;
+}
+static void ade_disable_vblank(struct drm_device *dev, unsigned int pipe) +{
- struct kirin_drm_private *priv = dev->dev_private;
- struct ade_crtc *acrtc = to_ade_crtc(priv->crtc[pipe]);
- struct ade_hw_ctx *ctx = acrtc->ctx;
- void __iomem *base = ctx->base;
- if (!ctx->power_on) {
DRM_ERROR("power is down! vblank disable fail\n");
return;
- }
- ade_update_bits(base + LDI_INT_EN, FRAME_END_INT_EN_OFST,
MASK(1), 0);
+}
+static irqreturn_t ade_irq_handler(int irq, void *data) +{
- struct ade_crtc *acrtc = data;
- struct ade_hw_ctx *ctx = acrtc->ctx;
- struct drm_crtc *crtc = &acrtc->base;
- void __iomem *base = ctx->base;
- u32 status;
- status = readl(base + LDI_MSK_INT);
- DRM_DEBUG_VBL("LDI IRQ: status=0x%X\n", status);
- /* vblank irq */
- if (status & BIT(FRAME_END_INT_EN_OFST)) {
ade_update_bits(base + LDI_INT_CLR, FRAME_END_INT_EN_OFST,
MASK(1), 1);
drm_crtc_handle_vblank(crtc);
- }
- return IRQ_HANDLED;
+}
- static void ade_display_enable(struct ade_crtc *acrtc) { struct ade_hw_ctx *ctx = acrtc->ctx;
@@ -967,6 +1020,15 @@ int ade_drm_init(struct drm_device *dev) if (ret) return ret;
- /* vblank irq init */
- ret = devm_request_irq(dev->dev, ctx->irq, ade_irq_handler,
DRIVER_IRQ_SHARED, dev->driver->name, acrtc);
This isn't the way we set up interrupts for kms drivers. We need to provide the handler in drm_driver and call drm_irq_install.
- if (ret)
return ret;
- dev->driver->get_vblank_counter = drm_vblank_no_hw_counter;
- dev->driver->enable_vblank = ade_enable_vblank;
- dev->driver->disable_vblank = ade_disable_vblank;
- return 0; }
diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c index 055729c1889c..723888feb760 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c @@ -32,6 +32,7 @@ static int kirin_drm_kms_cleanup(struct drm_device *dev) { struct kirin_drm_private *priv = dev->dev_private;
- drm_vblank_cleanup(dev); dc_ops->cleanup(dev); drm_mode_config_cleanup(dev); devm_kfree(dev->dev, priv);
@@ -85,11 +86,22 @@ static int kirin_drm_kms_init(struct drm_device *dev) goto err_dc_cleanup; }
/* vblank init */
ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
if (ret) {
DRM_ERROR("failed to initialize vblank.\n");
goto err_unbind_all;
}
/* with irq_enabled = true, we can use the vblank feature. */
dev->irq_enabled = true;
/* reset all the states of crtc/plane/encoder/connector */ drm_mode_config_reset(dev);
return 0;
+err_unbind_all:
- component_unbind_all(dev->dev, dev); err_dc_cleanup: dc_ops->cleanup(dev); err_mode_config_cleanup:
@@ -123,7 +135,7 @@ static int kirin_gem_cma_dumb_create(struct drm_file *file,
static struct drm_driver kirin_drm_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
DRIVER_ATOMIC,
DRIVER_ATOMIC | DRIVER_HAVE_IRQ,
DRIVER_IRQ_SHARED should be added here if you want IRQF_SHARED flag set when requesting the handler.
Archit
.fops = &kirin_drm_fops, .set_busid = drm_platform_set_busid,
Hi,
On 1 March 2016 at 02:48, Archit Taneja architt@codeaurora.org wrote:
On 2/26/2016 2:10 PM, Xinliang Liu wrote:
Add vblank irq handle.
v6: None. v5: None. v4: None. v3:
- Remove hisi_get_crtc_from_index func.
- A few cleanup.
v2:
- Remove abtraction layer.
Signed-off-by: Xinliang Liu xinliang.liu@linaro.org
drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c | 62 +++++++++++++++++++++++++ drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c | 14 +++++- 2 files changed, 75 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c index aa2cf75cab39..749382952ab7 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c @@ -292,6 +292,59 @@ static void ade_set_medianoc_qos(struct ade_crtc *acrtc) SOCKET_QOS_EN, SOCKET_QOS_EN); }
+static int ade_enable_vblank(struct drm_device *dev, unsigned int pipe) +{
struct kirin_drm_private *priv = dev->dev_private;
struct ade_crtc *acrtc = to_ade_crtc(priv->crtc[pipe]);
struct ade_hw_ctx *ctx = acrtc->ctx;
void __iomem *base = ctx->base;
if (!ctx->power_on)
(void)ade_power_up(ctx);
ade_update_bits(base + LDI_INT_EN, FRAME_END_INT_EN_OFST,
MASK(1), 1);
return 0;
+}
+static void ade_disable_vblank(struct drm_device *dev, unsigned int pipe) +{
struct kirin_drm_private *priv = dev->dev_private;
struct ade_crtc *acrtc = to_ade_crtc(priv->crtc[pipe]);
struct ade_hw_ctx *ctx = acrtc->ctx;
void __iomem *base = ctx->base;
if (!ctx->power_on) {
DRM_ERROR("power is down! vblank disable fail\n");
return;
}
ade_update_bits(base + LDI_INT_EN, FRAME_END_INT_EN_OFST,
MASK(1), 0);
+}
+static irqreturn_t ade_irq_handler(int irq, void *data) +{
struct ade_crtc *acrtc = data;
struct ade_hw_ctx *ctx = acrtc->ctx;
struct drm_crtc *crtc = &acrtc->base;
void __iomem *base = ctx->base;
u32 status;
status = readl(base + LDI_MSK_INT);
DRM_DEBUG_VBL("LDI IRQ: status=0x%X\n", status);
/* vblank irq */
if (status & BIT(FRAME_END_INT_EN_OFST)) {
ade_update_bits(base + LDI_INT_CLR, FRAME_END_INT_EN_OFST,
MASK(1), 1);
drm_crtc_handle_vblank(crtc);
}
return IRQ_HANDLED;
+}
- static void ade_display_enable(struct ade_crtc *acrtc) { struct ade_hw_ctx *ctx = acrtc->ctx;
@@ -967,6 +1020,15 @@ int ade_drm_init(struct drm_device *dev) if (ret) return ret;
/* vblank irq init */
ret = devm_request_irq(dev->dev, ctx->irq, ade_irq_handler,
DRIVER_IRQ_SHARED, dev->driver->name,
acrtc);
This isn't the way we set up interrupts for kms drivers. We need to provide the handler in drm_driver and call drm_irq_install.
I prefer to set up interrupts here for two reasons. One is that it is easy to pass any interrupt private "void * data" to the interrupt handler here. As I discussed with Daniel Vetter before: https://lkml.org/lkml/2015/9/10/204.
Second is setting up interrupt here in the specific SoC display controller driver, make other SoC reuse the kirin_drm_drv.c code easily. Different Hisilicon SoC may has different display controller interrupts.
if (ret)
return ret;
dev->driver->get_vblank_counter = drm_vblank_no_hw_counter;
dev->driver->enable_vblank = ade_enable_vblank;
dev->driver->disable_vblank = ade_disable_vblank;
}return 0;
diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c index 055729c1889c..723888feb760 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c @@ -32,6 +32,7 @@ static int kirin_drm_kms_cleanup(struct drm_device *dev) { struct kirin_drm_private *priv = dev->dev_private;
drm_vblank_cleanup(dev); dc_ops->cleanup(dev); drm_mode_config_cleanup(dev); devm_kfree(dev->dev, priv);
@@ -85,11 +86,22 @@ static int kirin_drm_kms_init(struct drm_device *dev) goto err_dc_cleanup; }
/* vblank init */
ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
if (ret) {
DRM_ERROR("failed to initialize vblank.\n");
goto err_unbind_all;
}
/* with irq_enabled = true, we can use the vblank feature. */
dev->irq_enabled = true;
/* reset all the states of crtc/plane/encoder/connector */ drm_mode_config_reset(dev); return 0;
+err_unbind_all:
err_dc_cleanup: dc_ops->cleanup(dev); err_mode_config_cleanup:component_unbind_all(dev->dev, dev);
@@ -123,7 +135,7 @@ static int kirin_gem_cma_dumb_create(struct drm_file *file,
static struct drm_driver kirin_drm_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
DRIVER_ATOMIC,
DRIVER_ATOMIC | DRIVER_HAVE_IRQ,
DRIVER_IRQ_SHARED should be added here if you want IRQF_SHARED flag set when requesting the handler.
If not using the drm_irq_install to set up interrupts, DRIVER_IRQ_SHARED may not be required to set.
Thanks, -xinliang
Archit
.fops = &kirin_drm_fops, .set_busid = drm_platform_set_busid,
-- Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
On 3/1/2016 3:44 PM, Xinliang Liu wrote:
Hi,
On 1 March 2016 at 02:48, Archit Taneja architt@codeaurora.org wrote:
On 2/26/2016 2:10 PM, Xinliang Liu wrote:
Add vblank irq handle.
v6: None. v5: None. v4: None. v3:
- Remove hisi_get_crtc_from_index func.
- A few cleanup.
v2:
- Remove abtraction layer.
Signed-off-by: Xinliang Liu xinliang.liu@linaro.org
drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c | 62 +++++++++++++++++++++++++ drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c | 14 +++++- 2 files changed, 75 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c index aa2cf75cab39..749382952ab7 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c @@ -292,6 +292,59 @@ static void ade_set_medianoc_qos(struct ade_crtc *acrtc) SOCKET_QOS_EN, SOCKET_QOS_EN); }
+static int ade_enable_vblank(struct drm_device *dev, unsigned int pipe) +{
struct kirin_drm_private *priv = dev->dev_private;
struct ade_crtc *acrtc = to_ade_crtc(priv->crtc[pipe]);
struct ade_hw_ctx *ctx = acrtc->ctx;
void __iomem *base = ctx->base;
if (!ctx->power_on)
(void)ade_power_up(ctx);
ade_update_bits(base + LDI_INT_EN, FRAME_END_INT_EN_OFST,
MASK(1), 1);
return 0;
+}
+static void ade_disable_vblank(struct drm_device *dev, unsigned int pipe) +{
struct kirin_drm_private *priv = dev->dev_private;
struct ade_crtc *acrtc = to_ade_crtc(priv->crtc[pipe]);
struct ade_hw_ctx *ctx = acrtc->ctx;
void __iomem *base = ctx->base;
if (!ctx->power_on) {
DRM_ERROR("power is down! vblank disable fail\n");
return;
}
ade_update_bits(base + LDI_INT_EN, FRAME_END_INT_EN_OFST,
MASK(1), 0);
+}
+static irqreturn_t ade_irq_handler(int irq, void *data) +{
struct ade_crtc *acrtc = data;
struct ade_hw_ctx *ctx = acrtc->ctx;
struct drm_crtc *crtc = &acrtc->base;
void __iomem *base = ctx->base;
u32 status;
status = readl(base + LDI_MSK_INT);
DRM_DEBUG_VBL("LDI IRQ: status=0x%X\n", status);
/* vblank irq */
if (status & BIT(FRAME_END_INT_EN_OFST)) {
ade_update_bits(base + LDI_INT_CLR, FRAME_END_INT_EN_OFST,
MASK(1), 1);
drm_crtc_handle_vblank(crtc);
}
return IRQ_HANDLED;
+}
- static void ade_display_enable(struct ade_crtc *acrtc) { struct ade_hw_ctx *ctx = acrtc->ctx;
@@ -967,6 +1020,15 @@ int ade_drm_init(struct drm_device *dev) if (ret) return ret;
/* vblank irq init */
ret = devm_request_irq(dev->dev, ctx->irq, ade_irq_handler,
DRIVER_IRQ_SHARED, dev->driver->name,
acrtc);
This isn't the way we set up interrupts for kms drivers. We need to provide the handler in drm_driver and call drm_irq_install.
I prefer to set up interrupts here for two reasons. One is that it is easy to pass any interrupt private "void * data" to the interrupt handler here. As I discussed with Daniel Vetter before: https://lkml.org/lkml/2015/9/10/204.
Second is setting up interrupt here in the specific SoC display controller driver, make other SoC reuse the kirin_drm_drv.c code easily. Different Hisilicon SoC may has different display controller interrupts.
Sure, as long as this has been discussed before.
if (ret)
return ret;
dev->driver->get_vblank_counter = drm_vblank_no_hw_counter;
dev->driver->enable_vblank = ade_enable_vblank;
dev->driver->disable_vblank = ade_disable_vblank;
}return 0;
diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c index 055729c1889c..723888feb760 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c @@ -32,6 +32,7 @@ static int kirin_drm_kms_cleanup(struct drm_device *dev) { struct kirin_drm_private *priv = dev->dev_private;
drm_vblank_cleanup(dev); dc_ops->cleanup(dev); drm_mode_config_cleanup(dev); devm_kfree(dev->dev, priv);
@@ -85,11 +86,22 @@ static int kirin_drm_kms_init(struct drm_device *dev) goto err_dc_cleanup; }
/* vblank init */
ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
if (ret) {
DRM_ERROR("failed to initialize vblank.\n");
goto err_unbind_all;
}
/* with irq_enabled = true, we can use the vblank feature. */
dev->irq_enabled = true;
/* reset all the states of crtc/plane/encoder/connector */ drm_mode_config_reset(dev); return 0;
+err_unbind_all:
err_dc_cleanup: dc_ops->cleanup(dev); err_mode_config_cleanup:component_unbind_all(dev->dev, dev);
@@ -123,7 +135,7 @@ static int kirin_gem_cma_dumb_create(struct drm_file *file,
static struct drm_driver kirin_drm_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
DRIVER_ATOMIC,
DRIVER_ATOMIC | DRIVER_HAVE_IRQ,
DRIVER_IRQ_SHARED should be added here if you want IRQF_SHARED flag set when requesting the handler.
If not using the drm_irq_install to set up interrupts, DRIVER_IRQ_SHARED may not be required to set.
Yes. In any case, you should use IRQF_SHARED and not DRIVER_IRQ_SHARED in the call to devm_request_irq(). The latter is a drm flag meant to be used only for driver_features.
Thanks, Archit
On 1 March 2016 at 20:40, Archit Taneja architt@codeaurora.org wrote:
On 3/1/2016 3:44 PM, Xinliang Liu wrote:
Hi,
On 1 March 2016 at 02:48, Archit Taneja architt@codeaurora.org wrote:
On 2/26/2016 2:10 PM, Xinliang Liu wrote:
Add vblank irq handle.
v6: None. v5: None. v4: None. v3:
- Remove hisi_get_crtc_from_index func.
- A few cleanup.
v2:
- Remove abtraction layer.
Signed-off-by: Xinliang Liu xinliang.liu@linaro.org
drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c | 62 +++++++++++++++++++++++++ drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c | 14 +++++- 2 files changed, 75 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c index aa2cf75cab39..749382952ab7 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c @@ -292,6 +292,59 @@ static void ade_set_medianoc_qos(struct ade_crtc *acrtc) SOCKET_QOS_EN, SOCKET_QOS_EN); }
+static int ade_enable_vblank(struct drm_device *dev, unsigned int pipe) +{
struct kirin_drm_private *priv = dev->dev_private;
struct ade_crtc *acrtc = to_ade_crtc(priv->crtc[pipe]);
struct ade_hw_ctx *ctx = acrtc->ctx;
void __iomem *base = ctx->base;
if (!ctx->power_on)
(void)ade_power_up(ctx);
ade_update_bits(base + LDI_INT_EN, FRAME_END_INT_EN_OFST,
MASK(1), 1);
return 0;
+}
+static void ade_disable_vblank(struct drm_device *dev, unsigned int pipe) +{
struct kirin_drm_private *priv = dev->dev_private;
struct ade_crtc *acrtc = to_ade_crtc(priv->crtc[pipe]);
struct ade_hw_ctx *ctx = acrtc->ctx;
void __iomem *base = ctx->base;
if (!ctx->power_on) {
DRM_ERROR("power is down! vblank disable fail\n");
return;
}
ade_update_bits(base + LDI_INT_EN, FRAME_END_INT_EN_OFST,
MASK(1), 0);
+}
+static irqreturn_t ade_irq_handler(int irq, void *data) +{
struct ade_crtc *acrtc = data;
struct ade_hw_ctx *ctx = acrtc->ctx;
struct drm_crtc *crtc = &acrtc->base;
void __iomem *base = ctx->base;
u32 status;
status = readl(base + LDI_MSK_INT);
DRM_DEBUG_VBL("LDI IRQ: status=0x%X\n", status);
/* vblank irq */
if (status & BIT(FRAME_END_INT_EN_OFST)) {
ade_update_bits(base + LDI_INT_CLR,
FRAME_END_INT_EN_OFST,
MASK(1), 1);
drm_crtc_handle_vblank(crtc);
}
return IRQ_HANDLED;
+}
- static void ade_display_enable(struct ade_crtc *acrtc) { struct ade_hw_ctx *ctx = acrtc->ctx;
@@ -967,6 +1020,15 @@ int ade_drm_init(struct drm_device *dev) if (ret) return ret;
/* vblank irq init */
ret = devm_request_irq(dev->dev, ctx->irq, ade_irq_handler,
DRIVER_IRQ_SHARED, dev->driver->name,
acrtc);
This isn't the way we set up interrupts for kms drivers. We need to provide the handler in drm_driver and call drm_irq_install.
I prefer to set up interrupts here for two reasons. One is that it is easy to pass any interrupt private "void * data" to the interrupt handler here. As I discussed with Daniel Vetter before: https://lkml.org/lkml/2015/9/10/204.
Second is setting up interrupt here in the specific SoC display controller driver, make other SoC reuse the kirin_drm_drv.c code easily. Different Hisilicon SoC may has different display controller interrupts.
Sure, as long as this has been discussed before.
if (ret)
return ret;
dev->driver->get_vblank_counter = drm_vblank_no_hw_counter;
dev->driver->enable_vblank = ade_enable_vblank;
dev->driver->disable_vblank = ade_disable_vblank;
}return 0;
diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c index 055729c1889c..723888feb760 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c @@ -32,6 +32,7 @@ static int kirin_drm_kms_cleanup(struct drm_device *dev) { struct kirin_drm_private *priv = dev->dev_private;
drm_vblank_cleanup(dev); dc_ops->cleanup(dev); drm_mode_config_cleanup(dev); devm_kfree(dev->dev, priv);
@@ -85,11 +86,22 @@ static int kirin_drm_kms_init(struct drm_device *dev) goto err_dc_cleanup; }
/* vblank init */
ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
if (ret) {
DRM_ERROR("failed to initialize vblank.\n");
goto err_unbind_all;
}
/* with irq_enabled = true, we can use the vblank feature. */
dev->irq_enabled = true;
/* reset all the states of crtc/plane/encoder/connector */ drm_mode_config_reset(dev); return 0;
+err_unbind_all:
err_dc_cleanup: dc_ops->cleanup(dev); err_mode_config_cleanup:component_unbind_all(dev->dev, dev);
@@ -123,7 +135,7 @@ static int kirin_gem_cma_dumb_create(struct drm_file *file,
static struct drm_driver kirin_drm_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
DRIVER_ATOMIC,
DRIVER_ATOMIC | DRIVER_HAVE_IRQ,
DRIVER_IRQ_SHARED should be added here if you want IRQF_SHARED flag set when requesting the handler.
If not using the drm_irq_install to set up interrupts, DRIVER_IRQ_SHARED may not be required to set.
Yes. In any case, you should use IRQF_SHARED and not DRIVER_IRQ_SHARED in the call to devm_request_irq(). The latter is a drm flag meant to be used only for driver_features.
agree, will fix in next version.
Thanks, Archit
-- Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
Add cma Fbdev, Fbdev is legency and optional, you can enable/disable it by configuring DRM_FBDEV_EMULATION. Add hotplug.
v6: None. v5: None. v4: None. v3: None. v2: - Use CONFIG_DRM_FBDEV_EMULATION instead of CONFIG_DRM_HISI_FBDEV.
Signed-off-by: Xinliang Liu xinliang.liu@linaro.org --- drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c | 34 +++++++++++++++++++++++++ drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h | 3 +++ 2 files changed, 37 insertions(+)
diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c index 723888feb760..d57b9fa0ce3e 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c @@ -23,6 +23,7 @@ #include <drm/drm_gem_cma_helper.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc_helper.h>
#include "kirin_drm_drv.h"
@@ -32,6 +33,13 @@ static int kirin_drm_kms_cleanup(struct drm_device *dev) { struct kirin_drm_private *priv = dev->dev_private;
+#ifdef CONFIG_DRM_FBDEV_EMULATION + if (priv->fbdev) { + drm_fbdev_cma_fini(priv->fbdev); + priv->fbdev = NULL; + } +#endif + drm_kms_helper_poll_fini(dev); drm_vblank_cleanup(dev); dc_ops->cleanup(dev); drm_mode_config_cleanup(dev); @@ -41,8 +49,28 @@ static int kirin_drm_kms_cleanup(struct drm_device *dev) return 0; }
+#ifdef CONFIG_DRM_FBDEV_EMULATION +static void kirin_fbdev_output_poll_changed(struct drm_device *dev) +{ + struct kirin_drm_private *priv = dev->dev_private; + + if (priv->fbdev) { + drm_fbdev_cma_hotplug_event(priv->fbdev); + } else { + priv->fbdev = drm_fbdev_cma_init(dev, 32, + dev->mode_config.num_crtc, + dev->mode_config.num_connector); + if (IS_ERR(priv->fbdev)) + priv->fbdev = NULL; + } +} +#endif + static const struct drm_mode_config_funcs kirin_drm_mode_config_funcs = { .fb_create = drm_fb_cma_create, +#ifdef CONFIG_DRM_FBDEV_EMULATION + .output_poll_changed = kirin_fbdev_output_poll_changed, +#endif .atomic_check = drm_atomic_helper_check, .atomic_commit = drm_atomic_helper_commit, }; @@ -98,6 +126,12 @@ static int kirin_drm_kms_init(struct drm_device *dev) /* reset all the states of crtc/plane/encoder/connector */ drm_mode_config_reset(dev);
+ /* init kms poll for handling hpd */ + drm_kms_helper_poll_init(dev); + + /* force detection after connectors init */ + (void)drm_helper_hpd_irq_event(dev); + return 0;
err_unbind_all: diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h index 5a05ad6a81db..1a07caf8e7f4 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h @@ -21,6 +21,9 @@ struct kirin_dc_ops {
struct kirin_drm_private { struct drm_crtc *crtc[MAX_CRTC]; +#ifdef CONFIG_DRM_FBDEV_EMULATION + struct drm_fbdev_cma *fbdev; +#endif };
extern const struct kirin_dc_ops ade_dc_ops;
Add DesignWare MIPI DSI Host Controller v1.02 encoder driver for hi6220 SoC.
v6: - Change "pclk_dsi" to "pclk". v5: None. v4: None. v3: - Rename file name to dw_drm_dsi.c - Make encoder type as DRM_MODE_ENCODER_DSI. - A few cleanup. v2: - Remove abtraction layer.
Signed-off-by: Xinliang Liu xinliang.liu@linaro.org Signed-off-by: Xinwei Kong kong.kongxinwei@hisilicon.com Signed-off-by: Andy Green andy.green@linaro.org --- drivers/gpu/drm/hisilicon/kirin/Kconfig | 1 + drivers/gpu/drm/hisilicon/kirin/Makefile | 3 +- drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c | 743 +++++++++++++++++++++++++++ drivers/gpu/drm/hisilicon/kirin/dw_dsi_reg.h | 83 +++ 4 files changed, 829 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c create mode 100644 drivers/gpu/drm/hisilicon/kirin/dw_dsi_reg.h
diff --git a/drivers/gpu/drm/hisilicon/kirin/Kconfig b/drivers/gpu/drm/hisilicon/kirin/Kconfig index 3ac4b8edeac1..de0d454c5c13 100644 --- a/drivers/gpu/drm/hisilicon/kirin/Kconfig +++ b/drivers/gpu/drm/hisilicon/kirin/Kconfig @@ -4,6 +4,7 @@ config DRM_HISI_KIRIN select DRM_KMS_HELPER select DRM_GEM_CMA_HELPER select DRM_KMS_CMA_HELPER + select DRM_MIPI_DSI help Choose this option if you have a hisilicon Kirin chipsets(hi6220). If M is selected the module will be called kirin-drm. diff --git a/drivers/gpu/drm/hisilicon/kirin/Makefile b/drivers/gpu/drm/hisilicon/kirin/Makefile index 2a61ab006ddb..5dcd0d4328b6 100644 --- a/drivers/gpu/drm/hisilicon/kirin/Makefile +++ b/drivers/gpu/drm/hisilicon/kirin/Makefile @@ -1,4 +1,5 @@ kirin-drm-y := kirin_drm_drv.o \ - kirin_drm_ade.o + kirin_drm_ade.o \ + dw_drm_dsi.o
obj-$(CONFIG_DRM_HISI_KIRIN) += kirin-drm.o diff --git a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c new file mode 100644 index 000000000000..8329148cc89d --- /dev/null +++ b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c @@ -0,0 +1,743 @@ +/* + * DesignWare MIPI DSI Host Controller v1.02 driver + * + * Copyright (c) 2016 Linaro Limited. + * Copyright (c) 2014-2016 Hisilicon Limited. + * + * Author: + * Xinliang Liu z.liuxinliang@hisilicon.com + * Xinliang Liu xinliang.liu@linaro.org + * Xinwei Kong kong.kongxinwei@hisilicon.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/clk.h> +#include <linux/component.h> +#include <linux/of_graph.h> + +#include <drm/drm_of.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_encoder_slave.h> +#include <drm/drm_atomic_helper.h> + +#include "dw_dsi_reg.h" + +#define MAX_TX_ESC_CLK (10) +#define ROUND(x, y) ((x) / (y) + ((x) % (y) * 10 / (y) >= 5 ? 1 : 0)) +#define PHY_REF_CLK_RATE 19200000 +#define PHY_REF_CLK_PERIOD_PS (1000000000 / (PHY_REF_CLK_RATE / 1000)) + +#define encoder_to_dsi(encoder) \ + container_of(encoder, struct dw_dsi, encoder) +#define host_to_dsi(host) \ + container_of(host, struct dw_dsi, host) + +struct mipi_phy_params { + u32 clk_t_lpx; + u32 clk_t_hs_prepare; + u32 clk_t_hs_zero; + u32 clk_t_hs_trial; + u32 clk_t_wakeup; + u32 data_t_lpx; + u32 data_t_hs_prepare; + u32 data_t_hs_zero; + u32 data_t_hs_trial; + u32 data_t_ta_go; + u32 data_t_ta_get; + u32 data_t_wakeup; + u32 hstx_ckg_sel; + u32 pll_fbd_div5f; + u32 pll_fbd_div1f; + u32 pll_fbd_2p; + u32 pll_enbwt; + u32 pll_fbd_p; + u32 pll_fbd_s; + u32 pll_pre_div1p; + u32 pll_pre_p; + u32 pll_vco_750M; + u32 pll_lpf_rs; + u32 pll_lpf_cs; + u32 clklp2hs_time; + u32 clkhs2lp_time; + u32 lp2hs_time; + u32 hs2lp_time; + u32 clk_to_data_delay; + u32 data_to_clk_delay; + u32 lane_byte_clk_kHz; + u32 clk_division; +}; + +struct dsi_hw_ctx { + void __iomem *base; + struct clk *pclk; +}; + +struct dw_dsi { + struct drm_encoder encoder; + struct drm_display_mode cur_mode; + struct dsi_hw_ctx *ctx; + struct mipi_phy_params phy; + + u32 lanes; + enum mipi_dsi_pixel_format format; + unsigned long mode_flags; + bool enable; +}; + +struct dsi_data { + struct dw_dsi dsi; + struct dsi_hw_ctx ctx; +}; + +struct dsi_phy_range { + u32 min_range_kHz; + u32 max_range_kHz; + u32 pll_vco_750M; + u32 hstx_ckg_sel; +}; + +static const struct dsi_phy_range dphy_range_info[] = { + { 46875, 62500, 1, 7 }, + { 62500, 93750, 0, 7 }, + { 93750, 125000, 1, 6 }, + { 125000, 187500, 0, 6 }, + { 187500, 250000, 1, 5 }, + { 250000, 375000, 0, 5 }, + { 375000, 500000, 1, 4 }, + { 500000, 750000, 0, 4 }, + { 750000, 1000000, 1, 0 }, + { 1000000, 1500000, 0, 0 } +}; + +static void dsi_get_phy_params(u32 phy_freq_kHz, + struct mipi_phy_params *phy) +{ + u32 ui = 0; + u32 cfg_clk_ps = PHY_REF_CLK_PERIOD_PS; + u32 i = 0; + u32 q_pll = 1; + u32 m_pll = 0; + u32 n_pll = 0; + u32 r_pll = 1; + u32 m_n = 0; + u32 m_n_int = 0; + u64 f_kHz; + u64 temp; + u64 tmp_kHz = phy_freq_kHz; + + do { + f_kHz = tmp_kHz; + + /* Find the PLL clock range from the table */ + for (i = 0; i < ARRAY_SIZE(dphy_range_info); i++) + if (f_kHz >= dphy_range_info[i].min_range_kHz && + f_kHz <= dphy_range_info[i].max_range_kHz) + break; + + if (i == ARRAY_SIZE(dphy_range_info)) { + DRM_ERROR("%lldkHz out of range\n", f_kHz); + return; + } + + phy->pll_vco_750M = dphy_range_info[i].pll_vco_750M; + phy->hstx_ckg_sel = dphy_range_info[i].hstx_ckg_sel; + + if (phy->hstx_ckg_sel <= 7 && + phy->hstx_ckg_sel >= 4) + q_pll = 0x10 >> (7 - phy->hstx_ckg_sel); + + temp = f_kHz * (u64)q_pll * (u64)cfg_clk_ps; + m_n_int = temp / (u64)1000000000; + m_n = (temp % (u64)1000000000) / (u64)100000000; + + if (m_n_int % 2 == 0) { + if (m_n * 6 >= 50) { + n_pll = 2; + m_pll = (m_n_int + 1) * n_pll; + } else if (m_n * 6 >= 30) { + n_pll = 3; + m_pll = m_n_int * n_pll + 2; + } else { + n_pll = 1; + m_pll = m_n_int * n_pll; + } + } else { + if (m_n * 6 >= 50) { + n_pll = 1; + m_pll = (m_n_int + 1) * n_pll; + } else if (m_n * 6 >= 30) { + n_pll = 1; + m_pll = (m_n_int + 1) * n_pll; + } else if (m_n * 6 >= 10) { + n_pll = 3; + m_pll = m_n_int * n_pll + 1; + } else { + n_pll = 2; + m_pll = m_n_int * n_pll; + } + } + + if (n_pll == 1) { + phy->pll_fbd_p = 0; + phy->pll_pre_div1p = 1; + } else { + phy->pll_fbd_p = n_pll; + phy->pll_pre_div1p = 0; + } + + if (phy->pll_fbd_2p <= 7 && phy->pll_fbd_2p >= 4) + r_pll = 0x10 >> (7 - phy->pll_fbd_2p); + + if (m_pll == 2) { + phy->pll_pre_p = 0; + phy->pll_fbd_s = 0; + phy->pll_fbd_div1f = 0; + phy->pll_fbd_div5f = 1; + } else if (m_pll >= 2 * 2 * r_pll && m_pll <= 2 * 4 * r_pll) { + phy->pll_pre_p = m_pll / (2 * r_pll); + phy->pll_fbd_s = 0; + phy->pll_fbd_div1f = 1; + phy->pll_fbd_div5f = 0; + } else if (m_pll >= 2 * 5 * r_pll && m_pll <= 2 * 150 * r_pll) { + if (((m_pll / (2 * r_pll)) % 2) == 0) { + phy->pll_pre_p = + (m_pll / (2 * r_pll)) / 2 - 1; + phy->pll_fbd_s = + (m_pll / (2 * r_pll)) % 2 + 2; + } else { + phy->pll_pre_p = + (m_pll / (2 * r_pll)) / 2; + phy->pll_fbd_s = + (m_pll / (2 * r_pll)) % 2; + } + phy->pll_fbd_div1f = 0; + phy->pll_fbd_div5f = 0; + } else { + phy->pll_pre_p = 0; + phy->pll_fbd_s = 0; + phy->pll_fbd_div1f = 0; + phy->pll_fbd_div5f = 1; + } + + f_kHz = (u64)1000000000 * (u64)m_pll / + ((u64)cfg_clk_ps * (u64)n_pll * (u64)q_pll); + + if (f_kHz >= phy_freq_kHz) + break; + + tmp_kHz += 10; + + } while (1); + + ui = 1000000 / f_kHz; + + phy->clk_t_lpx = ROUND(50, 8 * ui); + phy->clk_t_hs_prepare = ROUND(133, 16 * ui) - 1; + + phy->clk_t_hs_zero = ROUND(262, 8 * ui); + phy->clk_t_hs_trial = 2 * (ROUND(60, 8 * ui) - 1); + phy->clk_t_wakeup = ROUND(1000000, (cfg_clk_ps / 1000) - 1); + if (phy->clk_t_wakeup > 0xff) + phy->clk_t_wakeup = 0xff; + phy->data_t_wakeup = phy->clk_t_wakeup; + phy->data_t_lpx = phy->clk_t_lpx; + phy->data_t_hs_prepare = ROUND(125 + 10 * ui, 16 * ui) - 1; + phy->data_t_hs_zero = ROUND(105 + 6 * ui, 8 * ui); + phy->data_t_hs_trial = 2 * (ROUND(60 + 4 * ui, 8 * ui) - 1); + phy->data_t_ta_go = 3; + phy->data_t_ta_get = 4; + + phy->pll_enbwt = 1; + phy->clklp2hs_time = ROUND(407, 8 * ui) + 12; + phy->clkhs2lp_time = ROUND(105 + 12 * ui, 8 * ui); + phy->lp2hs_time = ROUND(240 + 12 * ui, 8 * ui) + 1; + phy->hs2lp_time = phy->clkhs2lp_time; + phy->clk_to_data_delay = 1 + phy->clklp2hs_time; + phy->data_to_clk_delay = ROUND(60 + 52 * ui, 8 * ui) + + phy->clkhs2lp_time; + + phy->lane_byte_clk_kHz = f_kHz / 8; + phy->clk_division = phy->lane_byte_clk_kHz / MAX_TX_ESC_CLK; + if (phy->lane_byte_clk_kHz % MAX_TX_ESC_CLK) + phy->clk_division++; +} + +static u32 dsi_get_dpi_color_coding(enum mipi_dsi_pixel_format format) +{ + u32 val; + + /* TODO: only support RGB888 now, to support more */ + switch (format) { + case MIPI_DSI_FMT_RGB888: + val = DSI_24BITS_1; + break; + default: + val = DSI_24BITS_1; + break; + } + + return val; +} + +/* + * dsi phy reg write function + */ +static void dsi_phy_tst_set(void __iomem *base, u32 reg, u32 val) +{ + writel(reg, base + PHY_TST_CTRL1); + /* reg addr written at first */ + wmb(); + writel(0x02, base + PHY_TST_CTRL0); + /* cmd1 sent for write */ + wmb(); + writel(0x00, base + PHY_TST_CTRL0); + /* cmd2 sent for write */ + wmb(); + writel(val, base + PHY_TST_CTRL1); + /* Then write data */ + wmb(); + writel(0x02, base + PHY_TST_CTRL0); + /* cmd2 sent for write */ + wmb(); + writel(0x00, base + PHY_TST_CTRL0); +} + +static void dsi_set_mipi_phy(void __iomem *base, + struct mipi_phy_params *phy, + u32 lanes) +{ + u32 delay_count; + u32 val; + u32 i; + + /* set lanes value */ + val = (lanes - 1) | (PHY_STOP_WAIT_TIME << 8); + writel(val, base + PHY_IF_CFG); + + /* set phy clk division */ + val = readl(base + CLKMGR_CFG) | phy->clk_division; + writel(val, base + CLKMGR_CFG); + + /* clean up phy set param */ + writel(0, base + PHY_RSTZ); + writel(0, base + PHY_TST_CTRL0); + writel(1, base + PHY_TST_CTRL0); + writel(0, base + PHY_TST_CTRL0); + + /* clock lane Timing control - TLPX */ + dsi_phy_tst_set(base, 0x10010, phy->clk_t_lpx); + + /* clock lane Timing control - THS-PREPARE */ + dsi_phy_tst_set(base, 0x10011, phy->clk_t_hs_prepare); + + /* clock lane Timing control - THS-ZERO */ + dsi_phy_tst_set(base, 0x10012, phy->clk_t_hs_zero); + + /* clock lane Timing control - THS-TRAIL */ + dsi_phy_tst_set(base, 0x10013, phy->clk_t_hs_trial); + + /* clock lane Timing control - TWAKEUP */ + dsi_phy_tst_set(base, 0x10014, phy->clk_t_wakeup); + + /* data lane */ + for (i = 0; i < lanes; i++) { + /* Timing control - TLPX */ + dsi_phy_tst_set(base, 0x10020 + (i << 4), phy->data_t_lpx); + + /* Timing control - THS-PREPARE */ + dsi_phy_tst_set(base, 0x10021 + (i << 4), + phy->data_t_hs_prepare); + + /* Timing control - THS-ZERO */ + dsi_phy_tst_set(base, 0x10022 + (i << 4), phy->data_t_hs_zero); + + /* Timing control - THS-TRAIL */ + dsi_phy_tst_set(base, 0x10023 + (i << 4), phy->data_t_hs_trial); + + /* Timing control - TTA-GO */ + dsi_phy_tst_set(base, 0x10024 + (i << 4), phy->data_t_ta_go); + + /* Timing control - TTA-GET */ + dsi_phy_tst_set(base, 0x10025 + (i << 4), phy->data_t_ta_get); + + /* Timing control - TWAKEUP */ + dsi_phy_tst_set(base, 0x10026 + (i << 4), phy->data_t_wakeup); + } + + /* physical configuration I */ + dsi_phy_tst_set(base, 0x10060, phy->hstx_ckg_sel); + + /* physical configuration pll II */ + val = (phy->pll_fbd_div5f << 5) + (phy->pll_fbd_div1f << 4) + + (phy->pll_fbd_2p << 1) + phy->pll_enbwt; + dsi_phy_tst_set(base, 0x10063, val); + + /* physical configuration pll II */ + dsi_phy_tst_set(base, 0x10064, phy->pll_fbd_p); + + /* physical configuration pll III */ + dsi_phy_tst_set(base, 0x10065, phy->pll_fbd_s); + + /* physical configuration pll IV */ + val = (phy->pll_pre_div1p << 7) + phy->pll_pre_p; + dsi_phy_tst_set(base, 0x10066, val); + + /* physical configuration pll V */ + val = (phy->pll_vco_750M << 4) + (phy->pll_lpf_rs << 2) + + phy->pll_lpf_cs + BIT(5); + dsi_phy_tst_set(base, 0x10067, val); + + writel(BIT(2), base + PHY_RSTZ); + udelay(1); + writel(BIT(2) | BIT(0), base + PHY_RSTZ); + udelay(1); + writel(BIT(2) | BIT(1) | BIT(0), base + PHY_RSTZ); + usleep_range(1000, 1500); + + /* wait for phy's clock ready */ + delay_count = 100; + while (delay_count--) { + val = readl(base + PHY_STATUS); + if ((BIT(0) | BIT(2)) & val) + break; + + udelay(1); + } + + if (!delay_count) + DRM_INFO("phylock and phystopstateclklane is not ready.\n"); + + /* set lp and hs switching params + */ + dw_update_bits(base + PHY_TMR_CFG, 24, MASK(8), phy->hs2lp_time); + dw_update_bits(base + PHY_TMR_CFG, 16, MASK(8), phy->lp2hs_time); + dw_update_bits(base + PHY_TMR_LPCLK_CFG, 16, MASK(10), + phy->clkhs2lp_time); + dw_update_bits(base + PHY_TMR_LPCLK_CFG, 0, MASK(10), + phy->clklp2hs_time); + dw_update_bits(base + CLK_DATA_TMR_CFG, 8, MASK(8), + phy->data_to_clk_delay); + dw_update_bits(base + CLK_DATA_TMR_CFG, 0, MASK(8), + phy->clk_to_data_delay); +} + +static void dsi_set_mode_timing(void __iomem *base, + u32 lane_byte_clk_kHz, + struct drm_display_mode *mode, + enum mipi_dsi_pixel_format format) +{ + u32 hfp, hbp, hsw, vfp, vbp, vsw; + u32 hline_time; + u32 hsa_time; + u32 hbp_time; + u32 pixel_clk_kHz; + int htot, vtot; + u32 val; + u64 tmp; + + /* DSI color coding setting */ + val = dsi_get_dpi_color_coding(format); + writel(val, base + DPI_COLOR_CODING); + + /* DSI format and pol setting */ + val = (mode->flags & DRM_MODE_FLAG_NHSYNC ? 1 : 0) << 2; + val |= (mode->flags & DRM_MODE_FLAG_NVSYNC ? 1 : 0) << 1; + writel(val, base + DPI_CFG_POL); + + /* + * The DSI IP accepts vertical timing using lines as normal, + * but horizontal timing is a mixture of pixel-clocks for the + * active region and byte-lane clocks for the blanking-related + * timings. hfp is specified as the total hline_time in byte- + * lane clocks minus hsa, hbp and active. + */ + pixel_clk_kHz = mode->clock; + htot = mode->htotal; + vtot = mode->vtotal; + hfp = mode->hsync_start - mode->hdisplay; + hbp = mode->htotal - mode->hsync_end; + hsw = mode->hsync_end - mode->hsync_start; + vfp = mode->vsync_start - mode->vdisplay; + vbp = mode->vtotal - mode->vsync_end; + vsw = mode->vsync_end - mode->vsync_start; + if (vsw > 15) { + DRM_DEBUG_DRIVER("vsw exceeded 15\n"); + vsw = 15; + } + + hsa_time = (hsw * lane_byte_clk_kHz) / pixel_clk_kHz; + hbp_time = (hbp * lane_byte_clk_kHz) / pixel_clk_kHz; + tmp = (u64)htot * (u64)lane_byte_clk_kHz; + hline_time = DIV_ROUND_UP(tmp, pixel_clk_kHz); + + /* all specified in byte-lane clocks */ + writel(hsa_time, base + VID_HSA_TIME); + writel(hbp_time, base + VID_HBP_TIME); + writel(hline_time, base + VID_HLINE_TIME); + + writel(vsw, base + VID_VSA_LINES); + writel(vbp, base + VID_VBP_LINES); + writel(vfp, base + VID_VFP_LINES); + writel(mode->vdisplay, base + VID_VACTIVE_LINES); + writel(mode->hdisplay, base + VID_PKT_SIZE); + + DRM_DEBUG_DRIVER("htot=%d, hfp=%d, hbp=%d, hsw=%d\n", + htot, hfp, hbp, hsw); + DRM_DEBUG_DRIVER("vtol=%d, vfp=%d, vbp=%d, vsw=%d\n", + vtot, vfp, vbp, vsw); + DRM_DEBUG_DRIVER("hsa_time=%d, hbp_time=%d, hline_time=%d\n", + hsa_time, hbp_time, hline_time); +} + +static void dsi_set_video_mode_type(void __iomem *base, + unsigned long flags) +{ + u32 val; + u32 mode_mask = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | + MIPI_DSI_MODE_VIDEO_SYNC_PULSE; + u32 non_burst_sync_pulse = MIPI_DSI_MODE_VIDEO | + MIPI_DSI_MODE_VIDEO_SYNC_PULSE; + u32 non_burst_sync_event = MIPI_DSI_MODE_VIDEO; + + /* + * choose video type + */ + if ((flags & mode_mask) == non_burst_sync_pulse) + val = DSI_NON_BURST_SYNC_PULSES; + else if ((flags & mode_mask) == non_burst_sync_event) + val = DSI_NON_BURST_SYNC_EVENTS; + else + val = DSI_BURST_SYNC_PULSES_1; + + writel(val, base + VID_MODE_CFG); +} + +static void dsi_mipi_init(struct dw_dsi *dsi) +{ + struct dsi_hw_ctx *ctx = dsi->ctx; + struct mipi_phy_params *phy = &dsi->phy; + struct drm_display_mode *mode = &dsi->cur_mode; + u32 bpp = mipi_dsi_pixel_format_to_bpp(dsi->format); + void __iomem *base = ctx->base; + u32 dphy_freq_kHz; + + /* count phy params */ + dphy_freq_kHz = mode->clock * bpp / dsi->lanes; + dsi_get_phy_params(dphy_freq_kHz, phy); + + /* reset Core */ + writel(0, base + PWR_UP); + + /* set phy params */ + dsi_set_mipi_phy(base, phy, dsi->lanes); + + /* set dsi mode */ + dsi_set_mode_timing(base, phy->lane_byte_clk_kHz, mode, dsi->format); + + /* set video mode type and lp */ + dsi_set_video_mode_type(base, dsi->mode_flags); + + /* dsi wake up */ + writel(DSI_VIDEO_MODE, base + MODE_CFG); + writel(BIT(0), base + LPCLK_CTRL); + writel(BIT(0), base + PWR_UP); + + DRM_DEBUG_DRIVER("lanes=%d, pixel_clk=%d kHz, bytes_freq=%d kHz\n", + dsi->lanes, mode->clock, phy->lane_byte_clk_kHz); +} + +static void dsi_encoder_disable(struct drm_encoder *encoder) +{ + struct dw_dsi *dsi = encoder_to_dsi(encoder); + struct dsi_hw_ctx *ctx = dsi->ctx; + void __iomem *base = ctx->base; + + if (!dsi->enable) + return; + + writel(0, base + PWR_UP); + writel(0, base + LPCLK_CTRL); + writel(0, base + PHY_RSTZ); + clk_disable_unprepare(ctx->pclk); + + dsi->enable = false; +} + +static void dsi_encoder_enable(struct drm_encoder *encoder) +{ + struct dw_dsi *dsi = encoder_to_dsi(encoder); + struct dsi_hw_ctx *ctx = dsi->ctx; + int ret; + + if (dsi->enable) + return; + + /* mipi dphy clock enable */ + ret = clk_prepare_enable(ctx->pclk); + if (ret) { + DRM_ERROR("fail to enable pclk: %d\n", ret); + return; + } + + dsi_mipi_init(dsi); + + dsi->enable = true; +} + +static void dsi_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adj_mode) +{ + struct dw_dsi *dsi = encoder_to_dsi(encoder); + + drm_mode_copy(&dsi->cur_mode, adj_mode); +} + +static int dsi_encoder_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + /* do nothing */ + return 0; +} + +static const struct drm_encoder_helper_funcs dw_encoder_helper_funcs = { + .atomic_check = dsi_encoder_atomic_check, + .mode_set = dsi_encoder_mode_set, + .enable = dsi_encoder_enable, + .disable = dsi_encoder_disable +}; + +static const struct drm_encoder_funcs dw_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +static int dw_drm_encoder_init(struct device *dev, + struct drm_device *drm_dev, + struct drm_encoder *encoder) +{ + int ret; + u32 crtc_mask = drm_of_find_possible_crtcs(drm_dev, dev->of_node); + + if (!crtc_mask) { + DRM_ERROR("failed to find crtc mask\n"); + return -EINVAL; + } + + encoder->possible_crtcs = crtc_mask; + ret = drm_encoder_init(drm_dev, encoder, &dw_encoder_funcs, + DRM_MODE_ENCODER_DSI, NULL); + if (ret) { + DRM_ERROR("failed to init dsi encoder\n"); + return ret; + } + + drm_encoder_helper_add(encoder, &dw_encoder_helper_funcs); + + return 0; +} + +static int dsi_bind(struct device *dev, struct device *master, void *data) +{ + struct dsi_data *ddata = dev_get_drvdata(dev); + struct dw_dsi *dsi = &ddata->dsi; + struct drm_device *drm_dev = data; + int ret; + + ret = dw_drm_encoder_init(dev, drm_dev, &dsi->encoder); + if (ret) + return ret; + + return 0; +} + +static void dsi_unbind(struct device *dev, struct device *master, void *data) +{ + /* do nothing */ +} + +static const struct component_ops dsi_ops = { + .bind = dsi_bind, + .unbind = dsi_unbind, +}; + +static int dsi_parse_dt(struct platform_device *pdev, struct dw_dsi *dsi) +{ + struct dsi_hw_ctx *ctx = dsi->ctx; + struct resource *res; + + ctx->pclk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(ctx->pclk)) { + DRM_ERROR("failed to get pclk clock\n"); + return PTR_ERR(ctx->pclk); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ctx->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ctx->base)) { + DRM_ERROR("failed to remap dsi io region\n"); + return PTR_ERR(ctx->base); + } + + return 0; +} + +static int dsi_probe(struct platform_device *pdev) +{ + struct dsi_data *data; + struct dw_dsi *dsi; + struct dsi_hw_ctx *ctx; + int ret; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) { + DRM_ERROR("failed to allocate dsi data.\n"); + return -ENOMEM; + } + dsi = &data->dsi; + ctx = &data->ctx; + dsi->ctx = ctx; + + ret = dsi_parse_dt(pdev, dsi); + if (ret) + return ret; + + platform_set_drvdata(pdev, data); + + return component_add(&pdev->dev, &dsi_ops); +} + +static int dsi_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &dsi_ops); + + return 0; +} + +static const struct of_device_id dsi_of_match[] = { + {.compatible = "hisilicon,hi6220-dsi"}, + { } +}; +MODULE_DEVICE_TABLE(of, dsi_of_match); + +static struct platform_driver dsi_driver = { + .probe = dsi_probe, + .remove = dsi_remove, + .driver = { + .name = "dw-dsi", + .of_match_table = dsi_of_match, + }, +}; + +module_platform_driver(dsi_driver); + +MODULE_AUTHOR("Xinliang Liu xinliang.liu@linaro.org"); +MODULE_AUTHOR("Xinliang Liu z.liuxinliang@hisilicon.com"); +MODULE_AUTHOR("Xinwei Kong kong.kongxinwei@hisilicon.com"); +MODULE_DESCRIPTION("DesignWare MIPI DSI Host Controller v1.02 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/hisilicon/kirin/dw_dsi_reg.h b/drivers/gpu/drm/hisilicon/kirin/dw_dsi_reg.h new file mode 100644 index 000000000000..d3d44b7d2098 --- /dev/null +++ b/drivers/gpu/drm/hisilicon/kirin/dw_dsi_reg.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2016 Linaro Limited. + * Copyright (c) 2014-2016 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef __DW_DSI_REG_H__ +#define __DW_DSI_REG_H__ + +#define MASK(x) (BIT(x) - 1) + +/* + * regs + */ +#define PWR_UP 0x04 /* Core power-up */ +#define PHY_IF_CFG 0xA4 /* D-PHY interface configuration */ +#define CLKMGR_CFG 0x08 /* the internal clock dividers */ +#define PHY_RSTZ 0xA0 /* D-PHY reset control */ +#define PHY_TST_CTRL0 0xB4 /* D-PHY test interface control 0 */ +#define PHY_TST_CTRL1 0xB8 /* D-PHY test interface control 1 */ +#define DPI_VCID 0x0C /* DPI virtual channel id */ +#define DPI_COLOR_CODING 0x10 /* DPI color coding */ +#define DPI_CFG_POL 0x14 /* DPI polarity configuration */ +#define VID_HSA_TIME 0x48 /* Horizontal Sync Active time */ +#define VID_HBP_TIME 0x4C /* Horizontal Back Porch time */ +#define VID_HLINE_TIME 0x50 /* Line time */ +#define VID_VSA_LINES 0x54 /* Vertical Sync Active period */ +#define VID_VBP_LINES 0x58 /* Vertical Back Porch period */ +#define VID_VFP_LINES 0x5C /* Vertical Front Porch period */ +#define VID_VACTIVE_LINES 0x60 /* Vertical resolution */ +#define VID_PKT_SIZE 0x3C /* Video packet size */ +#define VID_MODE_CFG 0x38 /* Video mode configuration */ +#define DPI_LP_CMD_TIM 0x18 /* Low-power command timing config */ +#define PHY_TMR_CFG 0x9C /* Data lanes timing configuration */ +#define BTA_TO_CNT 0x8C /* Response timeout definition */ +#define PHY_TMR_LPCLK_CFG 0x98 /* clock lane timing configuration */ +#define CLK_DATA_TMR_CFG 0xCC +#define LPCLK_CTRL 0x94 /* Low-power in clock lane */ +#define PCKHDL_CFG 0x2C /* Packet handler configuration */ +#define EDPI_CMD_SIZE 0x64 /* Size for eDPI packets */ +#define MODE_CFG 0x34 /* Video or Command mode selection */ +#define PHY_STATUS 0xB0 /* D-PHY PPI status interface */ + +#define PHY_STOP_WAIT_TIME 0x30 + +/* + * regs relevant enum + */ +enum dpi_color_coding { + DSI_24BITS_1 = 5, +}; + +enum dsi_video_mode_type { + DSI_NON_BURST_SYNC_PULSES = 0, + DSI_NON_BURST_SYNC_EVENTS, + DSI_BURST_SYNC_PULSES_1, + DSI_BURST_SYNC_PULSES_2 +}; + +enum dsi_work_mode { + DSI_VIDEO_MODE = 0, + DSI_COMMAND_MODE +}; + +/* + * Register Write/Read Helper functions + */ +static inline void dw_update_bits(void __iomem *addr, u32 bit_start, + u32 mask, u32 val) +{ + u32 tmp, orig; + + orig = readl(addr); + tmp = orig & ~(mask << bit_start); + tmp |= (val & mask) << bit_start; + writel(tmp, addr); +} + +#endif /* __DW_DRM_DSI_H__ */
On 2/26/2016 2:10 PM, Xinliang Liu wrote:
Add DesignWare MIPI DSI Host Controller v1.02 encoder driver for hi6220 SoC.
v6:
- Change "pclk_dsi" to "pclk".
v5: None. v4: None. v3:
- Rename file name to dw_drm_dsi.c
- Make encoder type as DRM_MODE_ENCODER_DSI.
- A few cleanup.
v2:
- Remove abtraction layer.
Signed-off-by: Xinliang Liu xinliang.liu@linaro.org Signed-off-by: Xinwei Kong kong.kongxinwei@hisilicon.com Signed-off-by: Andy Green andy.green@linaro.org
drivers/gpu/drm/hisilicon/kirin/Kconfig | 1 + drivers/gpu/drm/hisilicon/kirin/Makefile | 3 +- drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c | 743 +++++++++++++++++++++++++++ drivers/gpu/drm/hisilicon/kirin/dw_dsi_reg.h | 83 +++ 4 files changed, 829 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c create mode 100644 drivers/gpu/drm/hisilicon/kirin/dw_dsi_reg.h
diff --git a/drivers/gpu/drm/hisilicon/kirin/Kconfig b/drivers/gpu/drm/hisilicon/kirin/Kconfig index 3ac4b8edeac1..de0d454c5c13 100644 --- a/drivers/gpu/drm/hisilicon/kirin/Kconfig +++ b/drivers/gpu/drm/hisilicon/kirin/Kconfig @@ -4,6 +4,7 @@ config DRM_HISI_KIRIN select DRM_KMS_HELPER select DRM_GEM_CMA_HELPER select DRM_KMS_CMA_HELPER
- select DRM_MIPI_DSI help Choose this option if you have a hisilicon Kirin chipsets(hi6220). If M is selected the module will be called kirin-drm.
diff --git a/drivers/gpu/drm/hisilicon/kirin/Makefile b/drivers/gpu/drm/hisilicon/kirin/Makefile index 2a61ab006ddb..5dcd0d4328b6 100644 --- a/drivers/gpu/drm/hisilicon/kirin/Makefile +++ b/drivers/gpu/drm/hisilicon/kirin/Makefile @@ -1,4 +1,5 @@ kirin-drm-y := kirin_drm_drv.o \
kirin_drm_ade.o
kirin_drm_ade.o \
dw_drm_dsi.o
obj-$(CONFIG_DRM_HISI_KIRIN) += kirin-drm.o
diff --git a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c new file mode 100644 index 000000000000..8329148cc89d --- /dev/null +++ b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c @@ -0,0 +1,743 @@ +/*
- DesignWare MIPI DSI Host Controller v1.02 driver
- Copyright (c) 2016 Linaro Limited.
- Copyright (c) 2014-2016 Hisilicon Limited.
- Author:
- Xinliang Liu z.liuxinliang@hisilicon.com
- Xinliang Liu xinliang.liu@linaro.org
- Xinwei Kong kong.kongxinwei@hisilicon.com
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 as
- published by the Free Software Foundation.
- */
+#include <linux/clk.h> +#include <linux/component.h> +#include <linux/of_graph.h>
+#include <drm/drm_of.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_encoder_slave.h> +#include <drm/drm_atomic_helper.h>
+#include "dw_dsi_reg.h"
+#define MAX_TX_ESC_CLK (10) +#define ROUND(x, y) ((x) / (y) + ((x) % (y) * 10 / (y) >= 5 ? 1 : 0)) +#define PHY_REF_CLK_RATE 19200000 +#define PHY_REF_CLK_PERIOD_PS (1000000000 / (PHY_REF_CLK_RATE / 1000))
+#define encoder_to_dsi(encoder) \
- container_of(encoder, struct dw_dsi, encoder)
+#define host_to_dsi(host) \
- container_of(host, struct dw_dsi, host)
+struct mipi_phy_params {
- u32 clk_t_lpx;
- u32 clk_t_hs_prepare;
- u32 clk_t_hs_zero;
- u32 clk_t_hs_trial;
- u32 clk_t_wakeup;
- u32 data_t_lpx;
- u32 data_t_hs_prepare;
- u32 data_t_hs_zero;
- u32 data_t_hs_trial;
- u32 data_t_ta_go;
- u32 data_t_ta_get;
- u32 data_t_wakeup;
- u32 hstx_ckg_sel;
- u32 pll_fbd_div5f;
- u32 pll_fbd_div1f;
- u32 pll_fbd_2p;
- u32 pll_enbwt;
- u32 pll_fbd_p;
- u32 pll_fbd_s;
- u32 pll_pre_div1p;
- u32 pll_pre_p;
- u32 pll_vco_750M;
- u32 pll_lpf_rs;
- u32 pll_lpf_cs;
- u32 clklp2hs_time;
- u32 clkhs2lp_time;
- u32 lp2hs_time;
- u32 hs2lp_time;
- u32 clk_to_data_delay;
- u32 data_to_clk_delay;
- u32 lane_byte_clk_kHz;
- u32 clk_division;
+};
+struct dsi_hw_ctx {
- void __iomem *base;
- struct clk *pclk;
+};
+struct dw_dsi {
- struct drm_encoder encoder;
- struct drm_display_mode cur_mode;
- struct dsi_hw_ctx *ctx;
- struct mipi_phy_params phy;
- u32 lanes;
- enum mipi_dsi_pixel_format format;
- unsigned long mode_flags;
- bool enable;
+};
+struct dsi_data {
- struct dw_dsi dsi;
- struct dsi_hw_ctx ctx;
+};
+struct dsi_phy_range {
- u32 min_range_kHz;
- u32 max_range_kHz;
- u32 pll_vco_750M;
- u32 hstx_ckg_sel;
+};
+static const struct dsi_phy_range dphy_range_info[] = {
- { 46875, 62500, 1, 7 },
- { 62500, 93750, 0, 7 },
- { 93750, 125000, 1, 6 },
- { 125000, 187500, 0, 6 },
- { 187500, 250000, 1, 5 },
- { 250000, 375000, 0, 5 },
- { 375000, 500000, 1, 4 },
- { 500000, 750000, 0, 4 },
- { 750000, 1000000, 1, 0 },
- { 1000000, 1500000, 0, 0 }
+};
+static void dsi_get_phy_params(u32 phy_freq_kHz,
struct mipi_phy_params *phy)
+{
- u32 ui = 0;
- u32 cfg_clk_ps = PHY_REF_CLK_PERIOD_PS;
- u32 i = 0;
- u32 q_pll = 1;
- u32 m_pll = 0;
- u32 n_pll = 0;
- u32 r_pll = 1;
- u32 m_n = 0;
- u32 m_n_int = 0;
- u64 f_kHz;
- u64 temp;
- u64 tmp_kHz = phy_freq_kHz;
- do {
f_kHz = tmp_kHz;
/* Find the PLL clock range from the table */
for (i = 0; i < ARRAY_SIZE(dphy_range_info); i++)
if (f_kHz >= dphy_range_info[i].min_range_kHz &&
f_kHz <= dphy_range_info[i].max_range_kHz)
break;
if (i == ARRAY_SIZE(dphy_range_info)) {
DRM_ERROR("%lldkHz out of range\n", f_kHz);
return;
}
phy->pll_vco_750M = dphy_range_info[i].pll_vco_750M;
phy->hstx_ckg_sel = dphy_range_info[i].hstx_ckg_sel;
if (phy->hstx_ckg_sel <= 7 &&
phy->hstx_ckg_sel >= 4)
q_pll = 0x10 >> (7 - phy->hstx_ckg_sel);
temp = f_kHz * (u64)q_pll * (u64)cfg_clk_ps;
m_n_int = temp / (u64)1000000000;
m_n = (temp % (u64)1000000000) / (u64)100000000;
if (m_n_int % 2 == 0) {
if (m_n * 6 >= 50) {
n_pll = 2;
m_pll = (m_n_int + 1) * n_pll;
} else if (m_n * 6 >= 30) {
n_pll = 3;
m_pll = m_n_int * n_pll + 2;
} else {
n_pll = 1;
m_pll = m_n_int * n_pll;
}
} else {
if (m_n * 6 >= 50) {
n_pll = 1;
m_pll = (m_n_int + 1) * n_pll;
} else if (m_n * 6 >= 30) {
n_pll = 1;
m_pll = (m_n_int + 1) * n_pll;
} else if (m_n * 6 >= 10) {
n_pll = 3;
m_pll = m_n_int * n_pll + 1;
} else {
n_pll = 2;
m_pll = m_n_int * n_pll;
}
}
if (n_pll == 1) {
phy->pll_fbd_p = 0;
phy->pll_pre_div1p = 1;
} else {
phy->pll_fbd_p = n_pll;
phy->pll_pre_div1p = 0;
}
if (phy->pll_fbd_2p <= 7 && phy->pll_fbd_2p >= 4)
r_pll = 0x10 >> (7 - phy->pll_fbd_2p);
if (m_pll == 2) {
phy->pll_pre_p = 0;
phy->pll_fbd_s = 0;
phy->pll_fbd_div1f = 0;
phy->pll_fbd_div5f = 1;
} else if (m_pll >= 2 * 2 * r_pll && m_pll <= 2 * 4 * r_pll) {
phy->pll_pre_p = m_pll / (2 * r_pll);
phy->pll_fbd_s = 0;
phy->pll_fbd_div1f = 1;
phy->pll_fbd_div5f = 0;
} else if (m_pll >= 2 * 5 * r_pll && m_pll <= 2 * 150 * r_pll) {
if (((m_pll / (2 * r_pll)) % 2) == 0) {
phy->pll_pre_p =
(m_pll / (2 * r_pll)) / 2 - 1;
phy->pll_fbd_s =
(m_pll / (2 * r_pll)) % 2 + 2;
} else {
phy->pll_pre_p =
(m_pll / (2 * r_pll)) / 2;
phy->pll_fbd_s =
(m_pll / (2 * r_pll)) % 2;
}
phy->pll_fbd_div1f = 0;
phy->pll_fbd_div5f = 0;
} else {
phy->pll_pre_p = 0;
phy->pll_fbd_s = 0;
phy->pll_fbd_div1f = 0;
phy->pll_fbd_div5f = 1;
}
f_kHz = (u64)1000000000 * (u64)m_pll /
((u64)cfg_clk_ps * (u64)n_pll * (u64)q_pll);
if (f_kHz >= phy_freq_kHz)
break;
tmp_kHz += 10;
- } while (1);
- ui = 1000000 / f_kHz;
- phy->clk_t_lpx = ROUND(50, 8 * ui);
- phy->clk_t_hs_prepare = ROUND(133, 16 * ui) - 1;
- phy->clk_t_hs_zero = ROUND(262, 8 * ui);
- phy->clk_t_hs_trial = 2 * (ROUND(60, 8 * ui) - 1);
- phy->clk_t_wakeup = ROUND(1000000, (cfg_clk_ps / 1000) - 1);
- if (phy->clk_t_wakeup > 0xff)
phy->clk_t_wakeup = 0xff;
- phy->data_t_wakeup = phy->clk_t_wakeup;
- phy->data_t_lpx = phy->clk_t_lpx;
- phy->data_t_hs_prepare = ROUND(125 + 10 * ui, 16 * ui) - 1;
- phy->data_t_hs_zero = ROUND(105 + 6 * ui, 8 * ui);
- phy->data_t_hs_trial = 2 * (ROUND(60 + 4 * ui, 8 * ui) - 1);
- phy->data_t_ta_go = 3;
- phy->data_t_ta_get = 4;
- phy->pll_enbwt = 1;
- phy->clklp2hs_time = ROUND(407, 8 * ui) + 12;
- phy->clkhs2lp_time = ROUND(105 + 12 * ui, 8 * ui);
- phy->lp2hs_time = ROUND(240 + 12 * ui, 8 * ui) + 1;
- phy->hs2lp_time = phy->clkhs2lp_time;
- phy->clk_to_data_delay = 1 + phy->clklp2hs_time;
- phy->data_to_clk_delay = ROUND(60 + 52 * ui, 8 * ui) +
phy->clkhs2lp_time;
- phy->lane_byte_clk_kHz = f_kHz / 8;
- phy->clk_division = phy->lane_byte_clk_kHz / MAX_TX_ESC_CLK;
- if (phy->lane_byte_clk_kHz % MAX_TX_ESC_CLK)
phy->clk_division++;
+}
+static u32 dsi_get_dpi_color_coding(enum mipi_dsi_pixel_format format) +{
- u32 val;
- /* TODO: only support RGB888 now, to support more */
- switch (format) {
- case MIPI_DSI_FMT_RGB888:
val = DSI_24BITS_1;
break;
- default:
val = DSI_24BITS_1;
break;
- }
- return val;
+}
+/*
- dsi phy reg write function
- */
+static void dsi_phy_tst_set(void __iomem *base, u32 reg, u32 val) +{
- writel(reg, base + PHY_TST_CTRL1);
- /* reg addr written at first */
- wmb();
- writel(0x02, base + PHY_TST_CTRL0);
- /* cmd1 sent for write */
- wmb();
- writel(0x00, base + PHY_TST_CTRL0);
- /* cmd2 sent for write */
- wmb();
- writel(val, base + PHY_TST_CTRL1);
- /* Then write data */
- wmb();
- writel(0x02, base + PHY_TST_CTRL0);
- /* cmd2 sent for write */
- wmb();
- writel(0x00, base + PHY_TST_CTRL0);
You can use iowrite32 here to prevent calling wmb before each write.
+}
+static void dsi_set_mipi_phy(void __iomem *base,
struct mipi_phy_params *phy,
u32 lanes)
+{
- u32 delay_count;
- u32 val;
- u32 i;
- /* set lanes value */
- val = (lanes - 1) | (PHY_STOP_WAIT_TIME << 8);
- writel(val, base + PHY_IF_CFG);
- /* set phy clk division */
- val = readl(base + CLKMGR_CFG) | phy->clk_division;
- writel(val, base + CLKMGR_CFG);
- /* clean up phy set param */
- writel(0, base + PHY_RSTZ);
- writel(0, base + PHY_TST_CTRL0);
- writel(1, base + PHY_TST_CTRL0);
- writel(0, base + PHY_TST_CTRL0);
- /* clock lane Timing control - TLPX */
- dsi_phy_tst_set(base, 0x10010, phy->clk_t_lpx);
- /* clock lane Timing control - THS-PREPARE */
- dsi_phy_tst_set(base, 0x10011, phy->clk_t_hs_prepare);
- /* clock lane Timing control - THS-ZERO */
- dsi_phy_tst_set(base, 0x10012, phy->clk_t_hs_zero);
- /* clock lane Timing control - THS-TRAIL */
- dsi_phy_tst_set(base, 0x10013, phy->clk_t_hs_trial);
- /* clock lane Timing control - TWAKEUP */
- dsi_phy_tst_set(base, 0x10014, phy->clk_t_wakeup);
- /* data lane */
- for (i = 0; i < lanes; i++) {
/* Timing control - TLPX */
dsi_phy_tst_set(base, 0x10020 + (i << 4), phy->data_t_lpx);
Some macros here for these PHY registers would be nice.
/* Timing control - THS-PREPARE */
dsi_phy_tst_set(base, 0x10021 + (i << 4),
phy->data_t_hs_prepare);
/* Timing control - THS-ZERO */
dsi_phy_tst_set(base, 0x10022 + (i << 4), phy->data_t_hs_zero);
/* Timing control - THS-TRAIL */
dsi_phy_tst_set(base, 0x10023 + (i << 4), phy->data_t_hs_trial);
/* Timing control - TTA-GO */
dsi_phy_tst_set(base, 0x10024 + (i << 4), phy->data_t_ta_go);
/* Timing control - TTA-GET */
dsi_phy_tst_set(base, 0x10025 + (i << 4), phy->data_t_ta_get);
/* Timing control - TWAKEUP */
dsi_phy_tst_set(base, 0x10026 + (i << 4), phy->data_t_wakeup);
Something like the macro below would be more readable here:
#define PHY_TWAKEUP(i) (0x10026 + (i << 4))
Looks good otherwise.
Archit
Hi,
On 1 March 2016 at 02:49, Archit Taneja architt@codeaurora.org wrote:
On 2/26/2016 2:10 PM, Xinliang Liu wrote:
Add DesignWare MIPI DSI Host Controller v1.02 encoder driver for hi6220 SoC.
v6:
- Change "pclk_dsi" to "pclk".
v5: None. v4: None. v3:
- Rename file name to dw_drm_dsi.c
- Make encoder type as DRM_MODE_ENCODER_DSI.
- A few cleanup.
v2:
- Remove abtraction layer.
Signed-off-by: Xinliang Liu xinliang.liu@linaro.org Signed-off-by: Xinwei Kong kong.kongxinwei@hisilicon.com Signed-off-by: Andy Green andy.green@linaro.org
drivers/gpu/drm/hisilicon/kirin/Kconfig | 1 + drivers/gpu/drm/hisilicon/kirin/Makefile | 3 +- drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c | 743 +++++++++++++++++++++++++++ drivers/gpu/drm/hisilicon/kirin/dw_dsi_reg.h | 83 +++ 4 files changed, 829 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c create mode 100644 drivers/gpu/drm/hisilicon/kirin/dw_dsi_reg.h
diff --git a/drivers/gpu/drm/hisilicon/kirin/Kconfig b/drivers/gpu/drm/hisilicon/kirin/Kconfig index 3ac4b8edeac1..de0d454c5c13 100644 --- a/drivers/gpu/drm/hisilicon/kirin/Kconfig +++ b/drivers/gpu/drm/hisilicon/kirin/Kconfig @@ -4,6 +4,7 @@ config DRM_HISI_KIRIN select DRM_KMS_HELPER select DRM_GEM_CMA_HELPER select DRM_KMS_CMA_HELPER
select DRM_MIPI_DSI help Choose this option if you have a hisilicon Kirin
chipsets(hi6220). If M is selected the module will be called kirin-drm. diff --git a/drivers/gpu/drm/hisilicon/kirin/Makefile b/drivers/gpu/drm/hisilicon/kirin/Makefile index 2a61ab006ddb..5dcd0d4328b6 100644 --- a/drivers/gpu/drm/hisilicon/kirin/Makefile +++ b/drivers/gpu/drm/hisilicon/kirin/Makefile @@ -1,4 +1,5 @@ kirin-drm-y := kirin_drm_drv.o \
kirin_drm_ade.o
kirin_drm_ade.o \
dw_drm_dsi.o
obj-$(CONFIG_DRM_HISI_KIRIN) += kirin-drm.o
diff --git a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c new file mode 100644 index 000000000000..8329148cc89d --- /dev/null +++ b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c @@ -0,0 +1,743 @@ +/*
- DesignWare MIPI DSI Host Controller v1.02 driver
- Copyright (c) 2016 Linaro Limited.
- Copyright (c) 2014-2016 Hisilicon Limited.
- Author:
Xinliang Liu <z.liuxinliang@hisilicon.com>
Xinliang Liu <xinliang.liu@linaro.org>
Xinwei Kong <kong.kongxinwei@hisilicon.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 as
- published by the Free Software Foundation.
- */
+#include <linux/clk.h> +#include <linux/component.h> +#include <linux/of_graph.h>
+#include <drm/drm_of.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_encoder_slave.h> +#include <drm/drm_atomic_helper.h>
+#include "dw_dsi_reg.h"
+#define MAX_TX_ESC_CLK (10) +#define ROUND(x, y) ((x) / (y) + ((x) % (y) * 10 / (y) >= 5 ? 1 : 0)) +#define PHY_REF_CLK_RATE 19200000 +#define PHY_REF_CLK_PERIOD_PS (1000000000 / (PHY_REF_CLK_RATE / 1000))
+#define encoder_to_dsi(encoder) \
container_of(encoder, struct dw_dsi, encoder)
+#define host_to_dsi(host) \
container_of(host, struct dw_dsi, host)
+struct mipi_phy_params {
u32 clk_t_lpx;
u32 clk_t_hs_prepare;
u32 clk_t_hs_zero;
u32 clk_t_hs_trial;
u32 clk_t_wakeup;
u32 data_t_lpx;
u32 data_t_hs_prepare;
u32 data_t_hs_zero;
u32 data_t_hs_trial;
u32 data_t_ta_go;
u32 data_t_ta_get;
u32 data_t_wakeup;
u32 hstx_ckg_sel;
u32 pll_fbd_div5f;
u32 pll_fbd_div1f;
u32 pll_fbd_2p;
u32 pll_enbwt;
u32 pll_fbd_p;
u32 pll_fbd_s;
u32 pll_pre_div1p;
u32 pll_pre_p;
u32 pll_vco_750M;
u32 pll_lpf_rs;
u32 pll_lpf_cs;
u32 clklp2hs_time;
u32 clkhs2lp_time;
u32 lp2hs_time;
u32 hs2lp_time;
u32 clk_to_data_delay;
u32 data_to_clk_delay;
u32 lane_byte_clk_kHz;
u32 clk_division;
+};
+struct dsi_hw_ctx {
void __iomem *base;
struct clk *pclk;
+};
+struct dw_dsi {
struct drm_encoder encoder;
struct drm_display_mode cur_mode;
struct dsi_hw_ctx *ctx;
struct mipi_phy_params phy;
u32 lanes;
enum mipi_dsi_pixel_format format;
unsigned long mode_flags;
bool enable;
+};
+struct dsi_data {
struct dw_dsi dsi;
struct dsi_hw_ctx ctx;
+};
+struct dsi_phy_range {
u32 min_range_kHz;
u32 max_range_kHz;
u32 pll_vco_750M;
u32 hstx_ckg_sel;
+};
+static const struct dsi_phy_range dphy_range_info[] = {
{ 46875, 62500, 1, 7 },
{ 62500, 93750, 0, 7 },
{ 93750, 125000, 1, 6 },
{ 125000, 187500, 0, 6 },
{ 187500, 250000, 1, 5 },
{ 250000, 375000, 0, 5 },
{ 375000, 500000, 1, 4 },
{ 500000, 750000, 0, 4 },
{ 750000, 1000000, 1, 0 },
{ 1000000, 1500000, 0, 0 }
+};
+static void dsi_get_phy_params(u32 phy_freq_kHz,
struct mipi_phy_params *phy)
+{
u32 ui = 0;
u32 cfg_clk_ps = PHY_REF_CLK_PERIOD_PS;
u32 i = 0;
u32 q_pll = 1;
u32 m_pll = 0;
u32 n_pll = 0;
u32 r_pll = 1;
u32 m_n = 0;
u32 m_n_int = 0;
u64 f_kHz;
u64 temp;
u64 tmp_kHz = phy_freq_kHz;
do {
f_kHz = tmp_kHz;
/* Find the PLL clock range from the table */
for (i = 0; i < ARRAY_SIZE(dphy_range_info); i++)
if (f_kHz >= dphy_range_info[i].min_range_kHz &&
f_kHz <= dphy_range_info[i].max_range_kHz)
break;
if (i == ARRAY_SIZE(dphy_range_info)) {
DRM_ERROR("%lldkHz out of range\n", f_kHz);
return;
}
phy->pll_vco_750M = dphy_range_info[i].pll_vco_750M;
phy->hstx_ckg_sel = dphy_range_info[i].hstx_ckg_sel;
if (phy->hstx_ckg_sel <= 7 &&
phy->hstx_ckg_sel >= 4)
q_pll = 0x10 >> (7 - phy->hstx_ckg_sel);
temp = f_kHz * (u64)q_pll * (u64)cfg_clk_ps;
m_n_int = temp / (u64)1000000000;
m_n = (temp % (u64)1000000000) / (u64)100000000;
if (m_n_int % 2 == 0) {
if (m_n * 6 >= 50) {
n_pll = 2;
m_pll = (m_n_int + 1) * n_pll;
} else if (m_n * 6 >= 30) {
n_pll = 3;
m_pll = m_n_int * n_pll + 2;
} else {
n_pll = 1;
m_pll = m_n_int * n_pll;
}
} else {
if (m_n * 6 >= 50) {
n_pll = 1;
m_pll = (m_n_int + 1) * n_pll;
} else if (m_n * 6 >= 30) {
n_pll = 1;
m_pll = (m_n_int + 1) * n_pll;
} else if (m_n * 6 >= 10) {
n_pll = 3;
m_pll = m_n_int * n_pll + 1;
} else {
n_pll = 2;
m_pll = m_n_int * n_pll;
}
}
if (n_pll == 1) {
phy->pll_fbd_p = 0;
phy->pll_pre_div1p = 1;
} else {
phy->pll_fbd_p = n_pll;
phy->pll_pre_div1p = 0;
}
if (phy->pll_fbd_2p <= 7 && phy->pll_fbd_2p >= 4)
r_pll = 0x10 >> (7 - phy->pll_fbd_2p);
if (m_pll == 2) {
phy->pll_pre_p = 0;
phy->pll_fbd_s = 0;
phy->pll_fbd_div1f = 0;
phy->pll_fbd_div5f = 1;
} else if (m_pll >= 2 * 2 * r_pll && m_pll <= 2 * 4 *
r_pll) {
phy->pll_pre_p = m_pll / (2 * r_pll);
phy->pll_fbd_s = 0;
phy->pll_fbd_div1f = 1;
phy->pll_fbd_div5f = 0;
} else if (m_pll >= 2 * 5 * r_pll && m_pll <= 2 * 150 *
r_pll) {
if (((m_pll / (2 * r_pll)) % 2) == 0) {
phy->pll_pre_p =
(m_pll / (2 * r_pll)) / 2 - 1;
phy->pll_fbd_s =
(m_pll / (2 * r_pll)) % 2 + 2;
} else {
phy->pll_pre_p =
(m_pll / (2 * r_pll)) / 2;
phy->pll_fbd_s =
(m_pll / (2 * r_pll)) % 2;
}
phy->pll_fbd_div1f = 0;
phy->pll_fbd_div5f = 0;
} else {
phy->pll_pre_p = 0;
phy->pll_fbd_s = 0;
phy->pll_fbd_div1f = 0;
phy->pll_fbd_div5f = 1;
}
f_kHz = (u64)1000000000 * (u64)m_pll /
((u64)cfg_clk_ps * (u64)n_pll * (u64)q_pll);
if (f_kHz >= phy_freq_kHz)
break;
tmp_kHz += 10;
} while (1);
ui = 1000000 / f_kHz;
phy->clk_t_lpx = ROUND(50, 8 * ui);
phy->clk_t_hs_prepare = ROUND(133, 16 * ui) - 1;
phy->clk_t_hs_zero = ROUND(262, 8 * ui);
phy->clk_t_hs_trial = 2 * (ROUND(60, 8 * ui) - 1);
phy->clk_t_wakeup = ROUND(1000000, (cfg_clk_ps / 1000) - 1);
if (phy->clk_t_wakeup > 0xff)
phy->clk_t_wakeup = 0xff;
phy->data_t_wakeup = phy->clk_t_wakeup;
phy->data_t_lpx = phy->clk_t_lpx;
phy->data_t_hs_prepare = ROUND(125 + 10 * ui, 16 * ui) - 1;
phy->data_t_hs_zero = ROUND(105 + 6 * ui, 8 * ui);
phy->data_t_hs_trial = 2 * (ROUND(60 + 4 * ui, 8 * ui) - 1);
phy->data_t_ta_go = 3;
phy->data_t_ta_get = 4;
phy->pll_enbwt = 1;
phy->clklp2hs_time = ROUND(407, 8 * ui) + 12;
phy->clkhs2lp_time = ROUND(105 + 12 * ui, 8 * ui);
phy->lp2hs_time = ROUND(240 + 12 * ui, 8 * ui) + 1;
phy->hs2lp_time = phy->clkhs2lp_time;
phy->clk_to_data_delay = 1 + phy->clklp2hs_time;
phy->data_to_clk_delay = ROUND(60 + 52 * ui, 8 * ui) +
phy->clkhs2lp_time;
phy->lane_byte_clk_kHz = f_kHz / 8;
phy->clk_division = phy->lane_byte_clk_kHz / MAX_TX_ESC_CLK;
if (phy->lane_byte_clk_kHz % MAX_TX_ESC_CLK)
phy->clk_division++;
+}
+static u32 dsi_get_dpi_color_coding(enum mipi_dsi_pixel_format format) +{
u32 val;
/* TODO: only support RGB888 now, to support more */
switch (format) {
case MIPI_DSI_FMT_RGB888:
val = DSI_24BITS_1;
break;
default:
val = DSI_24BITS_1;
break;
}
return val;
+}
+/*
- dsi phy reg write function
- */
+static void dsi_phy_tst_set(void __iomem *base, u32 reg, u32 val) +{
writel(reg, base + PHY_TST_CTRL1);
/* reg addr written at first */
wmb();
writel(0x02, base + PHY_TST_CTRL0);
/* cmd1 sent for write */
wmb();
writel(0x00, base + PHY_TST_CTRL0);
/* cmd2 sent for write */
wmb();
writel(val, base + PHY_TST_CTRL1);
/* Then write data */
wmb();
writel(0x02, base + PHY_TST_CTRL0);
/* cmd2 sent for write */
wmb();
writel(0x00, base + PHY_TST_CTRL0);
You can use iowrite32 here to prevent calling wmb before each write.
This chip is arm64. I find that for arm64, it just wrap iowrite32 is the same to writel. Am I wrong?
+}
+static void dsi_set_mipi_phy(void __iomem *base,
struct mipi_phy_params *phy,
u32 lanes)
+{
u32 delay_count;
u32 val;
u32 i;
/* set lanes value */
val = (lanes - 1) | (PHY_STOP_WAIT_TIME << 8);
writel(val, base + PHY_IF_CFG);
/* set phy clk division */
val = readl(base + CLKMGR_CFG) | phy->clk_division;
writel(val, base + CLKMGR_CFG);
/* clean up phy set param */
writel(0, base + PHY_RSTZ);
writel(0, base + PHY_TST_CTRL0);
writel(1, base + PHY_TST_CTRL0);
writel(0, base + PHY_TST_CTRL0);
/* clock lane Timing control - TLPX */
dsi_phy_tst_set(base, 0x10010, phy->clk_t_lpx);
/* clock lane Timing control - THS-PREPARE */
dsi_phy_tst_set(base, 0x10011, phy->clk_t_hs_prepare);
/* clock lane Timing control - THS-ZERO */
dsi_phy_tst_set(base, 0x10012, phy->clk_t_hs_zero);
/* clock lane Timing control - THS-TRAIL */
dsi_phy_tst_set(base, 0x10013, phy->clk_t_hs_trial);
/* clock lane Timing control - TWAKEUP */
dsi_phy_tst_set(base, 0x10014, phy->clk_t_wakeup);
/* data lane */
for (i = 0; i < lanes; i++) {
/* Timing control - TLPX */
dsi_phy_tst_set(base, 0x10020 + (i << 4),
phy->data_t_lpx);
Some macros here for these PHY registers would be nice.
Agree, will fix in next version.
/* Timing control - THS-PREPARE */
dsi_phy_tst_set(base, 0x10021 + (i << 4),
phy->data_t_hs_prepare);
/* Timing control - THS-ZERO */
dsi_phy_tst_set(base, 0x10022 + (i << 4),
phy->data_t_hs_zero);
/* Timing control - THS-TRAIL */
dsi_phy_tst_set(base, 0x10023 + (i << 4),
phy->data_t_hs_trial);
/* Timing control - TTA-GO */
dsi_phy_tst_set(base, 0x10024 + (i << 4),
phy->data_t_ta_go);
/* Timing control - TTA-GET */
dsi_phy_tst_set(base, 0x10025 + (i << 4),
phy->data_t_ta_get);
/* Timing control - TWAKEUP */
dsi_phy_tst_set(base, 0x10026 + (i << 4),
phy->data_t_wakeup);
Something like the macro below would be more readable here:
#define PHY_TWAKEUP(i) (0x10026 + (i << 4))
Agree, will fix in next version.
Thanks, -xinliang
Looks good otherwise.
Archit
-- Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
On 3/1/2016 4:03 PM, Xinliang Liu wrote:
Hi,
On 1 March 2016 at 02:49, Archit Taneja architt@codeaurora.org wrote:
On 2/26/2016 2:10 PM, Xinliang Liu wrote:
Add DesignWare MIPI DSI Host Controller v1.02 encoder driver for hi6220 SoC.
v6:
- Change "pclk_dsi" to "pclk".
v5: None. v4: None. v3:
- Rename file name to dw_drm_dsi.c
- Make encoder type as DRM_MODE_ENCODER_DSI.
- A few cleanup.
v2:
- Remove abtraction layer.
Signed-off-by: Xinliang Liu xinliang.liu@linaro.org Signed-off-by: Xinwei Kong kong.kongxinwei@hisilicon.com Signed-off-by: Andy Green andy.green@linaro.org
drivers/gpu/drm/hisilicon/kirin/Kconfig | 1 + drivers/gpu/drm/hisilicon/kirin/Makefile | 3 +- drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c | 743 +++++++++++++++++++++++++++ drivers/gpu/drm/hisilicon/kirin/dw_dsi_reg.h | 83 +++ 4 files changed, 829 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c create mode 100644 drivers/gpu/drm/hisilicon/kirin/dw_dsi_reg.h
diff --git a/drivers/gpu/drm/hisilicon/kirin/Kconfig b/drivers/gpu/drm/hisilicon/kirin/Kconfig index 3ac4b8edeac1..de0d454c5c13 100644 --- a/drivers/gpu/drm/hisilicon/kirin/Kconfig +++ b/drivers/gpu/drm/hisilicon/kirin/Kconfig @@ -4,6 +4,7 @@ config DRM_HISI_KIRIN select DRM_KMS_HELPER select DRM_GEM_CMA_HELPER select DRM_KMS_CMA_HELPER
select DRM_MIPI_DSI help Choose this option if you have a hisilicon Kirin
chipsets(hi6220). If M is selected the module will be called kirin-drm. diff --git a/drivers/gpu/drm/hisilicon/kirin/Makefile b/drivers/gpu/drm/hisilicon/kirin/Makefile index 2a61ab006ddb..5dcd0d4328b6 100644 --- a/drivers/gpu/drm/hisilicon/kirin/Makefile +++ b/drivers/gpu/drm/hisilicon/kirin/Makefile @@ -1,4 +1,5 @@ kirin-drm-y := kirin_drm_drv.o \
kirin_drm_ade.o
kirin_drm_ade.o \
dw_drm_dsi.o
obj-$(CONFIG_DRM_HISI_KIRIN) += kirin-drm.o
diff --git a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c new file mode 100644 index 000000000000..8329148cc89d --- /dev/null +++ b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c @@ -0,0 +1,743 @@ +/*
- DesignWare MIPI DSI Host Controller v1.02 driver
- Copyright (c) 2016 Linaro Limited.
- Copyright (c) 2014-2016 Hisilicon Limited.
- Author:
Xinliang Liu <z.liuxinliang@hisilicon.com>
Xinliang Liu <xinliang.liu@linaro.org>
Xinwei Kong <kong.kongxinwei@hisilicon.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 as
- published by the Free Software Foundation.
- */
+#include <linux/clk.h> +#include <linux/component.h> +#include <linux/of_graph.h>
+#include <drm/drm_of.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_encoder_slave.h> +#include <drm/drm_atomic_helper.h>
+#include "dw_dsi_reg.h"
+#define MAX_TX_ESC_CLK (10) +#define ROUND(x, y) ((x) / (y) + ((x) % (y) * 10 / (y) >= 5 ? 1 : 0)) +#define PHY_REF_CLK_RATE 19200000 +#define PHY_REF_CLK_PERIOD_PS (1000000000 / (PHY_REF_CLK_RATE / 1000))
+#define encoder_to_dsi(encoder) \
container_of(encoder, struct dw_dsi, encoder)
+#define host_to_dsi(host) \
container_of(host, struct dw_dsi, host)
+struct mipi_phy_params {
u32 clk_t_lpx;
u32 clk_t_hs_prepare;
u32 clk_t_hs_zero;
u32 clk_t_hs_trial;
u32 clk_t_wakeup;
u32 data_t_lpx;
u32 data_t_hs_prepare;
u32 data_t_hs_zero;
u32 data_t_hs_trial;
u32 data_t_ta_go;
u32 data_t_ta_get;
u32 data_t_wakeup;
u32 hstx_ckg_sel;
u32 pll_fbd_div5f;
u32 pll_fbd_div1f;
u32 pll_fbd_2p;
u32 pll_enbwt;
u32 pll_fbd_p;
u32 pll_fbd_s;
u32 pll_pre_div1p;
u32 pll_pre_p;
u32 pll_vco_750M;
u32 pll_lpf_rs;
u32 pll_lpf_cs;
u32 clklp2hs_time;
u32 clkhs2lp_time;
u32 lp2hs_time;
u32 hs2lp_time;
u32 clk_to_data_delay;
u32 data_to_clk_delay;
u32 lane_byte_clk_kHz;
u32 clk_division;
+};
+struct dsi_hw_ctx {
void __iomem *base;
struct clk *pclk;
+};
+struct dw_dsi {
struct drm_encoder encoder;
struct drm_display_mode cur_mode;
struct dsi_hw_ctx *ctx;
struct mipi_phy_params phy;
u32 lanes;
enum mipi_dsi_pixel_format format;
unsigned long mode_flags;
bool enable;
+};
+struct dsi_data {
struct dw_dsi dsi;
struct dsi_hw_ctx ctx;
+};
+struct dsi_phy_range {
u32 min_range_kHz;
u32 max_range_kHz;
u32 pll_vco_750M;
u32 hstx_ckg_sel;
+};
+static const struct dsi_phy_range dphy_range_info[] = {
{ 46875, 62500, 1, 7 },
{ 62500, 93750, 0, 7 },
{ 93750, 125000, 1, 6 },
{ 125000, 187500, 0, 6 },
{ 187500, 250000, 1, 5 },
{ 250000, 375000, 0, 5 },
{ 375000, 500000, 1, 4 },
{ 500000, 750000, 0, 4 },
{ 750000, 1000000, 1, 0 },
{ 1000000, 1500000, 0, 0 }
+};
+static void dsi_get_phy_params(u32 phy_freq_kHz,
struct mipi_phy_params *phy)
+{
u32 ui = 0;
u32 cfg_clk_ps = PHY_REF_CLK_PERIOD_PS;
u32 i = 0;
u32 q_pll = 1;
u32 m_pll = 0;
u32 n_pll = 0;
u32 r_pll = 1;
u32 m_n = 0;
u32 m_n_int = 0;
u64 f_kHz;
u64 temp;
u64 tmp_kHz = phy_freq_kHz;
do {
f_kHz = tmp_kHz;
/* Find the PLL clock range from the table */
for (i = 0; i < ARRAY_SIZE(dphy_range_info); i++)
if (f_kHz >= dphy_range_info[i].min_range_kHz &&
f_kHz <= dphy_range_info[i].max_range_kHz)
break;
if (i == ARRAY_SIZE(dphy_range_info)) {
DRM_ERROR("%lldkHz out of range\n", f_kHz);
return;
}
phy->pll_vco_750M = dphy_range_info[i].pll_vco_750M;
phy->hstx_ckg_sel = dphy_range_info[i].hstx_ckg_sel;
if (phy->hstx_ckg_sel <= 7 &&
phy->hstx_ckg_sel >= 4)
q_pll = 0x10 >> (7 - phy->hstx_ckg_sel);
temp = f_kHz * (u64)q_pll * (u64)cfg_clk_ps;
m_n_int = temp / (u64)1000000000;
m_n = (temp % (u64)1000000000) / (u64)100000000;
if (m_n_int % 2 == 0) {
if (m_n * 6 >= 50) {
n_pll = 2;
m_pll = (m_n_int + 1) * n_pll;
} else if (m_n * 6 >= 30) {
n_pll = 3;
m_pll = m_n_int * n_pll + 2;
} else {
n_pll = 1;
m_pll = m_n_int * n_pll;
}
} else {
if (m_n * 6 >= 50) {
n_pll = 1;
m_pll = (m_n_int + 1) * n_pll;
} else if (m_n * 6 >= 30) {
n_pll = 1;
m_pll = (m_n_int + 1) * n_pll;
} else if (m_n * 6 >= 10) {
n_pll = 3;
m_pll = m_n_int * n_pll + 1;
} else {
n_pll = 2;
m_pll = m_n_int * n_pll;
}
}
if (n_pll == 1) {
phy->pll_fbd_p = 0;
phy->pll_pre_div1p = 1;
} else {
phy->pll_fbd_p = n_pll;
phy->pll_pre_div1p = 0;
}
if (phy->pll_fbd_2p <= 7 && phy->pll_fbd_2p >= 4)
r_pll = 0x10 >> (7 - phy->pll_fbd_2p);
if (m_pll == 2) {
phy->pll_pre_p = 0;
phy->pll_fbd_s = 0;
phy->pll_fbd_div1f = 0;
phy->pll_fbd_div5f = 1;
} else if (m_pll >= 2 * 2 * r_pll && m_pll <= 2 * 4 *
r_pll) {
phy->pll_pre_p = m_pll / (2 * r_pll);
phy->pll_fbd_s = 0;
phy->pll_fbd_div1f = 1;
phy->pll_fbd_div5f = 0;
} else if (m_pll >= 2 * 5 * r_pll && m_pll <= 2 * 150 *
r_pll) {
if (((m_pll / (2 * r_pll)) % 2) == 0) {
phy->pll_pre_p =
(m_pll / (2 * r_pll)) / 2 - 1;
phy->pll_fbd_s =
(m_pll / (2 * r_pll)) % 2 + 2;
} else {
phy->pll_pre_p =
(m_pll / (2 * r_pll)) / 2;
phy->pll_fbd_s =
(m_pll / (2 * r_pll)) % 2;
}
phy->pll_fbd_div1f = 0;
phy->pll_fbd_div5f = 0;
} else {
phy->pll_pre_p = 0;
phy->pll_fbd_s = 0;
phy->pll_fbd_div1f = 0;
phy->pll_fbd_div5f = 1;
}
f_kHz = (u64)1000000000 * (u64)m_pll /
((u64)cfg_clk_ps * (u64)n_pll * (u64)q_pll);
if (f_kHz >= phy_freq_kHz)
break;
tmp_kHz += 10;
} while (1);
ui = 1000000 / f_kHz;
phy->clk_t_lpx = ROUND(50, 8 * ui);
phy->clk_t_hs_prepare = ROUND(133, 16 * ui) - 1;
phy->clk_t_hs_zero = ROUND(262, 8 * ui);
phy->clk_t_hs_trial = 2 * (ROUND(60, 8 * ui) - 1);
phy->clk_t_wakeup = ROUND(1000000, (cfg_clk_ps / 1000) - 1);
if (phy->clk_t_wakeup > 0xff)
phy->clk_t_wakeup = 0xff;
phy->data_t_wakeup = phy->clk_t_wakeup;
phy->data_t_lpx = phy->clk_t_lpx;
phy->data_t_hs_prepare = ROUND(125 + 10 * ui, 16 * ui) - 1;
phy->data_t_hs_zero = ROUND(105 + 6 * ui, 8 * ui);
phy->data_t_hs_trial = 2 * (ROUND(60 + 4 * ui, 8 * ui) - 1);
phy->data_t_ta_go = 3;
phy->data_t_ta_get = 4;
phy->pll_enbwt = 1;
phy->clklp2hs_time = ROUND(407, 8 * ui) + 12;
phy->clkhs2lp_time = ROUND(105 + 12 * ui, 8 * ui);
phy->lp2hs_time = ROUND(240 + 12 * ui, 8 * ui) + 1;
phy->hs2lp_time = phy->clkhs2lp_time;
phy->clk_to_data_delay = 1 + phy->clklp2hs_time;
phy->data_to_clk_delay = ROUND(60 + 52 * ui, 8 * ui) +
phy->clkhs2lp_time;
phy->lane_byte_clk_kHz = f_kHz / 8;
phy->clk_division = phy->lane_byte_clk_kHz / MAX_TX_ESC_CLK;
if (phy->lane_byte_clk_kHz % MAX_TX_ESC_CLK)
phy->clk_division++;
+}
+static u32 dsi_get_dpi_color_coding(enum mipi_dsi_pixel_format format) +{
u32 val;
/* TODO: only support RGB888 now, to support more */
switch (format) {
case MIPI_DSI_FMT_RGB888:
val = DSI_24BITS_1;
break;
default:
val = DSI_24BITS_1;
break;
}
return val;
+}
+/*
- dsi phy reg write function
- */
+static void dsi_phy_tst_set(void __iomem *base, u32 reg, u32 val) +{
writel(reg, base + PHY_TST_CTRL1);
/* reg addr written at first */
wmb();
writel(0x02, base + PHY_TST_CTRL0);
/* cmd1 sent for write */
wmb();
writel(0x00, base + PHY_TST_CTRL0);
/* cmd2 sent for write */
wmb();
writel(val, base + PHY_TST_CTRL1);
/* Then write data */
wmb();
writel(0x02, base + PHY_TST_CTRL0);
/* cmd2 sent for write */
wmb();
writel(0x00, base + PHY_TST_CTRL0);
You can use iowrite32 here to prevent calling wmb before each write.
This chip is arm64. I find that for arm64, it just wrap iowrite32 is the same to writel. Am I wrong?
You're right. So, you can keep writel, but drop remove the wmb() calls, since writel already calls wmb() internally.
Archit
+}
+static void dsi_set_mipi_phy(void __iomem *base,
struct mipi_phy_params *phy,
u32 lanes)
+{
u32 delay_count;
u32 val;
u32 i;
/* set lanes value */
val = (lanes - 1) | (PHY_STOP_WAIT_TIME << 8);
writel(val, base + PHY_IF_CFG);
/* set phy clk division */
val = readl(base + CLKMGR_CFG) | phy->clk_division;
writel(val, base + CLKMGR_CFG);
/* clean up phy set param */
writel(0, base + PHY_RSTZ);
writel(0, base + PHY_TST_CTRL0);
writel(1, base + PHY_TST_CTRL0);
writel(0, base + PHY_TST_CTRL0);
/* clock lane Timing control - TLPX */
dsi_phy_tst_set(base, 0x10010, phy->clk_t_lpx);
/* clock lane Timing control - THS-PREPARE */
dsi_phy_tst_set(base, 0x10011, phy->clk_t_hs_prepare);
/* clock lane Timing control - THS-ZERO */
dsi_phy_tst_set(base, 0x10012, phy->clk_t_hs_zero);
/* clock lane Timing control - THS-TRAIL */
dsi_phy_tst_set(base, 0x10013, phy->clk_t_hs_trial);
/* clock lane Timing control - TWAKEUP */
dsi_phy_tst_set(base, 0x10014, phy->clk_t_wakeup);
/* data lane */
for (i = 0; i < lanes; i++) {
/* Timing control - TLPX */
dsi_phy_tst_set(base, 0x10020 + (i << 4),
phy->data_t_lpx);
Some macros here for these PHY registers would be nice.
Agree, will fix in next version.
/* Timing control - THS-PREPARE */
dsi_phy_tst_set(base, 0x10021 + (i << 4),
phy->data_t_hs_prepare);
/* Timing control - THS-ZERO */
dsi_phy_tst_set(base, 0x10022 + (i << 4),
phy->data_t_hs_zero);
/* Timing control - THS-TRAIL */
dsi_phy_tst_set(base, 0x10023 + (i << 4),
phy->data_t_hs_trial);
/* Timing control - TTA-GO */
dsi_phy_tst_set(base, 0x10024 + (i << 4),
phy->data_t_ta_go);
/* Timing control - TTA-GET */
dsi_phy_tst_set(base, 0x10025 + (i << 4),
phy->data_t_ta_get);
/* Timing control - TWAKEUP */
dsi_phy_tst_set(base, 0x10026 + (i << 4),
phy->data_t_wakeup);
Something like the macro below would be more readable here:
#define PHY_TWAKEUP(i) (0x10026 + (i << 4))
Agree, will fix in next version.
Thanks, -xinliang
Looks good otherwise.
Archit
-- Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
On 1 March 2016 at 20:45, Archit Taneja architt@codeaurora.org wrote:
On 3/1/2016 4:03 PM, Xinliang Liu wrote:
Hi,
On 1 March 2016 at 02:49, Archit Taneja architt@codeaurora.org wrote:
On 2/26/2016 2:10 PM, Xinliang Liu wrote:
Add DesignWare MIPI DSI Host Controller v1.02 encoder driver for hi6220 SoC.
v6:
- Change "pclk_dsi" to "pclk".
v5: None. v4: None. v3:
- Rename file name to dw_drm_dsi.c
- Make encoder type as DRM_MODE_ENCODER_DSI.
- A few cleanup.
v2:
- Remove abtraction layer.
Signed-off-by: Xinliang Liu xinliang.liu@linaro.org Signed-off-by: Xinwei Kong kong.kongxinwei@hisilicon.com Signed-off-by: Andy Green andy.green@linaro.org
drivers/gpu/drm/hisilicon/kirin/Kconfig | 1 + drivers/gpu/drm/hisilicon/kirin/Makefile | 3 +- drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c | 743 +++++++++++++++++++++++++++ drivers/gpu/drm/hisilicon/kirin/dw_dsi_reg.h | 83 +++ 4 files changed, 829 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c create mode 100644 drivers/gpu/drm/hisilicon/kirin/dw_dsi_reg.h
diff --git a/drivers/gpu/drm/hisilicon/kirin/Kconfig b/drivers/gpu/drm/hisilicon/kirin/Kconfig index 3ac4b8edeac1..de0d454c5c13 100644 --- a/drivers/gpu/drm/hisilicon/kirin/Kconfig +++ b/drivers/gpu/drm/hisilicon/kirin/Kconfig @@ -4,6 +4,7 @@ config DRM_HISI_KIRIN select DRM_KMS_HELPER select DRM_GEM_CMA_HELPER select DRM_KMS_CMA_HELPER
select DRM_MIPI_DSI help Choose this option if you have a hisilicon Kirin
chipsets(hi6220). If M is selected the module will be called kirin-drm. diff --git a/drivers/gpu/drm/hisilicon/kirin/Makefile b/drivers/gpu/drm/hisilicon/kirin/Makefile index 2a61ab006ddb..5dcd0d4328b6 100644 --- a/drivers/gpu/drm/hisilicon/kirin/Makefile +++ b/drivers/gpu/drm/hisilicon/kirin/Makefile @@ -1,4 +1,5 @@ kirin-drm-y := kirin_drm_drv.o \
kirin_drm_ade.o
kirin_drm_ade.o \
dw_drm_dsi.o
obj-$(CONFIG_DRM_HISI_KIRIN) += kirin-drm.o
diff --git a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c new file mode 100644 index 000000000000..8329148cc89d --- /dev/null +++ b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c @@ -0,0 +1,743 @@ +/*
- DesignWare MIPI DSI Host Controller v1.02 driver
- Copyright (c) 2016 Linaro Limited.
- Copyright (c) 2014-2016 Hisilicon Limited.
- Author:
Xinliang Liu <z.liuxinliang@hisilicon.com>
Xinliang Liu <xinliang.liu@linaro.org>
Xinwei Kong <kong.kongxinwei@hisilicon.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 as
- published by the Free Software Foundation.
- */
+#include <linux/clk.h> +#include <linux/component.h> +#include <linux/of_graph.h>
+#include <drm/drm_of.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_encoder_slave.h> +#include <drm/drm_atomic_helper.h>
+#include "dw_dsi_reg.h"
+#define MAX_TX_ESC_CLK (10) +#define ROUND(x, y) ((x) / (y) + ((x) % (y) * 10 / (y) >= 5 ? 1 : 0)) +#define PHY_REF_CLK_RATE 19200000 +#define PHY_REF_CLK_PERIOD_PS (1000000000 / (PHY_REF_CLK_RATE / 1000))
+#define encoder_to_dsi(encoder) \
container_of(encoder, struct dw_dsi, encoder)
+#define host_to_dsi(host) \
container_of(host, struct dw_dsi, host)
+struct mipi_phy_params {
u32 clk_t_lpx;
u32 clk_t_hs_prepare;
u32 clk_t_hs_zero;
u32 clk_t_hs_trial;
u32 clk_t_wakeup;
u32 data_t_lpx;
u32 data_t_hs_prepare;
u32 data_t_hs_zero;
u32 data_t_hs_trial;
u32 data_t_ta_go;
u32 data_t_ta_get;
u32 data_t_wakeup;
u32 hstx_ckg_sel;
u32 pll_fbd_div5f;
u32 pll_fbd_div1f;
u32 pll_fbd_2p;
u32 pll_enbwt;
u32 pll_fbd_p;
u32 pll_fbd_s;
u32 pll_pre_div1p;
u32 pll_pre_p;
u32 pll_vco_750M;
u32 pll_lpf_rs;
u32 pll_lpf_cs;
u32 clklp2hs_time;
u32 clkhs2lp_time;
u32 lp2hs_time;
u32 hs2lp_time;
u32 clk_to_data_delay;
u32 data_to_clk_delay;
u32 lane_byte_clk_kHz;
u32 clk_division;
+};
+struct dsi_hw_ctx {
void __iomem *base;
struct clk *pclk;
+};
+struct dw_dsi {
struct drm_encoder encoder;
struct drm_display_mode cur_mode;
struct dsi_hw_ctx *ctx;
struct mipi_phy_params phy;
u32 lanes;
enum mipi_dsi_pixel_format format;
unsigned long mode_flags;
bool enable;
+};
+struct dsi_data {
struct dw_dsi dsi;
struct dsi_hw_ctx ctx;
+};
+struct dsi_phy_range {
u32 min_range_kHz;
u32 max_range_kHz;
u32 pll_vco_750M;
u32 hstx_ckg_sel;
+};
+static const struct dsi_phy_range dphy_range_info[] = {
{ 46875, 62500, 1, 7 },
{ 62500, 93750, 0, 7 },
{ 93750, 125000, 1, 6 },
{ 125000, 187500, 0, 6 },
{ 187500, 250000, 1, 5 },
{ 250000, 375000, 0, 5 },
{ 375000, 500000, 1, 4 },
{ 500000, 750000, 0, 4 },
{ 750000, 1000000, 1, 0 },
{ 1000000, 1500000, 0, 0 }
+};
+static void dsi_get_phy_params(u32 phy_freq_kHz,
struct mipi_phy_params *phy)
+{
u32 ui = 0;
u32 cfg_clk_ps = PHY_REF_CLK_PERIOD_PS;
u32 i = 0;
u32 q_pll = 1;
u32 m_pll = 0;
u32 n_pll = 0;
u32 r_pll = 1;
u32 m_n = 0;
u32 m_n_int = 0;
u64 f_kHz;
u64 temp;
u64 tmp_kHz = phy_freq_kHz;
do {
f_kHz = tmp_kHz;
/* Find the PLL clock range from the table */
for (i = 0; i < ARRAY_SIZE(dphy_range_info); i++)
if (f_kHz >= dphy_range_info[i].min_range_kHz &&
f_kHz <= dphy_range_info[i].max_range_kHz)
break;
if (i == ARRAY_SIZE(dphy_range_info)) {
DRM_ERROR("%lldkHz out of range\n", f_kHz);
return;
}
phy->pll_vco_750M = dphy_range_info[i].pll_vco_750M;
phy->hstx_ckg_sel = dphy_range_info[i].hstx_ckg_sel;
if (phy->hstx_ckg_sel <= 7 &&
phy->hstx_ckg_sel >= 4)
q_pll = 0x10 >> (7 - phy->hstx_ckg_sel);
temp = f_kHz * (u64)q_pll * (u64)cfg_clk_ps;
m_n_int = temp / (u64)1000000000;
m_n = (temp % (u64)1000000000) / (u64)100000000;
if (m_n_int % 2 == 0) {
if (m_n * 6 >= 50) {
n_pll = 2;
m_pll = (m_n_int + 1) * n_pll;
} else if (m_n * 6 >= 30) {
n_pll = 3;
m_pll = m_n_int * n_pll + 2;
} else {
n_pll = 1;
m_pll = m_n_int * n_pll;
}
} else {
if (m_n * 6 >= 50) {
n_pll = 1;
m_pll = (m_n_int + 1) * n_pll;
} else if (m_n * 6 >= 30) {
n_pll = 1;
m_pll = (m_n_int + 1) * n_pll;
} else if (m_n * 6 >= 10) {
n_pll = 3;
m_pll = m_n_int * n_pll + 1;
} else {
n_pll = 2;
m_pll = m_n_int * n_pll;
}
}
if (n_pll == 1) {
phy->pll_fbd_p = 0;
phy->pll_pre_div1p = 1;
} else {
phy->pll_fbd_p = n_pll;
phy->pll_pre_div1p = 0;
}
if (phy->pll_fbd_2p <= 7 && phy->pll_fbd_2p >= 4)
r_pll = 0x10 >> (7 - phy->pll_fbd_2p);
if (m_pll == 2) {
phy->pll_pre_p = 0;
phy->pll_fbd_s = 0;
phy->pll_fbd_div1f = 0;
phy->pll_fbd_div5f = 1;
} else if (m_pll >= 2 * 2 * r_pll && m_pll <= 2 * 4 *
r_pll) {
phy->pll_pre_p = m_pll / (2 * r_pll);
phy->pll_fbd_s = 0;
phy->pll_fbd_div1f = 1;
phy->pll_fbd_div5f = 0;
} else if (m_pll >= 2 * 5 * r_pll && m_pll <= 2 * 150 *
r_pll) {
if (((m_pll / (2 * r_pll)) % 2) == 0) {
phy->pll_pre_p =
(m_pll / (2 * r_pll)) / 2 - 1;
phy->pll_fbd_s =
(m_pll / (2 * r_pll)) % 2 + 2;
} else {
phy->pll_pre_p =
(m_pll / (2 * r_pll)) / 2;
phy->pll_fbd_s =
(m_pll / (2 * r_pll)) % 2;
}
phy->pll_fbd_div1f = 0;
phy->pll_fbd_div5f = 0;
} else {
phy->pll_pre_p = 0;
phy->pll_fbd_s = 0;
phy->pll_fbd_div1f = 0;
phy->pll_fbd_div5f = 1;
}
f_kHz = (u64)1000000000 * (u64)m_pll /
((u64)cfg_clk_ps * (u64)n_pll * (u64)q_pll);
if (f_kHz >= phy_freq_kHz)
break;
tmp_kHz += 10;
} while (1);
ui = 1000000 / f_kHz;
phy->clk_t_lpx = ROUND(50, 8 * ui);
phy->clk_t_hs_prepare = ROUND(133, 16 * ui) - 1;
phy->clk_t_hs_zero = ROUND(262, 8 * ui);
phy->clk_t_hs_trial = 2 * (ROUND(60, 8 * ui) - 1);
phy->clk_t_wakeup = ROUND(1000000, (cfg_clk_ps / 1000) - 1);
if (phy->clk_t_wakeup > 0xff)
phy->clk_t_wakeup = 0xff;
phy->data_t_wakeup = phy->clk_t_wakeup;
phy->data_t_lpx = phy->clk_t_lpx;
phy->data_t_hs_prepare = ROUND(125 + 10 * ui, 16 * ui) - 1;
phy->data_t_hs_zero = ROUND(105 + 6 * ui, 8 * ui);
phy->data_t_hs_trial = 2 * (ROUND(60 + 4 * ui, 8 * ui) - 1);
phy->data_t_ta_go = 3;
phy->data_t_ta_get = 4;
phy->pll_enbwt = 1;
phy->clklp2hs_time = ROUND(407, 8 * ui) + 12;
phy->clkhs2lp_time = ROUND(105 + 12 * ui, 8 * ui);
phy->lp2hs_time = ROUND(240 + 12 * ui, 8 * ui) + 1;
phy->hs2lp_time = phy->clkhs2lp_time;
phy->clk_to_data_delay = 1 + phy->clklp2hs_time;
phy->data_to_clk_delay = ROUND(60 + 52 * ui, 8 * ui) +
phy->clkhs2lp_time;
phy->lane_byte_clk_kHz = f_kHz / 8;
phy->clk_division = phy->lane_byte_clk_kHz / MAX_TX_ESC_CLK;
if (phy->lane_byte_clk_kHz % MAX_TX_ESC_CLK)
phy->clk_division++;
+}
+static u32 dsi_get_dpi_color_coding(enum mipi_dsi_pixel_format format) +{
u32 val;
/* TODO: only support RGB888 now, to support more */
switch (format) {
case MIPI_DSI_FMT_RGB888:
val = DSI_24BITS_1;
break;
default:
val = DSI_24BITS_1;
break;
}
return val;
+}
+/*
- dsi phy reg write function
- */
+static void dsi_phy_tst_set(void __iomem *base, u32 reg, u32 val) +{
writel(reg, base + PHY_TST_CTRL1);
/* reg addr written at first */
wmb();
writel(0x02, base + PHY_TST_CTRL0);
/* cmd1 sent for write */
wmb();
writel(0x00, base + PHY_TST_CTRL0);
/* cmd2 sent for write */
wmb();
writel(val, base + PHY_TST_CTRL1);
/* Then write data */
wmb();
writel(0x02, base + PHY_TST_CTRL0);
/* cmd2 sent for write */
wmb();
writel(0x00, base + PHY_TST_CTRL0);
You can use iowrite32 here to prevent calling wmb before each write.
This chip is arm64. I find that for arm64, it just wrap iowrite32 is the same to writel. Am I wrong?
You're right. So, you can keep writel, but drop remove the wmb() calls, since writel already calls wmb() internally.
You are right! will fix in next version.
Archit
+}
+static void dsi_set_mipi_phy(void __iomem *base,
struct mipi_phy_params *phy,
u32 lanes)
+{
u32 delay_count;
u32 val;
u32 i;
/* set lanes value */
val = (lanes - 1) | (PHY_STOP_WAIT_TIME << 8);
writel(val, base + PHY_IF_CFG);
/* set phy clk division */
val = readl(base + CLKMGR_CFG) | phy->clk_division;
writel(val, base + CLKMGR_CFG);
/* clean up phy set param */
writel(0, base + PHY_RSTZ);
writel(0, base + PHY_TST_CTRL0);
writel(1, base + PHY_TST_CTRL0);
writel(0, base + PHY_TST_CTRL0);
/* clock lane Timing control - TLPX */
dsi_phy_tst_set(base, 0x10010, phy->clk_t_lpx);
/* clock lane Timing control - THS-PREPARE */
dsi_phy_tst_set(base, 0x10011, phy->clk_t_hs_prepare);
/* clock lane Timing control - THS-ZERO */
dsi_phy_tst_set(base, 0x10012, phy->clk_t_hs_zero);
/* clock lane Timing control - THS-TRAIL */
dsi_phy_tst_set(base, 0x10013, phy->clk_t_hs_trial);
/* clock lane Timing control - TWAKEUP */
dsi_phy_tst_set(base, 0x10014, phy->clk_t_wakeup);
/* data lane */
for (i = 0; i < lanes; i++) {
/* Timing control - TLPX */
dsi_phy_tst_set(base, 0x10020 + (i << 4),
phy->data_t_lpx);
Some macros here for these PHY registers would be nice.
Agree, will fix in next version.
/* Timing control - THS-PREPARE */
dsi_phy_tst_set(base, 0x10021 + (i << 4),
phy->data_t_hs_prepare);
/* Timing control - THS-ZERO */
dsi_phy_tst_set(base, 0x10022 + (i << 4),
phy->data_t_hs_zero);
/* Timing control - THS-TRAIL */
dsi_phy_tst_set(base, 0x10023 + (i << 4),
phy->data_t_hs_trial);
/* Timing control - TTA-GO */
dsi_phy_tst_set(base, 0x10024 + (i << 4),
phy->data_t_ta_go);
/* Timing control - TTA-GET */
dsi_phy_tst_set(base, 0x10025 + (i << 4),
phy->data_t_ta_get);
/* Timing control - TWAKEUP */
dsi_phy_tst_set(base, 0x10026 + (i << 4),
phy->data_t_wakeup);
Something like the macro below would be more readable here:
#define PHY_TWAKEUP(i) (0x10026 + (i << 4))
Agree, will fix in next version.
Thanks, -xinliang
Looks good otherwise.
Archit
-- Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
-- Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
Add DesignWare dsi host driver for hi6220 SoC.
v6: None. v5: None. v4: None. v3: None. v2: - Remove abtraction layer.
Signed-off-by: Xinliang Liu xinliang.liu@linaro.org --- drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c | 50 ++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+)
diff --git a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c index 8329148cc89d..f15798b61451 100644 --- a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c +++ b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c @@ -79,6 +79,7 @@ struct dsi_hw_ctx {
struct dw_dsi { struct drm_encoder encoder; + struct mipi_dsi_host host; struct drm_display_mode cur_mode; struct dsi_hw_ctx *ctx; struct mipi_phy_params phy; @@ -642,6 +643,51 @@ static int dw_drm_encoder_init(struct device *dev, return 0; }
+static int dsi_host_attach(struct mipi_dsi_host *host, + struct mipi_dsi_device *mdsi) +{ + struct dw_dsi *dsi = host_to_dsi(host); + + if (mdsi->lanes < 1 || mdsi->lanes > 4) { + DRM_ERROR("dsi device params invalid\n"); + return -EINVAL; + } + + dsi->lanes = mdsi->lanes; + dsi->format = mdsi->format; + dsi->mode_flags = mdsi->mode_flags; + + return 0; +} + +static int dsi_host_detach(struct mipi_dsi_host *host, + struct mipi_dsi_device *mdsi) +{ + /* do nothing */ + return 0; +} + +static const struct mipi_dsi_host_ops dsi_host_ops = { + .attach = dsi_host_attach, + .detach = dsi_host_detach, +}; + +static int dsi_host_init(struct device *dev, struct dw_dsi *dsi) +{ + struct mipi_dsi_host *host = &dsi->host; + int ret; + + host->dev = dev; + host->ops = &dsi_host_ops; + ret = mipi_dsi_host_register(host); + if (ret) { + DRM_ERROR("failed to register dsi host\n"); + return ret; + } + + return 0; +} + static int dsi_bind(struct device *dev, struct device *master, void *data) { struct dsi_data *ddata = dev_get_drvdata(dev); @@ -653,6 +699,10 @@ static int dsi_bind(struct device *dev, struct device *master, void *data) if (ret) return ret;
+ ret = dsi_host_init(dev, dsi); + if (ret) + return ret; + return 0; }
On 2/26/2016 2:10 PM, Xinliang Liu wrote:
Add DesignWare dsi host driver for hi6220 SoC.
v6: None. v5: None. v4: None. v3: None. v2:
- Remove abtraction layer.
Reviewed-by: Archit Taneja architt@codeaurora.org
Signed-off-by: Xinliang Liu xinliang.liu@linaro.org
drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c | 50 ++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+)
diff --git a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c index 8329148cc89d..f15798b61451 100644 --- a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c +++ b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c @@ -79,6 +79,7 @@ struct dsi_hw_ctx {
struct dw_dsi { struct drm_encoder encoder;
- struct mipi_dsi_host host; struct drm_display_mode cur_mode; struct dsi_hw_ctx *ctx; struct mipi_phy_params phy;
@@ -642,6 +643,51 @@ static int dw_drm_encoder_init(struct device *dev, return 0; }
+static int dsi_host_attach(struct mipi_dsi_host *host,
struct mipi_dsi_device *mdsi)
+{
- struct dw_dsi *dsi = host_to_dsi(host);
- if (mdsi->lanes < 1 || mdsi->lanes > 4) {
DRM_ERROR("dsi device params invalid\n");
return -EINVAL;
- }
- dsi->lanes = mdsi->lanes;
- dsi->format = mdsi->format;
- dsi->mode_flags = mdsi->mode_flags;
- return 0;
+}
+static int dsi_host_detach(struct mipi_dsi_host *host,
struct mipi_dsi_device *mdsi)
+{
- /* do nothing */
- return 0;
+}
+static const struct mipi_dsi_host_ops dsi_host_ops = {
- .attach = dsi_host_attach,
- .detach = dsi_host_detach,
+};
+static int dsi_host_init(struct device *dev, struct dw_dsi *dsi) +{
- struct mipi_dsi_host *host = &dsi->host;
- int ret;
- host->dev = dev;
- host->ops = &dsi_host_ops;
- ret = mipi_dsi_host_register(host);
- if (ret) {
DRM_ERROR("failed to register dsi host\n");
return ret;
- }
- return 0;
+}
- static int dsi_bind(struct device *dev, struct device *master, void *data) { struct dsi_data *ddata = dev_get_drvdata(dev);
@@ -653,6 +699,10 @@ static int dsi_bind(struct device *dev, struct device *master, void *data) if (ret) return ret;
- ret = dsi_host_init(dev, dsi);
- if (ret)
return ret;
- return 0; }
Hi,
On 1 March 2016 at 02:51, Archit Taneja architt@codeaurora.org wrote:
On 2/26/2016 2:10 PM, Xinliang Liu wrote:
Add DesignWare dsi host driver for hi6220 SoC.
v6: None. v5: None. v4: None. v3: None. v2:
- Remove abtraction layer.
Reviewed-by: Archit Taneja architt@codeaurora.org
Thanks :-)
Best, -xinliang
Signed-off-by: Xinliang Liu xinliang.liu@linaro.org
drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c | 50 ++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+)
diff --git a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c index 8329148cc89d..f15798b61451 100644 --- a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c +++ b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c @@ -79,6 +79,7 @@ struct dsi_hw_ctx {
struct dw_dsi { struct drm_encoder encoder;
struct mipi_dsi_host host; struct drm_display_mode cur_mode; struct dsi_hw_ctx *ctx; struct mipi_phy_params phy;
@@ -642,6 +643,51 @@ static int dw_drm_encoder_init(struct device *dev, return 0; }
+static int dsi_host_attach(struct mipi_dsi_host *host,
struct mipi_dsi_device *mdsi)
+{
struct dw_dsi *dsi = host_to_dsi(host);
if (mdsi->lanes < 1 || mdsi->lanes > 4) {
DRM_ERROR("dsi device params invalid\n");
return -EINVAL;
}
dsi->lanes = mdsi->lanes;
dsi->format = mdsi->format;
dsi->mode_flags = mdsi->mode_flags;
return 0;
+}
+static int dsi_host_detach(struct mipi_dsi_host *host,
struct mipi_dsi_device *mdsi)
+{
/* do nothing */
return 0;
+}
+static const struct mipi_dsi_host_ops dsi_host_ops = {
.attach = dsi_host_attach,
.detach = dsi_host_detach,
+};
+static int dsi_host_init(struct device *dev, struct dw_dsi *dsi) +{
struct mipi_dsi_host *host = &dsi->host;
int ret;
host->dev = dev;
host->ops = &dsi_host_ops;
ret = mipi_dsi_host_register(host);
if (ret) {
DRM_ERROR("failed to register dsi host\n");
return ret;
}
return 0;
+}
- static int dsi_bind(struct device *dev, struct device *master, void
*data) { struct dsi_data *ddata = dev_get_drvdata(dev); @@ -653,6 +699,10 @@ static int dsi_bind(struct device *dev, struct device *master, void *data) if (ret) return ret;
ret = dsi_host_init(dev, dsi);
if (ret)
return ret;
}return 0;
-- Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
Add support for external HDMI bridge.
v6: None. v5: None. v4: None. v3: - Fix a typo: s/exteranl/external. v2: - Remove abtraction layer.
Signed-off-by: Xinliang Liu xinliang.liu@linaro.org --- drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c | 52 ++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+)
diff --git a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c index f15798b61451..c2c406446513 100644 --- a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c +++ b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c @@ -79,6 +79,7 @@ struct dsi_hw_ctx {
struct dw_dsi { struct drm_encoder encoder; + struct drm_bridge *bridge; struct mipi_dsi_host host; struct drm_display_mode cur_mode; struct dsi_hw_ctx *ctx; @@ -688,6 +689,25 @@ static int dsi_host_init(struct device *dev, struct dw_dsi *dsi) return 0; }
+static int dsi_bridge_init(struct drm_device *dev, struct dw_dsi *dsi) +{ + struct drm_encoder *encoder = &dsi->encoder; + struct drm_bridge *bridge = dsi->bridge; + int ret; + + /* associate the bridge to dsi encoder */ + encoder->bridge = bridge; + bridge->encoder = encoder; + + ret = drm_bridge_attach(dev, bridge); + if (ret) { + DRM_ERROR("failed to attach external bridge\n"); + return ret; + } + + return 0; +} + static int dsi_bind(struct device *dev, struct device *master, void *data) { struct dsi_data *ddata = dev_get_drvdata(dev); @@ -703,6 +723,10 @@ static int dsi_bind(struct device *dev, struct device *master, void *data) if (ret) return ret;
+ ret = dsi_bridge_init(drm_dev, dsi); + if (ret) + return ret; + return 0; }
@@ -719,8 +743,36 @@ static const struct component_ops dsi_ops = { static int dsi_parse_dt(struct platform_device *pdev, struct dw_dsi *dsi) { struct dsi_hw_ctx *ctx = dsi->ctx; + struct device_node *np = pdev->dev.of_node; + struct device_node *endpoint, *bridge_node; + struct drm_bridge *bridge; struct resource *res;
+ /* + * Get the endpoint node. In our case, dsi has one output port1 + * to which the external HDMI bridge is connected. + */ + endpoint = of_graph_get_endpoint_by_regs(np, 1, -1); + if (!endpoint) { + DRM_ERROR("no valid endpoint node\n"); + return -ENODEV; + } + of_node_put(endpoint); + + bridge_node = of_graph_get_remote_port_parent(endpoint); + if (!bridge_node) { + DRM_ERROR("no valid bridge node\n"); + return -ENODEV; + } + of_node_put(bridge_node); + + bridge = of_drm_find_bridge(bridge_node); + if (!bridge) { + DRM_INFO("wait for external HDMI bridge driver.\n"); + return -EPROBE_DEFER; + } + dsi->bridge = bridge; + ctx->pclk = devm_clk_get(&pdev->dev, "pclk"); if (IS_ERR(ctx->pclk)) { DRM_ERROR("failed to get pclk clock\n");
On 2/26/2016 2:10 PM, Xinliang Liu wrote:
Add support for external HDMI bridge.
v6: None. v5: None. v4: None. v3:
- Fix a typo: s/exteranl/external.
v2:
- Remove abtraction layer.
Reviewed-by: Archit Taneja architt@codeaurora.org
Signed-off-by: Xinliang Liu xinliang.liu@linaro.org
drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c | 52 ++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+)
diff --git a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c index f15798b61451..c2c406446513 100644 --- a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c +++ b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c @@ -79,6 +79,7 @@ struct dsi_hw_ctx {
struct dw_dsi { struct drm_encoder encoder;
- struct drm_bridge *bridge; struct mipi_dsi_host host; struct drm_display_mode cur_mode; struct dsi_hw_ctx *ctx;
@@ -688,6 +689,25 @@ static int dsi_host_init(struct device *dev, struct dw_dsi *dsi) return 0; }
+static int dsi_bridge_init(struct drm_device *dev, struct dw_dsi *dsi) +{
- struct drm_encoder *encoder = &dsi->encoder;
- struct drm_bridge *bridge = dsi->bridge;
- int ret;
- /* associate the bridge to dsi encoder */
- encoder->bridge = bridge;
- bridge->encoder = encoder;
- ret = drm_bridge_attach(dev, bridge);
- if (ret) {
DRM_ERROR("failed to attach external bridge\n");
return ret;
- }
- return 0;
+}
- static int dsi_bind(struct device *dev, struct device *master, void *data) { struct dsi_data *ddata = dev_get_drvdata(dev);
@@ -703,6 +723,10 @@ static int dsi_bind(struct device *dev, struct device *master, void *data) if (ret) return ret;
- ret = dsi_bridge_init(drm_dev, dsi);
- if (ret)
return ret;
- return 0; }
@@ -719,8 +743,36 @@ static const struct component_ops dsi_ops = { static int dsi_parse_dt(struct platform_device *pdev, struct dw_dsi *dsi) { struct dsi_hw_ctx *ctx = dsi->ctx;
struct device_node *np = pdev->dev.of_node;
struct device_node *endpoint, *bridge_node;
struct drm_bridge *bridge; struct resource *res;
/*
* Get the endpoint node. In our case, dsi has one output port1
* to which the external HDMI bridge is connected.
*/
endpoint = of_graph_get_endpoint_by_regs(np, 1, -1);
if (!endpoint) {
DRM_ERROR("no valid endpoint node\n");
return -ENODEV;
}
of_node_put(endpoint);
bridge_node = of_graph_get_remote_port_parent(endpoint);
if (!bridge_node) {
DRM_ERROR("no valid bridge node\n");
return -ENODEV;
}
of_node_put(bridge_node);
bridge = of_drm_find_bridge(bridge_node);
if (!bridge) {
DRM_INFO("wait for external HDMI bridge driver.\n");
return -EPROBE_DEFER;
}
dsi->bridge = bridge;
ctx->pclk = devm_clk_get(&pdev->dev, "pclk"); if (IS_ERR(ctx->pclk)) { DRM_ERROR("failed to get pclk clock\n");
On 1 March 2016 at 02:53, Archit Taneja architt@codeaurora.org wrote:
On 2/26/2016 2:10 PM, Xinliang Liu wrote:
Add support for external HDMI bridge.
v6: None. v5: None. v4: None. v3:
- Fix a typo: s/exteranl/external.
v2:
- Remove abtraction layer.
Reviewed-by: Archit Taneja architt@codeaurora.org
Thanks :-)
Best, -xinliang
Signed-off-by: Xinliang Liu xinliang.liu@linaro.org
drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c | 52 ++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+)
diff --git a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c index f15798b61451..c2c406446513 100644 --- a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c +++ b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c @@ -79,6 +79,7 @@ struct dsi_hw_ctx {
struct dw_dsi { struct drm_encoder encoder;
struct drm_bridge *bridge; struct mipi_dsi_host host; struct drm_display_mode cur_mode; struct dsi_hw_ctx *ctx;
@@ -688,6 +689,25 @@ static int dsi_host_init(struct device *dev, struct dw_dsi *dsi) return 0; }
+static int dsi_bridge_init(struct drm_device *dev, struct dw_dsi *dsi) +{
struct drm_encoder *encoder = &dsi->encoder;
struct drm_bridge *bridge = dsi->bridge;
int ret;
/* associate the bridge to dsi encoder */
encoder->bridge = bridge;
bridge->encoder = encoder;
ret = drm_bridge_attach(dev, bridge);
if (ret) {
DRM_ERROR("failed to attach external bridge\n");
return ret;
}
return 0;
+}
- static int dsi_bind(struct device *dev, struct device *master, void
*data) { struct dsi_data *ddata = dev_get_drvdata(dev); @@ -703,6 +723,10 @@ static int dsi_bind(struct device *dev, struct device *master, void *data) if (ret) return ret;
ret = dsi_bridge_init(drm_dev, dsi);
if (ret)
return ret;
}return 0;
@@ -719,8 +743,36 @@ static const struct component_ops dsi_ops = { static int dsi_parse_dt(struct platform_device *pdev, struct dw_dsi *dsi) { struct dsi_hw_ctx *ctx = dsi->ctx;
struct device_node *np = pdev->dev.of_node;
struct device_node *endpoint, *bridge_node;
struct drm_bridge *bridge; struct resource *res;
/*
* Get the endpoint node. In our case, dsi has one output port1
* to which the external HDMI bridge is connected.
*/
endpoint = of_graph_get_endpoint_by_regs(np, 1, -1);
if (!endpoint) {
DRM_ERROR("no valid endpoint node\n");
return -ENODEV;
}
of_node_put(endpoint);
bridge_node = of_graph_get_remote_port_parent(endpoint);
if (!bridge_node) {
DRM_ERROR("no valid bridge node\n");
return -ENODEV;
}
of_node_put(bridge_node);
bridge = of_drm_find_bridge(bridge_node);
if (!bridge) {
DRM_INFO("wait for external HDMI bridge driver.\n");
return -EPROBE_DEFER;
}
dsi->bridge = bridge;
ctx->pclk = devm_clk_get(&pdev->dev, "pclk"); if (IS_ERR(ctx->pclk)) { DRM_ERROR("failed to get pclk clock\n");
-- Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
Add maintainer and reviewer for hisilicon DRM driver.
v6: None. v5: None. v4: - Add Chen Feng puck.chen@hisilicon.com as Designated reviewer. v3: First version.
Signed-off-by: Xinliang Liu xinliang.liu@linaro.org --- MAINTAINERS | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS index 4978dc19a4d2..b94ac713916a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3774,6 +3774,16 @@ S: Maintained F: drivers/gpu/drm/gma500 F: include/drm/gma500*
+DRM DRIVERS FOR HISILICON +M: Xinliang Liu z.liuxinliang@hisilicon.com +R: Xinwei Kong kong.kongxinwei@hisilicon.com +R: Chen Feng puck.chen@hisilicon.com +L: dri-devel@lists.freedesktop.org +T: git git://github.com/xin3liang/linux.git +S: Maintained +F: drivers/gpu/drm/hisilicon +F: Documentation/devicetree/bindings/display/hisilicon + DRM DRIVERS FOR NVIDIA TEGRA M: Thierry Reding thierry.reding@gmail.com M: Terje Bergström tbergstrom@nvidia.com
Add ade, dsi and adv7533 DT nodes for hikey board.
Signed-off-by: Xinliang Liu xinliang.liu@linaro.org --- arch/arm64/boot/dts/hisilicon/hi6220-hikey.dts | 40 +++++++++++++++++++ arch/arm64/boot/dts/hisilicon/hi6220.dtsi | 55 ++++++++++++++++++++++++++ 2 files changed, 95 insertions(+)
diff --git a/arch/arm64/boot/dts/hisilicon/hi6220-hikey.dts b/arch/arm64/boot/dts/hisilicon/hi6220-hikey.dts index 818525197508..40239fba0572 100644 --- a/arch/arm64/boot/dts/hisilicon/hi6220-hikey.dts +++ b/arch/arm64/boot/dts/hisilicon/hi6220-hikey.dts @@ -39,3 +39,43 @@ &uart3 { label = "LS-UART1"; }; + +&ade { + status = "okay"; +}; + +&dsi { + status = "ok"; + + ports { + /* 1 for output port */ + port@1 { + reg = <1>; + + dsi_out0: endpoint@0 { + remote-endpoint = <&adv7533_in>; + }; + }; + }; +}; + +&i2c2 { + #address-cells = <1>; + #size-cells = <0>; + status = "ok"; + + adv7533: adv7533@39 { + compatible = "adi,adv7533"; + reg = <0x39>; + interrupt-parent = <&gpio1>; + interrupts = <1 2>; + pd-gpio = <&gpio0 4 0>; + adi,dsi-lanes = <4>; + + port { + adv7533_in: endpoint { + remote-endpoint = <&dsi_out0>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/hisilicon/hi6220.dtsi b/arch/arm64/boot/dts/hisilicon/hi6220.dtsi index ad1f1ebcb05c..991568bb3ea1 100644 --- a/arch/arm64/boot/dts/hisilicon/hi6220.dtsi +++ b/arch/arm64/boot/dts/hisilicon/hi6220.dtsi @@ -162,6 +162,11 @@ #clock-cells = <1>; };
+ medianoc_ade: medianoc_ade@f4520000 { + compatible = "syscon"; + reg = <0x0 0xf4520000 0x0 0x4000>; + }; + uart0: uart@f8015000 { /* console */ compatible = "arm,pl011", "arm,primecell"; reg = <0x0 0xf8015000 0x0 0x1000>; @@ -209,5 +214,55 @@ clock-names = "uartclk", "apb_pclk"; status = "disabled"; }; + + ade: ade@f4100000 { + compatible = "hisilicon,hi6220-ade"; + reg = <0x0 0xf4100000 0x0 0x7800>; + reg-names = "ade_base"; + hisilicon,noc-syscon = <&medianoc_ade>; + resets = <&media_ctrl MEDIA_ADE>; + interrupts = <0 115 4>; /* ldi interrupt */ + + clocks = <&media_ctrl HI6220_ADE_CORE>, + <&media_ctrl HI6220_CODEC_JPEG>, + <&media_ctrl HI6220_ADE_PIX_SRC>; + /*clock name*/ + clock-names = "clk_ade_core", + "clk_codec_jpeg", + "clk_ade_pix"; + + assigned-clocks = <&media_ctrl HI6220_ADE_CORE>, + <&media_ctrl HI6220_CODEC_JPEG>; + assigned-clock-rates = <360000000>, <288000000>; + dma-coherent; + status = "disabled"; + + port { + ade_out: endpoint { + remote-endpoint = <&dsi_in>; + }; + }; + }; + + dsi: dsi@f4107800 { + compatible = "hisilicon,hi6220-dsi"; + reg = <0x0 0xf4107800 0x0 0x100>; + clocks = <&media_ctrl HI6220_DSI_PCLK>; + clock-names = "pclk"; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + /* 0 for input port */ + port@0 { + reg = <0>; + dsi_in: endpoint { + remote-endpoint = <&ade_out>; + }; + }; + }; + }; }; };
dri-devel@lists.freedesktop.org