DRM: Armada: Add Armada DRM driver
This patch adds support for the pair of LCD controllers on the Marvell Armada 510 SoCs. This driver supports: - multiple contiguous scanout buffers for video and graphics - shm backed cacheable buffer objects for X pixmaps for Vivante GPU acceleration - dual lcd0 and lcd1 crt operation - video overlay on each LCD crt via DRM planes - page flipping of the main scanout buffers - DRM prime for buffer export/import This driver is trivial to extend to other Armada SoCs. Included in this commit is the core driver with no output support; output support is platform and encoder driver dependent. Tested-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> Reviewed-by: Rob Clark <robdclark@gmail.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
This commit is contained in:
158
drivers/gpu/drm/armada/armada_output.c
Normal file
158
drivers/gpu/drm/armada/armada_output.c
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Russell King
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_edid.h>
|
||||
#include <drm/drm_encoder_slave.h>
|
||||
#include "armada_output.h"
|
||||
#include "armada_drm.h"
|
||||
|
||||
struct armada_connector {
|
||||
struct drm_connector conn;
|
||||
const struct armada_output_type *type;
|
||||
};
|
||||
|
||||
#define drm_to_armada_conn(c) container_of(c, struct armada_connector, conn)
|
||||
|
||||
struct drm_encoder *armada_drm_connector_encoder(struct drm_connector *conn)
|
||||
{
|
||||
struct drm_encoder *enc = conn->encoder;
|
||||
|
||||
return enc ? enc : drm_encoder_find(conn->dev, conn->encoder_ids[0]);
|
||||
}
|
||||
|
||||
static enum drm_connector_status armada_drm_connector_detect(
|
||||
struct drm_connector *conn, bool force)
|
||||
{
|
||||
struct armada_connector *dconn = drm_to_armada_conn(conn);
|
||||
enum drm_connector_status status = connector_status_disconnected;
|
||||
|
||||
if (dconn->type->detect) {
|
||||
status = dconn->type->detect(conn, force);
|
||||
} else {
|
||||
struct drm_encoder *enc = armada_drm_connector_encoder(conn);
|
||||
|
||||
if (enc)
|
||||
status = encoder_helper_funcs(enc)->detect(enc, conn);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void armada_drm_connector_destroy(struct drm_connector *conn)
|
||||
{
|
||||
struct armada_connector *dconn = drm_to_armada_conn(conn);
|
||||
|
||||
drm_sysfs_connector_remove(conn);
|
||||
drm_connector_cleanup(conn);
|
||||
kfree(dconn);
|
||||
}
|
||||
|
||||
static int armada_drm_connector_set_property(struct drm_connector *conn,
|
||||
struct drm_property *property, uint64_t value)
|
||||
{
|
||||
struct armada_connector *dconn = drm_to_armada_conn(conn);
|
||||
|
||||
if (!dconn->type->set_property)
|
||||
return -EINVAL;
|
||||
|
||||
return dconn->type->set_property(conn, property, value);
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs armada_drm_conn_funcs = {
|
||||
.dpms = drm_helper_connector_dpms,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.detect = armada_drm_connector_detect,
|
||||
.destroy = armada_drm_connector_destroy,
|
||||
.set_property = armada_drm_connector_set_property,
|
||||
};
|
||||
|
||||
void armada_drm_encoder_prepare(struct drm_encoder *encoder)
|
||||
{
|
||||
encoder_helper_funcs(encoder)->dpms(encoder, DRM_MODE_DPMS_OFF);
|
||||
}
|
||||
|
||||
void armada_drm_encoder_commit(struct drm_encoder *encoder)
|
||||
{
|
||||
encoder_helper_funcs(encoder)->dpms(encoder, DRM_MODE_DPMS_ON);
|
||||
}
|
||||
|
||||
bool armada_drm_encoder_mode_fixup(struct drm_encoder *encoder,
|
||||
const struct drm_display_mode *mode, struct drm_display_mode *adjusted)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Shouldn't this be a generic helper function? */
|
||||
int armada_drm_slave_encoder_mode_valid(struct drm_connector *conn,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct drm_encoder *encoder = armada_drm_connector_encoder(conn);
|
||||
int valid = MODE_BAD;
|
||||
|
||||
if (encoder) {
|
||||
struct drm_encoder_slave *slave = to_encoder_slave(encoder);
|
||||
|
||||
valid = slave->slave_funcs->mode_valid(encoder, mode);
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
int armada_drm_slave_encoder_set_property(struct drm_connector *conn,
|
||||
struct drm_property *property, uint64_t value)
|
||||
{
|
||||
struct drm_encoder *encoder = armada_drm_connector_encoder(conn);
|
||||
int rc = -EINVAL;
|
||||
|
||||
if (encoder) {
|
||||
struct drm_encoder_slave *slave = to_encoder_slave(encoder);
|
||||
|
||||
rc = slave->slave_funcs->set_property(encoder, conn, property,
|
||||
value);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int armada_output_create(struct drm_device *dev,
|
||||
const struct armada_output_type *type, const void *data)
|
||||
{
|
||||
struct armada_connector *dconn;
|
||||
int ret;
|
||||
|
||||
dconn = kzalloc(sizeof(*dconn), GFP_KERNEL);
|
||||
if (!dconn)
|
||||
return -ENOMEM;
|
||||
|
||||
dconn->type = type;
|
||||
|
||||
ret = drm_connector_init(dev, &dconn->conn, &armada_drm_conn_funcs,
|
||||
type->connector_type);
|
||||
if (ret) {
|
||||
DRM_ERROR("unable to init connector\n");
|
||||
goto err_destroy_dconn;
|
||||
}
|
||||
|
||||
ret = type->create(&dconn->conn, data);
|
||||
if (ret)
|
||||
goto err_conn;
|
||||
|
||||
ret = drm_sysfs_connector_add(&dconn->conn);
|
||||
if (ret)
|
||||
goto err_sysfs;
|
||||
|
||||
return 0;
|
||||
|
||||
err_sysfs:
|
||||
if (dconn->conn.encoder)
|
||||
dconn->conn.encoder->funcs->destroy(dconn->conn.encoder);
|
||||
err_conn:
|
||||
drm_connector_cleanup(&dconn->conn);
|
||||
err_destroy_dconn:
|
||||
kfree(dconn);
|
||||
return ret;
|
||||
}
|
Reference in New Issue
Block a user