Merge branch 'drm_iommu_v15' of https://github.com/markyzq/kernel-drm-rockchip into drm-next
Merge rockchip GPU support. This has a branch in common with the iommu tree, hopefully the process works. * 'drm_iommu_v15' of https://github.com/markyzq/kernel-drm-rockchip: dt-bindings: video: Add documentation for rockchip vop dt-bindings: video: Add for rockchip display subsytem drm: rockchip: Add basic drm driver dt-bindings: iommu: Add documentation for rockchip iommu iommu/rockchip: rk3288 iommu driver
This commit is contained in:
Коммит
b75478d1c7
|
@ -0,0 +1,26 @@
|
||||||
|
Rockchip IOMMU
|
||||||
|
==============
|
||||||
|
|
||||||
|
A Rockchip DRM iommu translates io virtual addresses to physical addresses for
|
||||||
|
its master device. Each slave device is bound to a single master device, and
|
||||||
|
shares its clocks, power domain and irq.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible : Should be "rockchip,iommu"
|
||||||
|
- reg : Address space for the configuration registers
|
||||||
|
- interrupts : Interrupt specifier for the IOMMU instance
|
||||||
|
- interrupt-names : Interrupt name for the IOMMU instance
|
||||||
|
- #iommu-cells : Should be <0>. This indicates the iommu is a
|
||||||
|
"single-master" device, and needs no additional information
|
||||||
|
to associate with its master device. See:
|
||||||
|
Documentation/devicetree/bindings/iommu/iommu.txt
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
vopl_mmu: iommu@ff940300 {
|
||||||
|
compatible = "rockchip,iommu";
|
||||||
|
reg = <0xff940300 0x100>;
|
||||||
|
interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
interrupt-names = "vopl_mmu";
|
||||||
|
#iommu-cells = <0>;
|
||||||
|
};
|
|
@ -0,0 +1,19 @@
|
||||||
|
Rockchip DRM master device
|
||||||
|
================================
|
||||||
|
|
||||||
|
The Rockchip DRM master device is a virtual device needed to list all
|
||||||
|
vop devices or other display interface nodes that comprise the
|
||||||
|
graphics subsystem.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: Should be "rockchip,display-subsystem"
|
||||||
|
- ports: Should contain a list of phandles pointing to display interface port
|
||||||
|
of vop devices. vop definitions as defined in
|
||||||
|
Documentation/devicetree/bindings/video/rockchip-vop.txt
|
||||||
|
|
||||||
|
example:
|
||||||
|
|
||||||
|
display-subsystem {
|
||||||
|
compatible = "rockchip,display-subsystem";
|
||||||
|
ports = <&vopl_out>, <&vopb_out>;
|
||||||
|
};
|
|
@ -0,0 +1,58 @@
|
||||||
|
device-tree bindings for rockchip soc display controller (vop)
|
||||||
|
|
||||||
|
VOP (Visual Output Processor) is the Display Controller for the Rockchip
|
||||||
|
series of SoCs which transfers the image data from a video memory
|
||||||
|
buffer to an external LCD interface.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: value should be one of the following
|
||||||
|
"rockchip,rk3288-vop";
|
||||||
|
|
||||||
|
- interrupts: should contain a list of all VOP IP block interrupts in the
|
||||||
|
order: VSYNC, LCD_SYSTEM. The interrupt specifier
|
||||||
|
format depends on the interrupt controller used.
|
||||||
|
|
||||||
|
- clocks: must include clock specifiers corresponding to entries in the
|
||||||
|
clock-names property.
|
||||||
|
|
||||||
|
- clock-names: Must contain
|
||||||
|
aclk_vop: for ddr buffer transfer.
|
||||||
|
hclk_vop: for ahb bus to R/W the phy regs.
|
||||||
|
dclk_vop: pixel clock.
|
||||||
|
|
||||||
|
- resets: Must contain an entry for each entry in reset-names.
|
||||||
|
See ../reset/reset.txt for details.
|
||||||
|
- reset-names: Must include the following entries:
|
||||||
|
- axi
|
||||||
|
- ahb
|
||||||
|
- dclk
|
||||||
|
|
||||||
|
- iommus: required a iommu node
|
||||||
|
|
||||||
|
- port: A port node with endpoint definitions as defined in
|
||||||
|
Documentation/devicetree/bindings/media/video-interfaces.txt.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
SoC specific DT entry:
|
||||||
|
vopb: vopb@ff930000 {
|
||||||
|
compatible = "rockchip,rk3288-vop";
|
||||||
|
reg = <0xff930000 0x19c>;
|
||||||
|
interrupts = <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
clocks = <&cru ACLK_VOP0>, <&cru DCLK_VOP0>, <&cru HCLK_VOP0>;
|
||||||
|
clock-names = "aclk_vop", "dclk_vop", "hclk_vop";
|
||||||
|
resets = <&cru SRST_LCDC1_AXI>, <&cru SRST_LCDC1_AHB>, <&cru SRST_LCDC1_DCLK>;
|
||||||
|
reset-names = "axi", "ahb", "dclk";
|
||||||
|
iommus = <&vopb_mmu>;
|
||||||
|
vopb_out: port {
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
vopb_out_edp: endpoint@0 {
|
||||||
|
reg = <0>;
|
||||||
|
remote-endpoint=<&edp_in_vopb>;
|
||||||
|
};
|
||||||
|
vopb_out_hdmi: endpoint@1 {
|
||||||
|
reg = <1>;
|
||||||
|
remote-endpoint=<&hdmi_in_vopb>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
|
@ -167,6 +167,8 @@ config DRM_SAVAGE
|
||||||
|
|
||||||
source "drivers/gpu/drm/exynos/Kconfig"
|
source "drivers/gpu/drm/exynos/Kconfig"
|
||||||
|
|
||||||
|
source "drivers/gpu/drm/rockchip/Kconfig"
|
||||||
|
|
||||||
source "drivers/gpu/drm/vmwgfx/Kconfig"
|
source "drivers/gpu/drm/vmwgfx/Kconfig"
|
||||||
|
|
||||||
source "drivers/gpu/drm/gma500/Kconfig"
|
source "drivers/gpu/drm/gma500/Kconfig"
|
||||||
|
|
|
@ -49,6 +49,7 @@ obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/
|
||||||
obj-$(CONFIG_DRM_VIA) +=via/
|
obj-$(CONFIG_DRM_VIA) +=via/
|
||||||
obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
|
obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
|
||||||
obj-$(CONFIG_DRM_EXYNOS) +=exynos/
|
obj-$(CONFIG_DRM_EXYNOS) +=exynos/
|
||||||
|
obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/
|
||||||
obj-$(CONFIG_DRM_GMA500) += gma500/
|
obj-$(CONFIG_DRM_GMA500) += gma500/
|
||||||
obj-$(CONFIG_DRM_UDL) += udl/
|
obj-$(CONFIG_DRM_UDL) += udl/
|
||||||
obj-$(CONFIG_DRM_AST) += ast/
|
obj-$(CONFIG_DRM_AST) += ast/
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
config DRM_ROCKCHIP
|
||||||
|
tristate "DRM Support for Rockchip"
|
||||||
|
depends on DRM && ROCKCHIP_IOMMU
|
||||||
|
select DRM_KMS_HELPER
|
||||||
|
select DRM_KMS_FB_HELPER
|
||||||
|
select DRM_PANEL
|
||||||
|
select FB_CFB_FILLRECT
|
||||||
|
select FB_CFB_COPYAREA
|
||||||
|
select FB_CFB_IMAGEBLIT
|
||||||
|
select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
|
||||||
|
select VIDEOMODE_HELPERS
|
||||||
|
help
|
||||||
|
Choose this option if you have a Rockchip soc chipset.
|
||||||
|
This driver provides kernel mode setting and buffer
|
||||||
|
management to userspace. This driver does not provide
|
||||||
|
2D or 3D acceleration; acceleration is performed by other
|
||||||
|
IP found on the SoC.
|
|
@ -0,0 +1,8 @@
|
||||||
|
#
|
||||||
|
# Makefile for the drm device driver. This driver provides support for the
|
||||||
|
# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
|
||||||
|
|
||||||
|
rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \
|
||||||
|
rockchip_drm_gem.o
|
||||||
|
|
||||||
|
obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o rockchip_drm_vop.o
|
|
@ -0,0 +1,551 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
|
||||||
|
* Author:Mark Yao <mark.yao@rock-chips.com>
|
||||||
|
*
|
||||||
|
* based on exynos_drm_drv.c
|
||||||
|
*
|
||||||
|
* This software is licensed under the terms of the GNU General Public
|
||||||
|
* License version 2, as published by the Free Software Foundation, and
|
||||||
|
* may be copied, distributed, and modified under those terms.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <asm/dma-iommu.h>
|
||||||
|
|
||||||
|
#include <drm/drmP.h>
|
||||||
|
#include <drm/drm_crtc_helper.h>
|
||||||
|
#include <drm/drm_fb_helper.h>
|
||||||
|
#include <linux/dma-mapping.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/of_graph.h>
|
||||||
|
#include <linux/component.h>
|
||||||
|
|
||||||
|
#include "rockchip_drm_drv.h"
|
||||||
|
#include "rockchip_drm_fb.h"
|
||||||
|
#include "rockchip_drm_fbdev.h"
|
||||||
|
#include "rockchip_drm_gem.h"
|
||||||
|
|
||||||
|
#define DRIVER_NAME "rockchip"
|
||||||
|
#define DRIVER_DESC "RockChip Soc DRM"
|
||||||
|
#define DRIVER_DATE "20140818"
|
||||||
|
#define DRIVER_MAJOR 1
|
||||||
|
#define DRIVER_MINOR 0
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Attach a (component) device to the shared drm dma mapping from master drm
|
||||||
|
* device. This is used by the VOPs to map GEM buffers to a common DMA
|
||||||
|
* mapping.
|
||||||
|
*/
|
||||||
|
int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
|
||||||
|
struct device *dev)
|
||||||
|
{
|
||||||
|
struct dma_iommu_mapping *mapping = drm_dev->dev->archdata.mapping;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
dma_set_max_seg_size(dev, DMA_BIT_MASK(32));
|
||||||
|
|
||||||
|
return arm_iommu_attach_device(dev, mapping);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(rockchip_drm_dma_attach_device);
|
||||||
|
|
||||||
|
void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
|
||||||
|
struct device *dev)
|
||||||
|
{
|
||||||
|
arm_iommu_detach_device(dev);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(rockchip_drm_dma_detach_device);
|
||||||
|
|
||||||
|
int rockchip_register_crtc_funcs(struct drm_device *dev,
|
||||||
|
const struct rockchip_crtc_funcs *crtc_funcs,
|
||||||
|
int pipe)
|
||||||
|
{
|
||||||
|
struct rockchip_drm_private *priv = dev->dev_private;
|
||||||
|
|
||||||
|
if (pipe > ROCKCHIP_MAX_CRTC)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
priv->crtc_funcs[pipe] = crtc_funcs;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(rockchip_register_crtc_funcs);
|
||||||
|
|
||||||
|
void rockchip_unregister_crtc_funcs(struct drm_device *dev, int pipe)
|
||||||
|
{
|
||||||
|
struct rockchip_drm_private *priv = dev->dev_private;
|
||||||
|
|
||||||
|
if (pipe > ROCKCHIP_MAX_CRTC)
|
||||||
|
return;
|
||||||
|
|
||||||
|
priv->crtc_funcs[pipe] = NULL;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(rockchip_unregister_crtc_funcs);
|
||||||
|
|
||||||
|
static struct drm_crtc *rockchip_crtc_from_pipe(struct drm_device *drm,
|
||||||
|
int pipe)
|
||||||
|
{
|
||||||
|
struct drm_crtc *crtc;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
list_for_each_entry(crtc, &drm->mode_config.crtc_list, head)
|
||||||
|
if (i++ == pipe)
|
||||||
|
return crtc;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int pipe)
|
||||||
|
{
|
||||||
|
struct rockchip_drm_private *priv = dev->dev_private;
|
||||||
|
struct drm_crtc *crtc = rockchip_crtc_from_pipe(dev, pipe);
|
||||||
|
|
||||||
|
if (crtc && priv->crtc_funcs[pipe] &&
|
||||||
|
priv->crtc_funcs[pipe]->enable_vblank)
|
||||||
|
return priv->crtc_funcs[pipe]->enable_vblank(crtc);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int pipe)
|
||||||
|
{
|
||||||
|
struct rockchip_drm_private *priv = dev->dev_private;
|
||||||
|
struct drm_crtc *crtc = rockchip_crtc_from_pipe(dev, pipe);
|
||||||
|
|
||||||
|
if (crtc && priv->crtc_funcs[pipe] &&
|
||||||
|
priv->crtc_funcs[pipe]->enable_vblank)
|
||||||
|
priv->crtc_funcs[pipe]->disable_vblank(crtc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rockchip_drm_load(struct drm_device *drm_dev, unsigned long flags)
|
||||||
|
{
|
||||||
|
struct rockchip_drm_private *private;
|
||||||
|
struct dma_iommu_mapping *mapping;
|
||||||
|
struct device *dev = drm_dev->dev;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL);
|
||||||
|
if (!private)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
drm_dev->dev_private = private;
|
||||||
|
|
||||||
|
drm_mode_config_init(drm_dev);
|
||||||
|
|
||||||
|
rockchip_drm_mode_config_init(drm_dev);
|
||||||
|
|
||||||
|
dev->dma_parms = devm_kzalloc(dev, sizeof(*dev->dma_parms),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!dev->dma_parms) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto err_config_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO(djkurtz): fetch the mapping start/size from somewhere */
|
||||||
|
mapping = arm_iommu_create_mapping(&platform_bus_type, 0x00000000,
|
||||||
|
SZ_2G);
|
||||||
|
if (IS_ERR(mapping)) {
|
||||||
|
ret = PTR_ERR(mapping);
|
||||||
|
goto err_config_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
|
||||||
|
if (ret)
|
||||||
|
goto err_release_mapping;
|
||||||
|
|
||||||
|
dma_set_max_seg_size(dev, DMA_BIT_MASK(32));
|
||||||
|
|
||||||
|
ret = arm_iommu_attach_device(dev, mapping);
|
||||||
|
if (ret)
|
||||||
|
goto err_release_mapping;
|
||||||
|
|
||||||
|
/* Try to bind all sub drivers. */
|
||||||
|
ret = component_bind_all(dev, drm_dev);
|
||||||
|
if (ret)
|
||||||
|
goto err_detach_device;
|
||||||
|
|
||||||
|
/* init kms poll for handling hpd */
|
||||||
|
drm_kms_helper_poll_init(drm_dev);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* enable drm irq mode.
|
||||||
|
* - with irq_enabled = true, we can use the vblank feature.
|
||||||
|
*/
|
||||||
|
drm_dev->irq_enabled = true;
|
||||||
|
|
||||||
|
ret = drm_vblank_init(drm_dev, ROCKCHIP_MAX_CRTC);
|
||||||
|
if (ret)
|
||||||
|
goto err_kms_helper_poll_fini;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* with vblank_disable_allowed = true, vblank interrupt will be disabled
|
||||||
|
* by drm timer once a current process gives up ownership of
|
||||||
|
* vblank event.(after drm_vblank_put function is called)
|
||||||
|
*/
|
||||||
|
drm_dev->vblank_disable_allowed = true;
|
||||||
|
|
||||||
|
ret = rockchip_drm_fbdev_init(drm_dev);
|
||||||
|
if (ret)
|
||||||
|
goto err_vblank_cleanup;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
err_vblank_cleanup:
|
||||||
|
drm_vblank_cleanup(drm_dev);
|
||||||
|
err_kms_helper_poll_fini:
|
||||||
|
drm_kms_helper_poll_fini(drm_dev);
|
||||||
|
component_unbind_all(dev, drm_dev);
|
||||||
|
err_detach_device:
|
||||||
|
arm_iommu_detach_device(dev);
|
||||||
|
err_release_mapping:
|
||||||
|
arm_iommu_release_mapping(dev->archdata.mapping);
|
||||||
|
err_config_cleanup:
|
||||||
|
drm_mode_config_cleanup(drm_dev);
|
||||||
|
drm_dev->dev_private = NULL;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rockchip_drm_unload(struct drm_device *drm_dev)
|
||||||
|
{
|
||||||
|
struct device *dev = drm_dev->dev;
|
||||||
|
|
||||||
|
rockchip_drm_fbdev_fini(drm_dev);
|
||||||
|
drm_vblank_cleanup(drm_dev);
|
||||||
|
drm_kms_helper_poll_fini(drm_dev);
|
||||||
|
component_unbind_all(dev, drm_dev);
|
||||||
|
arm_iommu_detach_device(dev);
|
||||||
|
arm_iommu_release_mapping(dev->archdata.mapping);
|
||||||
|
drm_mode_config_cleanup(drm_dev);
|
||||||
|
drm_dev->dev_private = NULL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rockchip_drm_lastclose(struct drm_device *dev)
|
||||||
|
{
|
||||||
|
struct rockchip_drm_private *priv = dev->dev_private;
|
||||||
|
|
||||||
|
drm_fb_helper_restore_fbdev_mode_unlocked(&priv->fbdev_helper);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct file_operations rockchip_drm_driver_fops = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.open = drm_open,
|
||||||
|
.mmap = rockchip_gem_mmap,
|
||||||
|
.poll = drm_poll,
|
||||||
|
.read = drm_read,
|
||||||
|
.unlocked_ioctl = drm_ioctl,
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
.compat_ioctl = drm_compat_ioctl,
|
||||||
|
#endif
|
||||||
|
.release = drm_release,
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct vm_operations_struct rockchip_drm_vm_ops = {
|
||||||
|
.open = drm_gem_vm_open,
|
||||||
|
.close = drm_gem_vm_close,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct drm_driver rockchip_drm_driver = {
|
||||||
|
.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
|
||||||
|
.load = rockchip_drm_load,
|
||||||
|
.unload = rockchip_drm_unload,
|
||||||
|
.lastclose = rockchip_drm_lastclose,
|
||||||
|
.get_vblank_counter = drm_vblank_count,
|
||||||
|
.enable_vblank = rockchip_drm_crtc_enable_vblank,
|
||||||
|
.disable_vblank = rockchip_drm_crtc_disable_vblank,
|
||||||
|
.gem_vm_ops = &rockchip_drm_vm_ops,
|
||||||
|
.gem_free_object = rockchip_gem_free_object,
|
||||||
|
.dumb_create = rockchip_gem_dumb_create,
|
||||||
|
.dumb_map_offset = rockchip_gem_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_import = drm_gem_prime_import,
|
||||||
|
.gem_prime_export = drm_gem_prime_export,
|
||||||
|
.gem_prime_get_sg_table = rockchip_gem_prime_get_sg_table,
|
||||||
|
.gem_prime_vmap = rockchip_gem_prime_vmap,
|
||||||
|
.gem_prime_vunmap = rockchip_gem_prime_vunmap,
|
||||||
|
.gem_prime_mmap = rockchip_gem_mmap_buf,
|
||||||
|
.fops = &rockchip_drm_driver_fops,
|
||||||
|
.name = DRIVER_NAME,
|
||||||
|
.desc = DRIVER_DESC,
|
||||||
|
.date = DRIVER_DATE,
|
||||||
|
.major = DRIVER_MAJOR,
|
||||||
|
.minor = DRIVER_MINOR,
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_SLEEP
|
||||||
|
static int rockchip_drm_sys_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct drm_device *drm = dev_get_drvdata(dev);
|
||||||
|
struct drm_connector *connector;
|
||||||
|
|
||||||
|
if (!drm)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
drm_modeset_lock_all(drm);
|
||||||
|
list_for_each_entry(connector, &drm->mode_config.connector_list, head) {
|
||||||
|
int old_dpms = connector->dpms;
|
||||||
|
|
||||||
|
if (connector->funcs->dpms)
|
||||||
|
connector->funcs->dpms(connector, DRM_MODE_DPMS_OFF);
|
||||||
|
|
||||||
|
/* Set the old mode back to the connector for resume */
|
||||||
|
connector->dpms = old_dpms;
|
||||||
|
}
|
||||||
|
drm_modeset_unlock_all(drm);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rockchip_drm_sys_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct drm_device *drm = dev_get_drvdata(dev);
|
||||||
|
struct drm_connector *connector;
|
||||||
|
enum drm_connector_status status;
|
||||||
|
bool changed = false;
|
||||||
|
|
||||||
|
if (!drm)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
drm_modeset_lock_all(drm);
|
||||||
|
list_for_each_entry(connector, &drm->mode_config.connector_list, head) {
|
||||||
|
int desired_mode = connector->dpms;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* at suspend time, we save dpms to connector->dpms,
|
||||||
|
* restore the old_dpms, and at current time, the connector
|
||||||
|
* dpms status must be DRM_MODE_DPMS_OFF.
|
||||||
|
*/
|
||||||
|
connector->dpms = DRM_MODE_DPMS_OFF;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the connector has been disconnected during suspend,
|
||||||
|
* disconnect it from the encoder and leave it off. We'll notify
|
||||||
|
* userspace at the end.
|
||||||
|
*/
|
||||||
|
if (desired_mode == DRM_MODE_DPMS_ON) {
|
||||||
|
status = connector->funcs->detect(connector, true);
|
||||||
|
if (status == connector_status_disconnected) {
|
||||||
|
connector->encoder = NULL;
|
||||||
|
connector->status = status;
|
||||||
|
changed = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (connector->funcs->dpms)
|
||||||
|
connector->funcs->dpms(connector, desired_mode);
|
||||||
|
}
|
||||||
|
drm_modeset_unlock_all(drm);
|
||||||
|
|
||||||
|
drm_helper_resume_force_mode(drm);
|
||||||
|
|
||||||
|
if (changed)
|
||||||
|
drm_kms_helper_hotplug_event(drm);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const struct dev_pm_ops rockchip_drm_pm_ops = {
|
||||||
|
SET_SYSTEM_SLEEP_PM_OPS(rockchip_drm_sys_suspend,
|
||||||
|
rockchip_drm_sys_resume)
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @node: device tree node containing encoder input ports
|
||||||
|
* @encoder: drm_encoder
|
||||||
|
*/
|
||||||
|
int rockchip_drm_encoder_get_mux_id(struct device_node *node,
|
||||||
|
struct drm_encoder *encoder)
|
||||||
|
{
|
||||||
|
struct device_node *ep = NULL;
|
||||||
|
struct drm_crtc *crtc = encoder->crtc;
|
||||||
|
struct of_endpoint endpoint;
|
||||||
|
struct device_node *port;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!node || !crtc)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
do {
|
||||||
|
ep = of_graph_get_next_endpoint(node, ep);
|
||||||
|
if (!ep)
|
||||||
|
break;
|
||||||
|
|
||||||
|
port = of_graph_get_remote_port(ep);
|
||||||
|
of_node_put(port);
|
||||||
|
if (port == crtc->port) {
|
||||||
|
ret = of_graph_parse_endpoint(ep, &endpoint);
|
||||||
|
return ret ?: endpoint.id;
|
||||||
|
}
|
||||||
|
} while (ep);
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int compare_of(struct device *dev, void *data)
|
||||||
|
{
|
||||||
|
struct device_node *np = data;
|
||||||
|
|
||||||
|
return dev->of_node == np;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rockchip_add_endpoints(struct device *dev,
|
||||||
|
struct component_match **match,
|
||||||
|
struct device_node *port)
|
||||||
|
{
|
||||||
|
struct device_node *ep, *remote;
|
||||||
|
|
||||||
|
for_each_child_of_node(port, ep) {
|
||||||
|
remote = of_graph_get_remote_port_parent(ep);
|
||||||
|
if (!remote || !of_device_is_available(remote)) {
|
||||||
|
of_node_put(remote);
|
||||||
|
continue;
|
||||||
|
} else if (!of_device_is_available(remote->parent)) {
|
||||||
|
dev_warn(dev, "parent device of %s is not available\n",
|
||||||
|
remote->full_name);
|
||||||
|
of_node_put(remote);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
component_match_add(dev, match, compare_of, remote);
|
||||||
|
of_node_put(remote);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rockchip_drm_bind(struct device *dev)
|
||||||
|
{
|
||||||
|
struct drm_device *drm;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
drm = drm_dev_alloc(&rockchip_drm_driver, dev);
|
||||||
|
if (!drm)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = drm_dev_set_unique(drm, "%s", dev_name(dev));
|
||||||
|
if (ret)
|
||||||
|
goto err_free;
|
||||||
|
|
||||||
|
ret = drm_dev_register(drm, 0);
|
||||||
|
if (ret)
|
||||||
|
goto err_free;
|
||||||
|
|
||||||
|
dev_set_drvdata(dev, drm);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_free:
|
||||||
|
drm_dev_unref(drm);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rockchip_drm_unbind(struct device *dev)
|
||||||
|
{
|
||||||
|
struct drm_device *drm = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
drm_dev_unregister(drm);
|
||||||
|
drm_dev_unref(drm);
|
||||||
|
dev_set_drvdata(dev, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct component_master_ops rockchip_drm_ops = {
|
||||||
|
.bind = rockchip_drm_bind,
|
||||||
|
.unbind = rockchip_drm_unbind,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int rockchip_drm_platform_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
struct component_match *match = NULL;
|
||||||
|
struct device_node *np = dev->of_node;
|
||||||
|
struct device_node *port;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!np)
|
||||||
|
return -ENODEV;
|
||||||
|
/*
|
||||||
|
* Bind the crtc ports first, so that
|
||||||
|
* drm_of_find_possible_crtcs called from encoder .bind callbacks
|
||||||
|
* works as expected.
|
||||||
|
*/
|
||||||
|
for (i = 0;; i++) {
|
||||||
|
port = of_parse_phandle(np, "ports", i);
|
||||||
|
if (!port)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!of_device_is_available(port->parent)) {
|
||||||
|
of_node_put(port);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
component_match_add(dev, &match, compare_of, port->parent);
|
||||||
|
of_node_put(port);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 0) {
|
||||||
|
dev_err(dev, "missing 'ports' property\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!match) {
|
||||||
|
dev_err(dev, "No available vop found for display-subsystem.\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* For each bound crtc, bind the encoders attached to its
|
||||||
|
* remote endpoint.
|
||||||
|
*/
|
||||||
|
for (i = 0;; i++) {
|
||||||
|
port = of_parse_phandle(np, "ports", i);
|
||||||
|
if (!port)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!of_device_is_available(port->parent)) {
|
||||||
|
of_node_put(port);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
rockchip_add_endpoints(dev, &match, port);
|
||||||
|
of_node_put(port);
|
||||||
|
}
|
||||||
|
|
||||||
|
return component_master_add_with_match(dev, &rockchip_drm_ops, match);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rockchip_drm_platform_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
component_master_del(&pdev->dev, &rockchip_drm_ops);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id rockchip_drm_dt_ids[] = {
|
||||||
|
{ .compatible = "rockchip,display-subsystem", },
|
||||||
|
{ /* sentinel */ },
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, rockchip_drm_dt_ids);
|
||||||
|
|
||||||
|
static struct platform_driver rockchip_drm_platform_driver = {
|
||||||
|
.probe = rockchip_drm_platform_probe,
|
||||||
|
.remove = rockchip_drm_platform_remove,
|
||||||
|
.driver = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.name = "rockchip-drm",
|
||||||
|
.of_match_table = rockchip_drm_dt_ids,
|
||||||
|
.pm = &rockchip_drm_pm_ops,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(rockchip_drm_platform_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Mark Yao <mark.yao@rock-chips.com>");
|
||||||
|
MODULE_DESCRIPTION("ROCKCHIP DRM Driver");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
|
||||||
|
* Author:Mark Yao <mark.yao@rock-chips.com>
|
||||||
|
*
|
||||||
|
* based on exynos_drm_drv.h
|
||||||
|
*
|
||||||
|
* This software is licensed under the terms of the GNU General Public
|
||||||
|
* License version 2, as published by the Free Software Foundation, and
|
||||||
|
* may be copied, distributed, and modified under those terms.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ROCKCHIP_DRM_DRV_H
|
||||||
|
#define _ROCKCHIP_DRM_DRV_H
|
||||||
|
|
||||||
|
#include <drm/drm_fb_helper.h>
|
||||||
|
#include <drm/drm_gem.h>
|
||||||
|
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/component.h>
|
||||||
|
|
||||||
|
#define ROCKCHIP_MAX_FB_BUFFER 3
|
||||||
|
#define ROCKCHIP_MAX_CONNECTOR 2
|
||||||
|
#define ROCKCHIP_MAX_CRTC 2
|
||||||
|
|
||||||
|
struct drm_device;
|
||||||
|
struct drm_connector;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Rockchip drm private crtc funcs.
|
||||||
|
* @enable_vblank: enable crtc vblank irq.
|
||||||
|
* @disable_vblank: disable crtc vblank irq.
|
||||||
|
*/
|
||||||
|
struct rockchip_crtc_funcs {
|
||||||
|
int (*enable_vblank)(struct drm_crtc *crtc);
|
||||||
|
void (*disable_vblank)(struct drm_crtc *crtc);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Rockchip drm private structure.
|
||||||
|
*
|
||||||
|
* @crtc: array of enabled CRTCs, used to map from "pipe" to drm_crtc.
|
||||||
|
* @num_pipe: number of pipes for this device.
|
||||||
|
*/
|
||||||
|
struct rockchip_drm_private {
|
||||||
|
struct drm_fb_helper fbdev_helper;
|
||||||
|
struct drm_gem_object *fbdev_bo;
|
||||||
|
const struct rockchip_crtc_funcs *crtc_funcs[ROCKCHIP_MAX_CRTC];
|
||||||
|
};
|
||||||
|
|
||||||
|
int rockchip_register_crtc_funcs(struct drm_device *dev,
|
||||||
|
const struct rockchip_crtc_funcs *crtc_funcs,
|
||||||
|
int pipe);
|
||||||
|
void rockchip_unregister_crtc_funcs(struct drm_device *dev, int pipe);
|
||||||
|
int rockchip_drm_encoder_get_mux_id(struct device_node *node,
|
||||||
|
struct drm_encoder *encoder);
|
||||||
|
int rockchip_drm_crtc_mode_config(struct drm_crtc *crtc, int connector_type,
|
||||||
|
int out_mode);
|
||||||
|
int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
|
||||||
|
struct device *dev);
|
||||||
|
void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
|
||||||
|
struct device *dev);
|
||||||
|
|
||||||
|
#endif /* _ROCKCHIP_DRM_DRV_H_ */
|
|
@ -0,0 +1,201 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
|
||||||
|
* Author:Mark Yao <mark.yao@rock-chips.com>
|
||||||
|
*
|
||||||
|
* This software is licensed under the terms of the GNU General Public
|
||||||
|
* License version 2, as published by the Free Software Foundation, and
|
||||||
|
* may be copied, distributed, and modified under those terms.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <drm/drm.h>
|
||||||
|
#include <drm/drmP.h>
|
||||||
|
#include <drm/drm_fb_helper.h>
|
||||||
|
#include <drm/drm_crtc_helper.h>
|
||||||
|
|
||||||
|
#include "rockchip_drm_drv.h"
|
||||||
|
#include "rockchip_drm_gem.h"
|
||||||
|
|
||||||
|
#define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
|
||||||
|
|
||||||
|
struct rockchip_drm_fb {
|
||||||
|
struct drm_framebuffer fb;
|
||||||
|
struct drm_gem_object *obj[ROCKCHIP_MAX_FB_BUFFER];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb,
|
||||||
|
unsigned int plane)
|
||||||
|
{
|
||||||
|
struct rockchip_drm_fb *rk_fb = to_rockchip_fb(fb);
|
||||||
|
|
||||||
|
if (plane >= ROCKCHIP_MAX_FB_BUFFER)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return rk_fb->obj[plane];
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(rockchip_fb_get_gem_obj);
|
||||||
|
|
||||||
|
static void rockchip_drm_fb_destroy(struct drm_framebuffer *fb)
|
||||||
|
{
|
||||||
|
struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
|
||||||
|
struct drm_gem_object *obj;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ROCKCHIP_MAX_FB_BUFFER; i++) {
|
||||||
|
obj = rockchip_fb->obj[i];
|
||||||
|
if (obj)
|
||||||
|
drm_gem_object_unreference_unlocked(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
drm_framebuffer_cleanup(fb);
|
||||||
|
kfree(rockchip_fb);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
|
||||||
|
struct drm_file *file_priv,
|
||||||
|
unsigned int *handle)
|
||||||
|
{
|
||||||
|
struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
|
||||||
|
|
||||||
|
return drm_gem_handle_create(file_priv,
|
||||||
|
rockchip_fb->obj[0], handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
|
||||||
|
.destroy = rockchip_drm_fb_destroy,
|
||||||
|
.create_handle = rockchip_drm_fb_create_handle,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct rockchip_drm_fb *
|
||||||
|
rockchip_fb_alloc(struct drm_device *dev, struct drm_mode_fb_cmd2 *mode_cmd,
|
||||||
|
struct drm_gem_object **obj, unsigned int num_planes)
|
||||||
|
{
|
||||||
|
struct rockchip_drm_fb *rockchip_fb;
|
||||||
|
int ret;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
rockchip_fb = kzalloc(sizeof(*rockchip_fb), GFP_KERNEL);
|
||||||
|
if (!rockchip_fb)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
drm_helper_mode_fill_fb_struct(&rockchip_fb->fb, mode_cmd);
|
||||||
|
|
||||||
|
for (i = 0; i < num_planes; i++)
|
||||||
|
rockchip_fb->obj[i] = obj[i];
|
||||||
|
|
||||||
|
ret = drm_framebuffer_init(dev, &rockchip_fb->fb,
|
||||||
|
&rockchip_drm_fb_funcs);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev->dev, "Failed to initialize framebuffer: %d\n",
|
||||||
|
ret);
|
||||||
|
kfree(rockchip_fb);
|
||||||
|
return ERR_PTR(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rockchip_fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct drm_framebuffer *
|
||||||
|
rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
|
||||||
|
struct drm_mode_fb_cmd2 *mode_cmd)
|
||||||
|
{
|
||||||
|
struct rockchip_drm_fb *rockchip_fb;
|
||||||
|
struct drm_gem_object *objs[ROCKCHIP_MAX_FB_BUFFER];
|
||||||
|
struct drm_gem_object *obj;
|
||||||
|
unsigned int hsub;
|
||||||
|
unsigned int vsub;
|
||||||
|
int num_planes;
|
||||||
|
int ret;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format);
|
||||||
|
vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format);
|
||||||
|
num_planes = min(drm_format_num_planes(mode_cmd->pixel_format),
|
||||||
|
ROCKCHIP_MAX_FB_BUFFER);
|
||||||
|
|
||||||
|
for (i = 0; i < num_planes; i++) {
|
||||||
|
unsigned int width = mode_cmd->width / (i ? hsub : 1);
|
||||||
|
unsigned int height = mode_cmd->height / (i ? vsub : 1);
|
||||||
|
unsigned int min_size;
|
||||||
|
|
||||||
|
obj = drm_gem_object_lookup(dev, file_priv,
|
||||||
|
mode_cmd->handles[i]);
|
||||||
|
if (!obj) {
|
||||||
|
dev_err(dev->dev, "Failed to lookup GEM object\n");
|
||||||
|
ret = -ENXIO;
|
||||||
|
goto err_gem_object_unreference;
|
||||||
|
}
|
||||||
|
|
||||||
|
min_size = (height - 1) * mode_cmd->pitches[i] +
|
||||||
|
mode_cmd->offsets[i] +
|
||||||
|
width * drm_format_plane_cpp(mode_cmd->pixel_format, i);
|
||||||
|
|
||||||
|
if (obj->size < min_size) {
|
||||||
|
drm_gem_object_unreference_unlocked(obj);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto err_gem_object_unreference;
|
||||||
|
}
|
||||||
|
objs[i] = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, objs, i);
|
||||||
|
if (IS_ERR(rockchip_fb)) {
|
||||||
|
ret = PTR_ERR(rockchip_fb);
|
||||||
|
goto err_gem_object_unreference;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &rockchip_fb->fb;
|
||||||
|
|
||||||
|
err_gem_object_unreference:
|
||||||
|
for (i--; i >= 0; i--)
|
||||||
|
drm_gem_object_unreference_unlocked(objs[i]);
|
||||||
|
return ERR_PTR(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rockchip_drm_output_poll_changed(struct drm_device *dev)
|
||||||
|
{
|
||||||
|
struct rockchip_drm_private *private = dev->dev_private;
|
||||||
|
struct drm_fb_helper *fb_helper = &private->fbdev_helper;
|
||||||
|
|
||||||
|
drm_fb_helper_hotplug_event(fb_helper);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
|
||||||
|
.fb_create = rockchip_user_fb_create,
|
||||||
|
.output_poll_changed = rockchip_drm_output_poll_changed,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct drm_framebuffer *
|
||||||
|
rockchip_drm_framebuffer_init(struct drm_device *dev,
|
||||||
|
struct drm_mode_fb_cmd2 *mode_cmd,
|
||||||
|
struct drm_gem_object *obj)
|
||||||
|
{
|
||||||
|
struct rockchip_drm_fb *rockchip_fb;
|
||||||
|
|
||||||
|
rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, &obj, 1);
|
||||||
|
if (IS_ERR(rockchip_fb))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return &rockchip_fb->fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rockchip_drm_mode_config_init(struct drm_device *dev)
|
||||||
|
{
|
||||||
|
dev->mode_config.min_width = 0;
|
||||||
|
dev->mode_config.min_height = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* set max width and height as default value(4096x4096).
|
||||||
|
* this value would be used to check framebuffer size limitation
|
||||||
|
* at drm_mode_addfb().
|
||||||
|
*/
|
||||||
|
dev->mode_config.max_width = 4096;
|
||||||
|
dev->mode_config.max_height = 4096;
|
||||||
|
|
||||||
|
dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
|
||||||
|
* Author:Mark Yao <mark.yao@rock-chips.com>
|
||||||
|
*
|
||||||
|
* This software is licensed under the terms of the GNU General Public
|
||||||
|
* License version 2, as published by the Free Software Foundation, and
|
||||||
|
* may be copied, distributed, and modified under those terms.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ROCKCHIP_DRM_FB_H
|
||||||
|
#define _ROCKCHIP_DRM_FB_H
|
||||||
|
|
||||||
|
struct drm_framebuffer *
|
||||||
|
rockchip_drm_framebuffer_init(struct drm_device *dev,
|
||||||
|
struct drm_mode_fb_cmd2 *mode_cmd,
|
||||||
|
struct drm_gem_object *obj);
|
||||||
|
void rockchip_drm_framebuffer_fini(struct drm_framebuffer *fb);
|
||||||
|
|
||||||
|
void rockchip_drm_mode_config_init(struct drm_device *dev);
|
||||||
|
|
||||||
|
struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb,
|
||||||
|
unsigned int plane);
|
||||||
|
#endif /* _ROCKCHIP_DRM_FB_H */
|
|
@ -0,0 +1,210 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
|
||||||
|
* Author:Mark Yao <mark.yao@rock-chips.com>
|
||||||
|
*
|
||||||
|
* This software is licensed under the terms of the GNU General Public
|
||||||
|
* License version 2, as published by the Free Software Foundation, and
|
||||||
|
* may be copied, distributed, and modified under those terms.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <drm/drm.h>
|
||||||
|
#include <drm/drmP.h>
|
||||||
|
#include <drm/drm_fb_helper.h>
|
||||||
|
#include <drm/drm_crtc_helper.h>
|
||||||
|
|
||||||
|
#include "rockchip_drm_drv.h"
|
||||||
|
#include "rockchip_drm_gem.h"
|
||||||
|
#include "rockchip_drm_fb.h"
|
||||||
|
|
||||||
|
#define PREFERRED_BPP 32
|
||||||
|
#define to_drm_private(x) \
|
||||||
|
container_of(x, struct rockchip_drm_private, fbdev_helper)
|
||||||
|
|
||||||
|
static int rockchip_fbdev_mmap(struct fb_info *info,
|
||||||
|
struct vm_area_struct *vma)
|
||||||
|
{
|
||||||
|
struct drm_fb_helper *helper = info->par;
|
||||||
|
struct rockchip_drm_private *private = to_drm_private(helper);
|
||||||
|
|
||||||
|
return rockchip_gem_mmap_buf(private->fbdev_bo, vma);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct fb_ops rockchip_drm_fbdev_ops = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.fb_mmap = rockchip_fbdev_mmap,
|
||||||
|
.fb_fillrect = cfb_fillrect,
|
||||||
|
.fb_copyarea = cfb_copyarea,
|
||||||
|
.fb_imageblit = cfb_imageblit,
|
||||||
|
.fb_check_var = drm_fb_helper_check_var,
|
||||||
|
.fb_set_par = drm_fb_helper_set_par,
|
||||||
|
.fb_blank = drm_fb_helper_blank,
|
||||||
|
.fb_pan_display = drm_fb_helper_pan_display,
|
||||||
|
.fb_setcmap = drm_fb_helper_setcmap,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper,
|
||||||
|
struct drm_fb_helper_surface_size *sizes)
|
||||||
|
{
|
||||||
|
struct rockchip_drm_private *private = to_drm_private(helper);
|
||||||
|
struct drm_mode_fb_cmd2 mode_cmd = { 0 };
|
||||||
|
struct drm_device *dev = helper->dev;
|
||||||
|
struct rockchip_gem_object *rk_obj;
|
||||||
|
struct drm_framebuffer *fb;
|
||||||
|
unsigned int bytes_per_pixel;
|
||||||
|
unsigned long offset;
|
||||||
|
struct fb_info *fbi;
|
||||||
|
size_t size;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
|
||||||
|
|
||||||
|
mode_cmd.width = sizes->surface_width;
|
||||||
|
mode_cmd.height = sizes->surface_height;
|
||||||
|
mode_cmd.pitches[0] = sizes->surface_width * bytes_per_pixel;
|
||||||
|
mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
|
||||||
|
sizes->surface_depth);
|
||||||
|
|
||||||
|
size = mode_cmd.pitches[0] * mode_cmd.height;
|
||||||
|
|
||||||
|
rk_obj = rockchip_gem_create_object(dev, size);
|
||||||
|
if (IS_ERR(rk_obj))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
private->fbdev_bo = &rk_obj->base;
|
||||||
|
|
||||||
|
fbi = framebuffer_alloc(0, dev->dev);
|
||||||
|
if (!fbi) {
|
||||||
|
dev_err(dev->dev, "Failed to allocate framebuffer info.\n");
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto err_rockchip_gem_free_object;
|
||||||
|
}
|
||||||
|
|
||||||
|
helper->fb = rockchip_drm_framebuffer_init(dev, &mode_cmd,
|
||||||
|
private->fbdev_bo);
|
||||||
|
if (IS_ERR(helper->fb)) {
|
||||||
|
dev_err(dev->dev, "Failed to allocate DRM framebuffer.\n");
|
||||||
|
ret = PTR_ERR(helper->fb);
|
||||||
|
goto err_framebuffer_release;
|
||||||
|
}
|
||||||
|
|
||||||
|
helper->fbdev = fbi;
|
||||||
|
|
||||||
|
fbi->par = helper;
|
||||||
|
fbi->flags = FBINFO_FLAG_DEFAULT;
|
||||||
|
fbi->fbops = &rockchip_drm_fbdev_ops;
|
||||||
|
|
||||||
|
ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev->dev, "Failed to allocate color map.\n");
|
||||||
|
goto err_drm_framebuffer_unref;
|
||||||
|
}
|
||||||
|
|
||||||
|
fb = helper->fb;
|
||||||
|
drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
|
||||||
|
drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
|
||||||
|
|
||||||
|
offset = fbi->var.xoffset * bytes_per_pixel;
|
||||||
|
offset += fbi->var.yoffset * fb->pitches[0];
|
||||||
|
|
||||||
|
dev->mode_config.fb_base = 0;
|
||||||
|
fbi->screen_base = rk_obj->kvaddr + offset;
|
||||||
|
fbi->screen_size = rk_obj->base.size;
|
||||||
|
fbi->fix.smem_len = rk_obj->base.size;
|
||||||
|
|
||||||
|
DRM_DEBUG_KMS("FB [%dx%d]-%d kvaddr=%p offset=%ld size=%d\n",
|
||||||
|
fb->width, fb->height, fb->depth, rk_obj->kvaddr,
|
||||||
|
offset, size);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_drm_framebuffer_unref:
|
||||||
|
drm_framebuffer_unreference(helper->fb);
|
||||||
|
err_framebuffer_release:
|
||||||
|
framebuffer_release(fbi);
|
||||||
|
err_rockchip_gem_free_object:
|
||||||
|
rockchip_gem_free_object(&rk_obj->base);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct drm_fb_helper_funcs rockchip_drm_fb_helper_funcs = {
|
||||||
|
.fb_probe = rockchip_drm_fbdev_create,
|
||||||
|
};
|
||||||
|
|
||||||
|
int rockchip_drm_fbdev_init(struct drm_device *dev)
|
||||||
|
{
|
||||||
|
struct rockchip_drm_private *private = dev->dev_private;
|
||||||
|
struct drm_fb_helper *helper;
|
||||||
|
unsigned int num_crtc;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
num_crtc = dev->mode_config.num_crtc;
|
||||||
|
|
||||||
|
helper = &private->fbdev_helper;
|
||||||
|
|
||||||
|
drm_fb_helper_prepare(dev, helper, &rockchip_drm_fb_helper_funcs);
|
||||||
|
|
||||||
|
ret = drm_fb_helper_init(dev, helper, num_crtc, ROCKCHIP_MAX_CONNECTOR);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(dev->dev, "Failed to initialize drm fb helper - %d.\n",
|
||||||
|
ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = drm_fb_helper_single_add_all_connectors(helper);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(dev->dev, "Failed to add connectors - %d.\n", ret);
|
||||||
|
goto err_drm_fb_helper_fini;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* disable all the possible outputs/crtcs before entering KMS mode */
|
||||||
|
drm_helper_disable_unused_functions(dev);
|
||||||
|
|
||||||
|
ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(dev->dev, "Failed to set initial hw config - %d.\n",
|
||||||
|
ret);
|
||||||
|
goto err_drm_fb_helper_fini;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_drm_fb_helper_fini:
|
||||||
|
drm_fb_helper_fini(helper);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rockchip_drm_fbdev_fini(struct drm_device *dev)
|
||||||
|
{
|
||||||
|
struct rockchip_drm_private *private = dev->dev_private;
|
||||||
|
struct drm_fb_helper *helper;
|
||||||
|
|
||||||
|
helper = &private->fbdev_helper;
|
||||||
|
|
||||||
|
if (helper->fbdev) {
|
||||||
|
struct fb_info *info;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
info = helper->fbdev;
|
||||||
|
ret = unregister_framebuffer(info);
|
||||||
|
if (ret < 0)
|
||||||
|
DRM_DEBUG_KMS("failed unregister_framebuffer() - %d\n",
|
||||||
|
ret);
|
||||||
|
|
||||||
|
if (info->cmap.len)
|
||||||
|
fb_dealloc_cmap(&info->cmap);
|
||||||
|
|
||||||
|
framebuffer_release(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (helper->fb)
|
||||||
|
drm_framebuffer_unreference(helper->fb);
|
||||||
|
|
||||||
|
drm_fb_helper_fini(helper);
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
|
||||||
|
* Author:Mark Yao <mark.yao@rock-chips.com>
|
||||||
|
*
|
||||||
|
* This software is licensed under the terms of the GNU General Public
|
||||||
|
* License version 2, as published by the Free Software Foundation, and
|
||||||
|
* may be copied, distributed, and modified under those terms.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ROCKCHIP_DRM_FBDEV_H
|
||||||
|
#define _ROCKCHIP_DRM_FBDEV_H
|
||||||
|
|
||||||
|
int rockchip_drm_fbdev_init(struct drm_device *dev);
|
||||||
|
void rockchip_drm_fbdev_fini(struct drm_device *dev);
|
||||||
|
|
||||||
|
#endif /* _ROCKCHIP_DRM_FBDEV_H */
|
|
@ -0,0 +1,294 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
|
||||||
|
* Author:Mark Yao <mark.yao@rock-chips.com>
|
||||||
|
*
|
||||||
|
* This software is licensed under the terms of the GNU General Public
|
||||||
|
* License version 2, as published by the Free Software Foundation, and
|
||||||
|
* may be copied, distributed, and modified under those terms.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <drm/drm.h>
|
||||||
|
#include <drm/drmP.h>
|
||||||
|
#include <drm/drm_gem.h>
|
||||||
|
#include <drm/drm_vma_manager.h>
|
||||||
|
|
||||||
|
#include <linux/dma-attrs.h>
|
||||||
|
|
||||||
|
#include "rockchip_drm_drv.h"
|
||||||
|
#include "rockchip_drm_gem.h"
|
||||||
|
|
||||||
|
static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj)
|
||||||
|
{
|
||||||
|
struct drm_gem_object *obj = &rk_obj->base;
|
||||||
|
struct drm_device *drm = obj->dev;
|
||||||
|
|
||||||
|
init_dma_attrs(&rk_obj->dma_attrs);
|
||||||
|
dma_set_attr(DMA_ATTR_WRITE_COMBINE, &rk_obj->dma_attrs);
|
||||||
|
|
||||||
|
/* TODO(djkurtz): Use DMA_ATTR_NO_KERNEL_MAPPING except for fbdev */
|
||||||
|
rk_obj->kvaddr = dma_alloc_attrs(drm->dev, obj->size,
|
||||||
|
&rk_obj->dma_addr, GFP_KERNEL,
|
||||||
|
&rk_obj->dma_attrs);
|
||||||
|
if (IS_ERR(rk_obj->kvaddr)) {
|
||||||
|
int ret = PTR_ERR(rk_obj->kvaddr);
|
||||||
|
|
||||||
|
DRM_ERROR("failed to allocate %#x byte dma buffer, %d",
|
||||||
|
obj->size, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rockchip_gem_free_buf(struct rockchip_gem_object *rk_obj)
|
||||||
|
{
|
||||||
|
struct drm_gem_object *obj = &rk_obj->base;
|
||||||
|
struct drm_device *drm = obj->dev;
|
||||||
|
|
||||||
|
dma_free_attrs(drm->dev, obj->size, rk_obj->kvaddr, rk_obj->dma_addr,
|
||||||
|
&rk_obj->dma_attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
int rockchip_gem_mmap_buf(struct drm_gem_object *obj,
|
||||||
|
struct vm_area_struct *vma)
|
||||||
|
{
|
||||||
|
struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
|
||||||
|
struct drm_device *drm = obj->dev;
|
||||||
|
unsigned long vm_size;
|
||||||
|
|
||||||
|
vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
|
||||||
|
vm_size = vma->vm_end - vma->vm_start;
|
||||||
|
|
||||||
|
if (vm_size > obj->size)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return dma_mmap_attrs(drm->dev, vma, rk_obj->kvaddr, rk_obj->dma_addr,
|
||||||
|
obj->size, &rk_obj->dma_attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* drm driver mmap file operations */
|
||||||
|
int rockchip_gem_mmap(struct file *filp, struct vm_area_struct *vma)
|
||||||
|
{
|
||||||
|
struct drm_file *priv = filp->private_data;
|
||||||
|
struct drm_device *dev = priv->minor->dev;
|
||||||
|
struct drm_gem_object *obj;
|
||||||
|
struct drm_vma_offset_node *node;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (drm_device_is_unplugged(dev))
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
mutex_lock(&dev->struct_mutex);
|
||||||
|
|
||||||
|
node = drm_vma_offset_exact_lookup(dev->vma_offset_manager,
|
||||||
|
vma->vm_pgoff,
|
||||||
|
vma_pages(vma));
|
||||||
|
if (!node) {
|
||||||
|
mutex_unlock(&dev->struct_mutex);
|
||||||
|
DRM_ERROR("failed to find vma node.\n");
|
||||||
|
return -EINVAL;
|
||||||
|
} else if (!drm_vma_node_is_allowed(node, filp)) {
|
||||||
|
mutex_unlock(&dev->struct_mutex);
|
||||||
|
return -EACCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = container_of(node, struct drm_gem_object, vma_node);
|
||||||
|
ret = rockchip_gem_mmap_buf(obj, vma);
|
||||||
|
|
||||||
|
mutex_unlock(&dev->struct_mutex);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct rockchip_gem_object *
|
||||||
|
rockchip_gem_create_object(struct drm_device *drm, unsigned int size)
|
||||||
|
{
|
||||||
|
struct rockchip_gem_object *rk_obj;
|
||||||
|
struct drm_gem_object *obj;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
size = round_up(size, PAGE_SIZE);
|
||||||
|
|
||||||
|
rk_obj = kzalloc(sizeof(*rk_obj), GFP_KERNEL);
|
||||||
|
if (!rk_obj)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
obj = &rk_obj->base;
|
||||||
|
|
||||||
|
drm_gem_private_object_init(drm, obj, size);
|
||||||
|
|
||||||
|
ret = rockchip_gem_alloc_buf(rk_obj);
|
||||||
|
if (ret)
|
||||||
|
goto err_free_rk_obj;
|
||||||
|
|
||||||
|
return rk_obj;
|
||||||
|
|
||||||
|
err_free_rk_obj:
|
||||||
|
kfree(rk_obj);
|
||||||
|
return ERR_PTR(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* rockchip_gem_free_object - (struct drm_driver)->gem_free_object callback
|
||||||
|
* function
|
||||||
|
*/
|
||||||
|
void rockchip_gem_free_object(struct drm_gem_object *obj)
|
||||||
|
{
|
||||||
|
struct rockchip_gem_object *rk_obj;
|
||||||
|
|
||||||
|
drm_gem_free_mmap_offset(obj);
|
||||||
|
|
||||||
|
rk_obj = to_rockchip_obj(obj);
|
||||||
|
|
||||||
|
rockchip_gem_free_buf(rk_obj);
|
||||||
|
|
||||||
|
kfree(rk_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* rockchip_gem_create_with_handle - allocate an object with the given
|
||||||
|
* size and create a gem handle on it
|
||||||
|
*
|
||||||
|
* returns a struct rockchip_gem_object* on success or ERR_PTR values
|
||||||
|
* on failure.
|
||||||
|
*/
|
||||||
|
static struct rockchip_gem_object *
|
||||||
|
rockchip_gem_create_with_handle(struct drm_file *file_priv,
|
||||||
|
struct drm_device *drm, unsigned int size,
|
||||||
|
unsigned int *handle)
|
||||||
|
{
|
||||||
|
struct rockchip_gem_object *rk_obj;
|
||||||
|
struct drm_gem_object *obj;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
rk_obj = rockchip_gem_create_object(drm, size);
|
||||||
|
if (IS_ERR(rk_obj))
|
||||||
|
return ERR_CAST(rk_obj);
|
||||||
|
|
||||||
|
obj = &rk_obj->base;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* allocate a id of idr table where the obj is registered
|
||||||
|
* and handle has the id what user can see.
|
||||||
|
*/
|
||||||
|
ret = drm_gem_handle_create(file_priv, obj, handle);
|
||||||
|
if (ret)
|
||||||
|
goto err_handle_create;
|
||||||
|
|
||||||
|
/* drop reference from allocate - handle holds it now. */
|
||||||
|
drm_gem_object_unreference_unlocked(obj);
|
||||||
|
|
||||||
|
return rk_obj;
|
||||||
|
|
||||||
|
err_handle_create:
|
||||||
|
rockchip_gem_free_object(obj);
|
||||||
|
|
||||||
|
return ERR_PTR(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
int rockchip_gem_dumb_map_offset(struct drm_file *file_priv,
|
||||||
|
struct drm_device *dev, uint32_t handle,
|
||||||
|
uint64_t *offset)
|
||||||
|
{
|
||||||
|
struct drm_gem_object *obj;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mutex_lock(&dev->struct_mutex);
|
||||||
|
|
||||||
|
obj = drm_gem_object_lookup(dev, file_priv, handle);
|
||||||
|
if (!obj) {
|
||||||
|
DRM_ERROR("failed to lookup gem object.\n");
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = drm_gem_create_mmap_offset(obj);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
*offset = drm_vma_node_offset_addr(&obj->vma_node);
|
||||||
|
DRM_DEBUG_KMS("offset = 0x%llx\n", *offset);
|
||||||
|
|
||||||
|
out:
|
||||||
|
drm_gem_object_unreference(obj);
|
||||||
|
unlock:
|
||||||
|
mutex_unlock(&dev->struct_mutex);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* rockchip_gem_dumb_create - (struct drm_driver)->dumb_create callback
|
||||||
|
* function
|
||||||
|
*
|
||||||
|
* This aligns the pitch and size arguments to the minimum required. wrap
|
||||||
|
* this into your own function if you need bigger alignment.
|
||||||
|
*/
|
||||||
|
int rockchip_gem_dumb_create(struct drm_file *file_priv,
|
||||||
|
struct drm_device *dev,
|
||||||
|
struct drm_mode_create_dumb *args)
|
||||||
|
{
|
||||||
|
struct rockchip_gem_object *rk_obj;
|
||||||
|
int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* align to 64 bytes since Mali requires it.
|
||||||
|
*/
|
||||||
|
min_pitch = ALIGN(min_pitch, 64);
|
||||||
|
|
||||||
|
if (args->pitch < min_pitch)
|
||||||
|
args->pitch = min_pitch;
|
||||||
|
|
||||||
|
if (args->size < args->pitch * args->height)
|
||||||
|
args->size = args->pitch * args->height;
|
||||||
|
|
||||||
|
rk_obj = rockchip_gem_create_with_handle(file_priv, dev, args->size,
|
||||||
|
&args->handle);
|
||||||
|
|
||||||
|
return PTR_ERR_OR_ZERO(rk_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate a sg_table for this GEM object.
|
||||||
|
* Note: Both the table's contents, and the sg_table itself must be freed by
|
||||||
|
* the caller.
|
||||||
|
* Returns a pointer to the newly allocated sg_table, or an ERR_PTR() error.
|
||||||
|
*/
|
||||||
|
struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj)
|
||||||
|
{
|
||||||
|
struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
|
||||||
|
struct drm_device *drm = obj->dev;
|
||||||
|
struct sg_table *sgt;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
|
||||||
|
if (!sgt)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
ret = dma_get_sgtable_attrs(drm->dev, sgt, rk_obj->kvaddr,
|
||||||
|
rk_obj->dma_addr, obj->size,
|
||||||
|
&rk_obj->dma_attrs);
|
||||||
|
if (ret) {
|
||||||
|
DRM_ERROR("failed to allocate sgt, %d\n", ret);
|
||||||
|
kfree(sgt);
|
||||||
|
return ERR_PTR(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sgt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *rockchip_gem_prime_vmap(struct drm_gem_object *obj)
|
||||||
|
{
|
||||||
|
struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
|
||||||
|
|
||||||
|
return rk_obj->kvaddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
|
||||||
|
{
|
||||||
|
/* Nothing to do */
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
|
||||||
|
* Author:Mark Yao <mark.yao@rock-chips.com>
|
||||||
|
*
|
||||||
|
* This software is licensed under the terms of the GNU General Public
|
||||||
|
* License version 2, as published by the Free Software Foundation, and
|
||||||
|
* may be copied, distributed, and modified under those terms.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ROCKCHIP_DRM_GEM_H
|
||||||
|
#define _ROCKCHIP_DRM_GEM_H
|
||||||
|
|
||||||
|
#define to_rockchip_obj(x) container_of(x, struct rockchip_gem_object, base)
|
||||||
|
|
||||||
|
struct rockchip_gem_object {
|
||||||
|
struct drm_gem_object base;
|
||||||
|
unsigned int flags;
|
||||||
|
|
||||||
|
void *kvaddr;
|
||||||
|
dma_addr_t dma_addr;
|
||||||
|
struct dma_attrs dma_attrs;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj);
|
||||||
|
struct drm_gem_object *
|
||||||
|
rockchip_gem_prime_import_sg_table(struct drm_device *dev, size_t size,
|
||||||
|
struct sg_table *sgt);
|
||||||
|
void *rockchip_gem_prime_vmap(struct drm_gem_object *obj);
|
||||||
|
void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
|
||||||
|
|
||||||
|
/* drm driver mmap file operations */
|
||||||
|
int rockchip_gem_mmap(struct file *filp, struct vm_area_struct *vma);
|
||||||
|
|
||||||
|
/* mmap a gem object to userspace. */
|
||||||
|
int rockchip_gem_mmap_buf(struct drm_gem_object *obj,
|
||||||
|
struct vm_area_struct *vma);
|
||||||
|
|
||||||
|
struct rockchip_gem_object *
|
||||||
|
rockchip_gem_create_object(struct drm_device *drm, unsigned int size);
|
||||||
|
|
||||||
|
void rockchip_gem_free_object(struct drm_gem_object *obj);
|
||||||
|
|
||||||
|
int rockchip_gem_dumb_create(struct drm_file *file_priv,
|
||||||
|
struct drm_device *dev,
|
||||||
|
struct drm_mode_create_dumb *args);
|
||||||
|
int rockchip_gem_dumb_map_offset(struct drm_file *file_priv,
|
||||||
|
struct drm_device *dev, uint32_t handle,
|
||||||
|
uint64_t *offset);
|
||||||
|
#endif /* _ROCKCHIP_DRM_GEM_H */
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,201 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
|
||||||
|
* Author:Mark Yao <mark.yao@rock-chips.com>
|
||||||
|
*
|
||||||
|
* This software is licensed under the terms of the GNU General Public
|
||||||
|
* License version 2, as published by the Free Software Foundation, and
|
||||||
|
* may be copied, distributed, and modified under those terms.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ROCKCHIP_DRM_VOP_H
|
||||||
|
#define _ROCKCHIP_DRM_VOP_H
|
||||||
|
|
||||||
|
/* register definition */
|
||||||
|
#define REG_CFG_DONE 0x0000
|
||||||
|
#define VERSION_INFO 0x0004
|
||||||
|
#define SYS_CTRL 0x0008
|
||||||
|
#define SYS_CTRL1 0x000c
|
||||||
|
#define DSP_CTRL0 0x0010
|
||||||
|
#define DSP_CTRL1 0x0014
|
||||||
|
#define DSP_BG 0x0018
|
||||||
|
#define MCU_CTRL 0x001c
|
||||||
|
#define INTR_CTRL0 0x0020
|
||||||
|
#define INTR_CTRL1 0x0024
|
||||||
|
#define WIN0_CTRL0 0x0030
|
||||||
|
#define WIN0_CTRL1 0x0034
|
||||||
|
#define WIN0_COLOR_KEY 0x0038
|
||||||
|
#define WIN0_VIR 0x003c
|
||||||
|
#define WIN0_YRGB_MST 0x0040
|
||||||
|
#define WIN0_CBR_MST 0x0044
|
||||||
|
#define WIN0_ACT_INFO 0x0048
|
||||||
|
#define WIN0_DSP_INFO 0x004c
|
||||||
|
#define WIN0_DSP_ST 0x0050
|
||||||
|
#define WIN0_SCL_FACTOR_YRGB 0x0054
|
||||||
|
#define WIN0_SCL_FACTOR_CBR 0x0058
|
||||||
|
#define WIN0_SCL_OFFSET 0x005c
|
||||||
|
#define WIN0_SRC_ALPHA_CTRL 0x0060
|
||||||
|
#define WIN0_DST_ALPHA_CTRL 0x0064
|
||||||
|
#define WIN0_FADING_CTRL 0x0068
|
||||||
|
/* win1 register */
|
||||||
|
#define WIN1_CTRL0 0x0070
|
||||||
|
#define WIN1_CTRL1 0x0074
|
||||||
|
#define WIN1_COLOR_KEY 0x0078
|
||||||
|
#define WIN1_VIR 0x007c
|
||||||
|
#define WIN1_YRGB_MST 0x0080
|
||||||
|
#define WIN1_CBR_MST 0x0084
|
||||||
|
#define WIN1_ACT_INFO 0x0088
|
||||||
|
#define WIN1_DSP_INFO 0x008c
|
||||||
|
#define WIN1_DSP_ST 0x0090
|
||||||
|
#define WIN1_SCL_FACTOR_YRGB 0x0094
|
||||||
|
#define WIN1_SCL_FACTOR_CBR 0x0098
|
||||||
|
#define WIN1_SCL_OFFSET 0x009c
|
||||||
|
#define WIN1_SRC_ALPHA_CTRL 0x00a0
|
||||||
|
#define WIN1_DST_ALPHA_CTRL 0x00a4
|
||||||
|
#define WIN1_FADING_CTRL 0x00a8
|
||||||
|
/* win2 register */
|
||||||
|
#define WIN2_CTRL0 0x00b0
|
||||||
|
#define WIN2_CTRL1 0x00b4
|
||||||
|
#define WIN2_VIR0_1 0x00b8
|
||||||
|
#define WIN2_VIR2_3 0x00bc
|
||||||
|
#define WIN2_MST0 0x00c0
|
||||||
|
#define WIN2_DSP_INFO0 0x00c4
|
||||||
|
#define WIN2_DSP_ST0 0x00c8
|
||||||
|
#define WIN2_COLOR_KEY 0x00cc
|
||||||
|
#define WIN2_MST1 0x00d0
|
||||||
|
#define WIN2_DSP_INFO1 0x00d4
|
||||||
|
#define WIN2_DSP_ST1 0x00d8
|
||||||
|
#define WIN2_SRC_ALPHA_CTRL 0x00dc
|
||||||
|
#define WIN2_MST2 0x00e0
|
||||||
|
#define WIN2_DSP_INFO2 0x00e4
|
||||||
|
#define WIN2_DSP_ST2 0x00e8
|
||||||
|
#define WIN2_DST_ALPHA_CTRL 0x00ec
|
||||||
|
#define WIN2_MST3 0x00f0
|
||||||
|
#define WIN2_DSP_INFO3 0x00f4
|
||||||
|
#define WIN2_DSP_ST3 0x00f8
|
||||||
|
#define WIN2_FADING_CTRL 0x00fc
|
||||||
|
/* win3 register */
|
||||||
|
#define WIN3_CTRL0 0x0100
|
||||||
|
#define WIN3_CTRL1 0x0104
|
||||||
|
#define WIN3_VIR0_1 0x0108
|
||||||
|
#define WIN3_VIR2_3 0x010c
|
||||||
|
#define WIN3_MST0 0x0110
|
||||||
|
#define WIN3_DSP_INFO0 0x0114
|
||||||
|
#define WIN3_DSP_ST0 0x0118
|
||||||
|
#define WIN3_COLOR_KEY 0x011c
|
||||||
|
#define WIN3_MST1 0x0120
|
||||||
|
#define WIN3_DSP_INFO1 0x0124
|
||||||
|
#define WIN3_DSP_ST1 0x0128
|
||||||
|
#define WIN3_SRC_ALPHA_CTRL 0x012c
|
||||||
|
#define WIN3_MST2 0x0130
|
||||||
|
#define WIN3_DSP_INFO2 0x0134
|
||||||
|
#define WIN3_DSP_ST2 0x0138
|
||||||
|
#define WIN3_DST_ALPHA_CTRL 0x013c
|
||||||
|
#define WIN3_MST3 0x0140
|
||||||
|
#define WIN3_DSP_INFO3 0x0144
|
||||||
|
#define WIN3_DSP_ST3 0x0148
|
||||||
|
#define WIN3_FADING_CTRL 0x014c
|
||||||
|
/* hwc register */
|
||||||
|
#define HWC_CTRL0 0x0150
|
||||||
|
#define HWC_CTRL1 0x0154
|
||||||
|
#define HWC_MST 0x0158
|
||||||
|
#define HWC_DSP_ST 0x015c
|
||||||
|
#define HWC_SRC_ALPHA_CTRL 0x0160
|
||||||
|
#define HWC_DST_ALPHA_CTRL 0x0164
|
||||||
|
#define HWC_FADING_CTRL 0x0168
|
||||||
|
/* post process register */
|
||||||
|
#define POST_DSP_HACT_INFO 0x0170
|
||||||
|
#define POST_DSP_VACT_INFO 0x0174
|
||||||
|
#define POST_SCL_FACTOR_YRGB 0x0178
|
||||||
|
#define POST_SCL_CTRL 0x0180
|
||||||
|
#define POST_DSP_VACT_INFO_F1 0x0184
|
||||||
|
#define DSP_HTOTAL_HS_END 0x0188
|
||||||
|
#define DSP_HACT_ST_END 0x018c
|
||||||
|
#define DSP_VTOTAL_VS_END 0x0190
|
||||||
|
#define DSP_VACT_ST_END 0x0194
|
||||||
|
#define DSP_VS_ST_END_F1 0x0198
|
||||||
|
#define DSP_VACT_ST_END_F1 0x019c
|
||||||
|
/* register definition end */
|
||||||
|
|
||||||
|
/* interrupt define */
|
||||||
|
#define DSP_HOLD_VALID_INTR (1 << 0)
|
||||||
|
#define FS_INTR (1 << 1)
|
||||||
|
#define LINE_FLAG_INTR (1 << 2)
|
||||||
|
#define BUS_ERROR_INTR (1 << 3)
|
||||||
|
|
||||||
|
#define INTR_MASK (DSP_HOLD_VALID_INTR | FS_INTR | \
|
||||||
|
LINE_FLAG_INTR | BUS_ERROR_INTR)
|
||||||
|
|
||||||
|
#define DSP_HOLD_VALID_INTR_EN(x) ((x) << 4)
|
||||||
|
#define FS_INTR_EN(x) ((x) << 5)
|
||||||
|
#define LINE_FLAG_INTR_EN(x) ((x) << 6)
|
||||||
|
#define BUS_ERROR_INTR_EN(x) ((x) << 7)
|
||||||
|
#define DSP_HOLD_VALID_INTR_MASK (1 << 4)
|
||||||
|
#define FS_INTR_MASK (1 << 5)
|
||||||
|
#define LINE_FLAG_INTR_MASK (1 << 6)
|
||||||
|
#define BUS_ERROR_INTR_MASK (1 << 7)
|
||||||
|
|
||||||
|
#define INTR_CLR_SHIFT 8
|
||||||
|
#define DSP_HOLD_VALID_INTR_CLR (1 << (INTR_CLR_SHIFT + 0))
|
||||||
|
#define FS_INTR_CLR (1 << (INTR_CLR_SHIFT + 1))
|
||||||
|
#define LINE_FLAG_INTR_CLR (1 << (INTR_CLR_SHIFT + 2))
|
||||||
|
#define BUS_ERROR_INTR_CLR (1 << (INTR_CLR_SHIFT + 3))
|
||||||
|
|
||||||
|
#define DSP_LINE_NUM(x) (((x) & 0x1fff) << 12)
|
||||||
|
#define DSP_LINE_NUM_MASK (0x1fff << 12)
|
||||||
|
|
||||||
|
/* src alpha ctrl define */
|
||||||
|
#define SRC_FADING_VALUE(x) (((x) & 0xff) << 24)
|
||||||
|
#define SRC_GLOBAL_ALPHA(x) (((x) & 0xff) << 16)
|
||||||
|
#define SRC_FACTOR_M0(x) (((x) & 0x7) << 6)
|
||||||
|
#define SRC_ALPHA_CAL_M0(x) (((x) & 0x1) << 5)
|
||||||
|
#define SRC_BLEND_M0(x) (((x) & 0x3) << 3)
|
||||||
|
#define SRC_ALPHA_M0(x) (((x) & 0x1) << 2)
|
||||||
|
#define SRC_COLOR_M0(x) (((x) & 0x1) << 1)
|
||||||
|
#define SRC_ALPHA_EN(x) (((x) & 0x1) << 0)
|
||||||
|
/* dst alpha ctrl define */
|
||||||
|
#define DST_FACTOR_M0(x) (((x) & 0x7) << 6)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* display output interface supported by rockchip lcdc
|
||||||
|
*/
|
||||||
|
#define ROCKCHIP_OUT_MODE_P888 0
|
||||||
|
#define ROCKCHIP_OUT_MODE_P666 1
|
||||||
|
#define ROCKCHIP_OUT_MODE_P565 2
|
||||||
|
/* for use special outface */
|
||||||
|
#define ROCKCHIP_OUT_MODE_AAAA 15
|
||||||
|
|
||||||
|
enum alpha_mode {
|
||||||
|
ALPHA_STRAIGHT,
|
||||||
|
ALPHA_INVERSE,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum global_blend_mode {
|
||||||
|
ALPHA_GLOBAL,
|
||||||
|
ALPHA_PER_PIX,
|
||||||
|
ALPHA_PER_PIX_GLOBAL,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum alpha_cal_mode {
|
||||||
|
ALPHA_SATURATION,
|
||||||
|
ALPHA_NO_SATURATION,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum color_mode {
|
||||||
|
ALPHA_SRC_PRE_MUL,
|
||||||
|
ALPHA_SRC_NO_PRE_MUL,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum factor_mode {
|
||||||
|
ALPHA_ZERO,
|
||||||
|
ALPHA_ONE,
|
||||||
|
ALPHA_SRC,
|
||||||
|
ALPHA_SRC_INVERSE,
|
||||||
|
ALPHA_SRC_GLOBAL,
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _ROCKCHIP_DRM_VOP_H */
|
|
@ -152,6 +152,18 @@ config OMAP_IOMMU_DEBUG
|
||||||
|
|
||||||
Say N unless you know you need this.
|
Say N unless you know you need this.
|
||||||
|
|
||||||
|
config ROCKCHIP_IOMMU
|
||||||
|
bool "Rockchip IOMMU Support"
|
||||||
|
depends on ARCH_ROCKCHIP
|
||||||
|
select IOMMU_API
|
||||||
|
select ARM_DMA_USE_IOMMU
|
||||||
|
help
|
||||||
|
Support for IOMMUs found on Rockchip rk32xx SOCs.
|
||||||
|
These IOMMUs allow virtualization of the address space used by most
|
||||||
|
cores within the multimedia subsystem.
|
||||||
|
Say Y here if you are using a Rockchip SoC that includes an IOMMU
|
||||||
|
device.
|
||||||
|
|
||||||
config TEGRA_IOMMU_GART
|
config TEGRA_IOMMU_GART
|
||||||
bool "Tegra GART IOMMU Support"
|
bool "Tegra GART IOMMU Support"
|
||||||
depends on ARCH_TEGRA_2x_SOC
|
depends on ARCH_TEGRA_2x_SOC
|
||||||
|
|
|
@ -13,6 +13,7 @@ obj-$(CONFIG_IRQ_REMAP) += intel_irq_remapping.o irq_remapping.o
|
||||||
obj-$(CONFIG_OMAP_IOMMU) += omap-iommu.o
|
obj-$(CONFIG_OMAP_IOMMU) += omap-iommu.o
|
||||||
obj-$(CONFIG_OMAP_IOMMU) += omap-iommu2.o
|
obj-$(CONFIG_OMAP_IOMMU) += omap-iommu2.o
|
||||||
obj-$(CONFIG_OMAP_IOMMU_DEBUG) += omap-iommu-debug.o
|
obj-$(CONFIG_OMAP_IOMMU_DEBUG) += omap-iommu-debug.o
|
||||||
|
obj-$(CONFIG_ROCKCHIP_IOMMU) += rockchip-iommu.o
|
||||||
obj-$(CONFIG_TEGRA_IOMMU_GART) += tegra-gart.o
|
obj-$(CONFIG_TEGRA_IOMMU_GART) += tegra-gart.o
|
||||||
obj-$(CONFIG_TEGRA_IOMMU_SMMU) += tegra-smmu.o
|
obj-$(CONFIG_TEGRA_IOMMU_SMMU) += tegra-smmu.o
|
||||||
obj-$(CONFIG_EXYNOS_IOMMU) += exynos-iommu.o
|
obj-$(CONFIG_EXYNOS_IOMMU) += exynos-iommu.o
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Загрузка…
Ссылка в новой задаче