forked from Minki/linux
88e72717c2
This continues the pattern started in commit cc1ef118fc
("drm/irq:
Make pipe unsigned and name consistent"). This is applied to the public
APIs and driver callbacks, so pretty much all drivers need to be updated
to match the new prototypes.
Cc: Christian König <christian.koenig@amd.com>
Cc: Alex Deucher <alexander.deucher@amd.com>
Cc: Russell King <rmk+kernel@arm.linux.org.uk>
Cc: Inki Dae <inki.dae@samsung.com>
Cc: Jianwei Wang <jianwei.wang.chn@gmail.com>
Cc: Alison Wang <alison.wang@freescale.com>
Cc: Patrik Jakobsson <patrik.r.jakobsson@gmail.com>
Cc: Daniel Vetter <daniel.vetter@intel.com>
Cc: Jani Nikula <jani.nikula@linux.intel.com>
Cc: Philipp Zabel <p.zabel@pengutronix.de>
Cc: David Airlie <airlied@linux.ie>
Cc: Rob Clark <robdclark@gmail.com>
Cc: Ben Skeggs <bskeggs@redhat.com>
Cc: Tomi Valkeinen <tomi.valkeinen@ti.com>
Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Cc: Mark Yao <mark.yao@rock-chips.com>
Cc: Benjamin Gaignard <benjamin.gaignard@linaro.org>
Cc: Vincent Abriou <vincent.abriou@st.com>
Cc: Thomas Hellstrom <thellstrom@vmware.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
351 lines
8.8 KiB
C
351 lines
8.8 KiB
C
/*
|
|
* rcar_du_drv.c -- R-Car Display Unit DRM driver
|
|
*
|
|
* Copyright (C) 2013-2014 Renesas Electronics Corporation
|
|
*
|
|
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*/
|
|
|
|
#include <linux/clk.h>
|
|
#include <linux/io.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/pm.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/wait.h>
|
|
|
|
#include <drm/drmP.h>
|
|
#include <drm/drm_crtc_helper.h>
|
|
#include <drm/drm_fb_cma_helper.h>
|
|
#include <drm/drm_gem_cma_helper.h>
|
|
|
|
#include "rcar_du_crtc.h"
|
|
#include "rcar_du_drv.h"
|
|
#include "rcar_du_kms.h"
|
|
#include "rcar_du_regs.h"
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* Device Information
|
|
*/
|
|
|
|
static const struct rcar_du_device_info rcar_du_r8a7779_info = {
|
|
.features = 0,
|
|
.num_crtcs = 2,
|
|
.routes = {
|
|
/* R8A7779 has two RGB outputs and one (currently unsupported)
|
|
* TCON output.
|
|
*/
|
|
[RCAR_DU_OUTPUT_DPAD0] = {
|
|
.possible_crtcs = BIT(0),
|
|
.encoder_type = DRM_MODE_ENCODER_NONE,
|
|
.port = 0,
|
|
},
|
|
[RCAR_DU_OUTPUT_DPAD1] = {
|
|
.possible_crtcs = BIT(1) | BIT(0),
|
|
.encoder_type = DRM_MODE_ENCODER_NONE,
|
|
.port = 1,
|
|
},
|
|
},
|
|
.num_lvds = 0,
|
|
};
|
|
|
|
static const struct rcar_du_device_info rcar_du_r8a7790_info = {
|
|
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
|
|
| RCAR_DU_FEATURE_EXT_CTRL_REGS,
|
|
.quirks = RCAR_DU_QUIRK_ALIGN_128B | RCAR_DU_QUIRK_LVDS_LANES,
|
|
.num_crtcs = 3,
|
|
.routes = {
|
|
/* R8A7790 has one RGB output, two LVDS outputs and one
|
|
* (currently unsupported) TCON output.
|
|
*/
|
|
[RCAR_DU_OUTPUT_DPAD0] = {
|
|
.possible_crtcs = BIT(2) | BIT(1) | BIT(0),
|
|
.encoder_type = DRM_MODE_ENCODER_NONE,
|
|
.port = 0,
|
|
},
|
|
[RCAR_DU_OUTPUT_LVDS0] = {
|
|
.possible_crtcs = BIT(0),
|
|
.encoder_type = DRM_MODE_ENCODER_LVDS,
|
|
.port = 1,
|
|
},
|
|
[RCAR_DU_OUTPUT_LVDS1] = {
|
|
.possible_crtcs = BIT(2) | BIT(1),
|
|
.encoder_type = DRM_MODE_ENCODER_LVDS,
|
|
.port = 2,
|
|
},
|
|
},
|
|
.num_lvds = 2,
|
|
};
|
|
|
|
static const struct rcar_du_device_info rcar_du_r8a7791_info = {
|
|
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
|
|
| RCAR_DU_FEATURE_EXT_CTRL_REGS,
|
|
.num_crtcs = 2,
|
|
.routes = {
|
|
/* R8A7791 has one RGB output, one LVDS output and one
|
|
* (currently unsupported) TCON output.
|
|
*/
|
|
[RCAR_DU_OUTPUT_DPAD0] = {
|
|
.possible_crtcs = BIT(1),
|
|
.encoder_type = DRM_MODE_ENCODER_NONE,
|
|
.port = 0,
|
|
},
|
|
[RCAR_DU_OUTPUT_LVDS0] = {
|
|
.possible_crtcs = BIT(0),
|
|
.encoder_type = DRM_MODE_ENCODER_LVDS,
|
|
.port = 1,
|
|
},
|
|
},
|
|
.num_lvds = 1,
|
|
};
|
|
|
|
static const struct platform_device_id rcar_du_id_table[] = {
|
|
{ "rcar-du-r8a7779", (kernel_ulong_t)&rcar_du_r8a7779_info },
|
|
{ "rcar-du-r8a7790", (kernel_ulong_t)&rcar_du_r8a7790_info },
|
|
{ "rcar-du-r8a7791", (kernel_ulong_t)&rcar_du_r8a7791_info },
|
|
{ }
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(platform, rcar_du_id_table);
|
|
|
|
static const struct of_device_id rcar_du_of_table[] = {
|
|
{ .compatible = "renesas,du-r8a7779", .data = &rcar_du_r8a7779_info },
|
|
{ .compatible = "renesas,du-r8a7790", .data = &rcar_du_r8a7790_info },
|
|
{ .compatible = "renesas,du-r8a7791", .data = &rcar_du_r8a7791_info },
|
|
{ }
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(of, rcar_du_of_table);
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* DRM operations
|
|
*/
|
|
|
|
static int rcar_du_unload(struct drm_device *dev)
|
|
{
|
|
struct rcar_du_device *rcdu = dev->dev_private;
|
|
|
|
if (rcdu->fbdev)
|
|
drm_fbdev_cma_fini(rcdu->fbdev);
|
|
|
|
drm_kms_helper_poll_fini(dev);
|
|
drm_mode_config_cleanup(dev);
|
|
drm_vblank_cleanup(dev);
|
|
|
|
dev->irq_enabled = 0;
|
|
dev->dev_private = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rcar_du_load(struct drm_device *dev, unsigned long flags)
|
|
{
|
|
struct platform_device *pdev = dev->platformdev;
|
|
struct device_node *np = pdev->dev.of_node;
|
|
struct rcar_du_device *rcdu;
|
|
struct resource *mem;
|
|
int ret;
|
|
|
|
if (np == NULL) {
|
|
dev_err(dev->dev, "no platform data\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
rcdu = devm_kzalloc(&pdev->dev, sizeof(*rcdu), GFP_KERNEL);
|
|
if (rcdu == NULL) {
|
|
dev_err(dev->dev, "failed to allocate private data\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
init_waitqueue_head(&rcdu->commit.wait);
|
|
|
|
rcdu->dev = &pdev->dev;
|
|
rcdu->info = np ? of_match_device(rcar_du_of_table, rcdu->dev)->data
|
|
: (void *)platform_get_device_id(pdev)->driver_data;
|
|
rcdu->ddev = dev;
|
|
dev->dev_private = rcdu;
|
|
|
|
/* I/O resources */
|
|
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
rcdu->mmio = devm_ioremap_resource(&pdev->dev, mem);
|
|
if (IS_ERR(rcdu->mmio))
|
|
return PTR_ERR(rcdu->mmio);
|
|
|
|
/* Initialize vertical blanking interrupts handling. Start with vblank
|
|
* disabled for all CRTCs.
|
|
*/
|
|
ret = drm_vblank_init(dev, (1 << rcdu->info->num_crtcs) - 1);
|
|
if (ret < 0) {
|
|
dev_err(&pdev->dev, "failed to initialize vblank\n");
|
|
goto done;
|
|
}
|
|
|
|
/* DRM/KMS objects */
|
|
ret = rcar_du_modeset_init(rcdu);
|
|
if (ret < 0) {
|
|
dev_err(&pdev->dev, "failed to initialize DRM/KMS (%d)\n", ret);
|
|
goto done;
|
|
}
|
|
|
|
dev->irq_enabled = 1;
|
|
|
|
platform_set_drvdata(pdev, rcdu);
|
|
|
|
done:
|
|
if (ret)
|
|
rcar_du_unload(dev);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void rcar_du_preclose(struct drm_device *dev, struct drm_file *file)
|
|
{
|
|
struct rcar_du_device *rcdu = dev->dev_private;
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < rcdu->num_crtcs; ++i)
|
|
rcar_du_crtc_cancel_page_flip(&rcdu->crtcs[i], file);
|
|
}
|
|
|
|
static void rcar_du_lastclose(struct drm_device *dev)
|
|
{
|
|
struct rcar_du_device *rcdu = dev->dev_private;
|
|
|
|
drm_fbdev_cma_restore_mode(rcdu->fbdev);
|
|
}
|
|
|
|
static int rcar_du_enable_vblank(struct drm_device *dev, unsigned int pipe)
|
|
{
|
|
struct rcar_du_device *rcdu = dev->dev_private;
|
|
|
|
rcar_du_crtc_enable_vblank(&rcdu->crtcs[pipe], true);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void rcar_du_disable_vblank(struct drm_device *dev, unsigned int pipe)
|
|
{
|
|
struct rcar_du_device *rcdu = dev->dev_private;
|
|
|
|
rcar_du_crtc_enable_vblank(&rcdu->crtcs[pipe], false);
|
|
}
|
|
|
|
static const struct file_operations rcar_du_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = drm_open,
|
|
.release = drm_release,
|
|
.unlocked_ioctl = drm_ioctl,
|
|
#ifdef CONFIG_COMPAT
|
|
.compat_ioctl = drm_compat_ioctl,
|
|
#endif
|
|
.poll = drm_poll,
|
|
.read = drm_read,
|
|
.llseek = no_llseek,
|
|
.mmap = drm_gem_cma_mmap,
|
|
};
|
|
|
|
static struct drm_driver rcar_du_driver = {
|
|
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME
|
|
| DRIVER_ATOMIC,
|
|
.load = rcar_du_load,
|
|
.unload = rcar_du_unload,
|
|
.preclose = rcar_du_preclose,
|
|
.lastclose = rcar_du_lastclose,
|
|
.set_busid = drm_platform_set_busid,
|
|
.get_vblank_counter = drm_vblank_count,
|
|
.enable_vblank = rcar_du_enable_vblank,
|
|
.disable_vblank = rcar_du_disable_vblank,
|
|
.gem_free_object = drm_gem_cma_free_object,
|
|
.gem_vm_ops = &drm_gem_cma_vm_ops,
|
|
.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 = drm_gem_cma_prime_get_sg_table,
|
|
.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
|
|
.gem_prime_vmap = drm_gem_cma_prime_vmap,
|
|
.gem_prime_vunmap = drm_gem_cma_prime_vunmap,
|
|
.gem_prime_mmap = drm_gem_cma_prime_mmap,
|
|
.dumb_create = rcar_du_dumb_create,
|
|
.dumb_map_offset = drm_gem_cma_dumb_map_offset,
|
|
.dumb_destroy = drm_gem_dumb_destroy,
|
|
.fops = &rcar_du_fops,
|
|
.name = "rcar-du",
|
|
.desc = "Renesas R-Car Display Unit",
|
|
.date = "20130110",
|
|
.major = 1,
|
|
.minor = 0,
|
|
};
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* Power management
|
|
*/
|
|
|
|
#ifdef CONFIG_PM_SLEEP
|
|
static int rcar_du_pm_suspend(struct device *dev)
|
|
{
|
|
struct rcar_du_device *rcdu = dev_get_drvdata(dev);
|
|
|
|
drm_kms_helper_poll_disable(rcdu->ddev);
|
|
/* TODO Suspend the CRTC */
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rcar_du_pm_resume(struct device *dev)
|
|
{
|
|
struct rcar_du_device *rcdu = dev_get_drvdata(dev);
|
|
|
|
/* TODO Resume the CRTC */
|
|
|
|
drm_kms_helper_poll_enable(rcdu->ddev);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static const struct dev_pm_ops rcar_du_pm_ops = {
|
|
SET_SYSTEM_SLEEP_PM_OPS(rcar_du_pm_suspend, rcar_du_pm_resume)
|
|
};
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* Platform driver
|
|
*/
|
|
|
|
static int rcar_du_probe(struct platform_device *pdev)
|
|
{
|
|
return drm_platform_init(&rcar_du_driver, pdev);
|
|
}
|
|
|
|
static int rcar_du_remove(struct platform_device *pdev)
|
|
{
|
|
struct rcar_du_device *rcdu = platform_get_drvdata(pdev);
|
|
|
|
drm_put_dev(rcdu->ddev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct platform_driver rcar_du_platform_driver = {
|
|
.probe = rcar_du_probe,
|
|
.remove = rcar_du_remove,
|
|
.driver = {
|
|
.name = "rcar-du",
|
|
.pm = &rcar_du_pm_ops,
|
|
.of_match_table = rcar_du_of_table,
|
|
},
|
|
.id_table = rcar_du_id_table,
|
|
};
|
|
|
|
module_platform_driver(rcar_du_platform_driver);
|
|
|
|
MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
|
|
MODULE_DESCRIPTION("Renesas R-Car Display Unit DRM Driver");
|
|
MODULE_LICENSE("GPL");
|