summaryrefslogtreecommitdiffstats
path: root/kernel/drivers/gpu/drm/armada/armada_slave.c
blob: 00d0facb42f3e3cf5f3ff9125e574033b38cd9c8 (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
/*
 * Copyright (C) 2012 Russell King
 *  Rewritten from the dovefb driver, and Armada510 manuals.
 *
 * 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_drm.h"
#include "armada_output.h"
#include "armada_slave.h"

static int armada_drm_slave_get_modes(struct drm_connector *conn)
{
	struct drm_encoder *enc = armada_drm_connector_encoder(conn);
	int count = 0;

	if (enc) {
		struct drm_encoder_slave *slave = to_encoder_slave(enc);

		count = slave->slave_funcs->get_modes(enc, conn);
	}

	return count;
}

static void armada_drm_slave_destroy(struct drm_encoder *enc)
{
	struct drm_encoder_slave *slave = to_encoder_slave(enc);
	struct i2c_client *client = drm_i2c_encoder_get_client(enc);

	if (slave->slave_funcs)
		slave->slave_funcs->destroy(enc);
	if (client)
		i2c_put_adapter(client->adapter);

	drm_encoder_cleanup(&slave->base);
	kfree(slave);
}

static const struct drm_encoder_funcs armada_drm_slave_encoder_funcs = {
	.destroy	= armada_drm_slave_destroy,
};

static const struct drm_connector_helper_funcs armada_drm_slave_helper_funcs = {
	.get_modes	= armada_drm_slave_get_modes,
	.mode_valid	= armada_drm_slave_encoder_mode_valid,
	.best_encoder	= armada_drm_connector_encoder,
};

static const struct drm_encoder_helper_funcs drm_slave_encoder_helpers = {
	.dpms = drm_i2c_encoder_dpms,
	.save = drm_i2c_encoder_save,
	.restore = drm_i2c_encoder_restore,
	.mode_fixup = drm_i2c_encoder_mode_fixup,
	.prepare = drm_i2c_encoder_prepare,
	.commit = drm_i2c_encoder_commit,
	.mode_set = drm_i2c_encoder_mode_set,
	.detect = drm_i2c_encoder_detect,
};

static int
armada_drm_conn_slave_create(struct drm_connector *conn, const void *data)
{
	const struct armada_drm_slave_config *config = data;
	struct drm_encoder_slave *slave;
	struct i2c_adapter *adap;
	int ret;

	conn->interlace_allowed = config->interlace_allowed;
	conn->doublescan_allowed = config->doublescan_allowed;
	conn->polled = config->polled;

	drm_connector_helper_add(conn, &armada_drm_slave_helper_funcs);

	slave = kzalloc(sizeof(*slave), GFP_KERNEL);
	if (!slave)
		return -ENOMEM;

	slave->base.possible_crtcs = config->crtcs;

	adap = i2c_get_adapter(config->i2c_adapter_id);
	if (!adap) {
		kfree(slave);
		return -EPROBE_DEFER;
	}

	ret = drm_encoder_init(conn->dev, &slave->base,
			       &armada_drm_slave_encoder_funcs,
			       DRM_MODE_ENCODER_TMDS);
	if (ret) {
		DRM_ERROR("unable to init encoder\n");
		i2c_put_adapter(adap);
		kfree(slave);
		return ret;
	}

	ret = drm_i2c_encoder_init(conn->dev, slave, adap, &config->info);
	i2c_put_adapter(adap);
	if (ret) {
		DRM_ERROR("unable to init encoder slave\n");
		armada_drm_slave_destroy(&slave->base);
		return ret;
	}

	drm_encoder_helper_add(&slave->base, &drm_slave_encoder_helpers);

	ret = slave->slave_funcs->create_resources(&slave->base, conn);
	if (ret) {
		armada_drm_slave_destroy(&slave->base);
		return ret;
	}

	ret = drm_mode_connector_attach_encoder(conn, &slave->base);
	if (ret) {
		armada_drm_slave_destroy(&slave->base);
		return ret;
	}

	conn->encoder = &slave->base;

	return ret;
}

static const struct armada_output_type armada_drm_conn_slave = {
	.connector_type	= DRM_MODE_CONNECTOR_HDMIA,
	.create		= armada_drm_conn_slave_create,
	.set_property	= armada_drm_slave_encoder_set_property,
};

int armada_drm_connector_slave_create(struct drm_device *dev,
	const struct armada_drm_slave_config *config)
{
	return armada_output_create(dev, &armada_drm_conn_slave, config);
}