This patch adds the basic structure of a DRM Driver for Rockchip Socs. Signed-off-by: Mark Yao <mark.yao@rock-chips.com> Signed-off-by: Daniel Kurtz <djkurtz@chromium.org> Acked-by: Daniel Vetter <daniel@ffwll.ch> Reviewed-by: Rob Clark <robdclark@gmail.com>tirimbino
parent
656d7077d8
commit
2048e3286f
@ -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 */ |
File diff suppressed because it is too large
Load Diff
@ -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 */ |
Loading…
Reference in new issue