summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/armada/armada_output.c
blob: abbc309fe539e9d917464980b344e30ed3a1d58f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
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_connector_unregister(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_connector_register(&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;
}