[go: nahoru, domu]

1/*
2 * Copyright (c) 2011 Samsung Electronics Co., Ltd.
3 * Authors:
4 *	Inki Dae <inki.dae@samsung.com>
5 *	Joonyoung Shim <jy0922.shim@samsung.com>
6 *	Seung-Woo Kim <sw0312.kim@samsung.com>
7 *
8 * This program is free software; you can redistribute  it and/or modify it
9 * under  the terms of  the GNU General  Public License as published by the
10 * Free Software Foundation;  either version 2 of the  License, or (at your
11 * option) any later version.
12 */
13
14#include <drm/drmP.h>
15#include <drm/drm_crtc_helper.h>
16
17#include <drm/exynos_drm.h>
18#include "exynos_drm_drv.h"
19#include "exynos_drm_encoder.h"
20#include "exynos_drm_connector.h"
21
22#define to_exynos_connector(x)	container_of(x, struct exynos_drm_connector,\
23				drm_connector)
24
25struct exynos_drm_connector {
26	struct drm_connector		drm_connector;
27	uint32_t			encoder_id;
28	struct exynos_drm_display	*display;
29};
30
31static int exynos_drm_connector_get_modes(struct drm_connector *connector)
32{
33	struct exynos_drm_connector *exynos_connector =
34					to_exynos_connector(connector);
35	struct exynos_drm_display *display = exynos_connector->display;
36	struct edid *edid = NULL;
37	unsigned int count = 0;
38	int ret;
39
40	/*
41	 * if get_edid() exists then get_edid() callback of hdmi side
42	 * is called to get edid data through i2c interface else
43	 * get timing from the FIMD driver(display controller).
44	 *
45	 * P.S. in case of lcd panel, count is always 1 if success
46	 * because lcd panel has only one mode.
47	 */
48	if (display->ops->get_edid) {
49		edid = display->ops->get_edid(display, connector);
50		if (IS_ERR_OR_NULL(edid)) {
51			ret = PTR_ERR(edid);
52			edid = NULL;
53			DRM_ERROR("Panel operation get_edid failed %d\n", ret);
54			goto out;
55		}
56
57		count = drm_add_edid_modes(connector, edid);
58		if (!count) {
59			DRM_ERROR("Add edid modes failed %d\n", count);
60			goto out;
61		}
62
63		drm_mode_connector_update_edid_property(connector, edid);
64	} else {
65		struct exynos_drm_panel_info *panel;
66		struct drm_display_mode *mode = drm_mode_create(connector->dev);
67		if (!mode) {
68			DRM_ERROR("failed to create a new display mode.\n");
69			return 0;
70		}
71
72		if (display->ops->get_panel)
73			panel = display->ops->get_panel(display);
74		else {
75			drm_mode_destroy(connector->dev, mode);
76			return 0;
77		}
78
79		drm_display_mode_from_videomode(&panel->vm, mode);
80		mode->width_mm = panel->width_mm;
81		mode->height_mm = panel->height_mm;
82		connector->display_info.width_mm = mode->width_mm;
83		connector->display_info.height_mm = mode->height_mm;
84
85		mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
86		drm_mode_set_name(mode);
87		drm_mode_probed_add(connector, mode);
88
89		count = 1;
90	}
91
92out:
93	kfree(edid);
94	return count;
95}
96
97static int exynos_drm_connector_mode_valid(struct drm_connector *connector,
98					    struct drm_display_mode *mode)
99{
100	struct exynos_drm_connector *exynos_connector =
101					to_exynos_connector(connector);
102	struct exynos_drm_display *display = exynos_connector->display;
103	int ret = MODE_BAD;
104
105	DRM_DEBUG_KMS("%s\n", __FILE__);
106
107	if (display->ops->check_mode)
108		if (!display->ops->check_mode(display, mode))
109			ret = MODE_OK;
110
111	return ret;
112}
113
114static struct drm_encoder *exynos_drm_best_encoder(
115		struct drm_connector *connector)
116{
117	struct drm_device *dev = connector->dev;
118	struct exynos_drm_connector *exynos_connector =
119					to_exynos_connector(connector);
120	return drm_encoder_find(dev, exynos_connector->encoder_id);
121}
122
123static struct drm_connector_helper_funcs exynos_connector_helper_funcs = {
124	.get_modes	= exynos_drm_connector_get_modes,
125	.mode_valid	= exynos_drm_connector_mode_valid,
126	.best_encoder	= exynos_drm_best_encoder,
127};
128
129static int exynos_drm_connector_fill_modes(struct drm_connector *connector,
130				unsigned int max_width, unsigned int max_height)
131{
132	struct exynos_drm_connector *exynos_connector =
133					to_exynos_connector(connector);
134	struct exynos_drm_display *display = exynos_connector->display;
135	unsigned int width, height;
136
137	width = max_width;
138	height = max_height;
139
140	/*
141	 * if specific driver want to find desired_mode using maxmum
142	 * resolution then get max width and height from that driver.
143	 */
144	if (display->ops->get_max_resol)
145		display->ops->get_max_resol(display, &width, &height);
146
147	return drm_helper_probe_single_connector_modes(connector, width,
148							height);
149}
150
151/* get detection status of display device. */
152static enum drm_connector_status
153exynos_drm_connector_detect(struct drm_connector *connector, bool force)
154{
155	struct exynos_drm_connector *exynos_connector =
156					to_exynos_connector(connector);
157	struct exynos_drm_display *display = exynos_connector->display;
158	enum drm_connector_status status = connector_status_disconnected;
159
160	if (display->ops->is_connected) {
161		if (display->ops->is_connected(display))
162			status = connector_status_connected;
163		else
164			status = connector_status_disconnected;
165	}
166
167	return status;
168}
169
170static void exynos_drm_connector_destroy(struct drm_connector *connector)
171{
172	struct exynos_drm_connector *exynos_connector =
173		to_exynos_connector(connector);
174
175	drm_connector_unregister(connector);
176	drm_connector_cleanup(connector);
177	kfree(exynos_connector);
178}
179
180static struct drm_connector_funcs exynos_connector_funcs = {
181	.dpms		= drm_helper_connector_dpms,
182	.fill_modes	= exynos_drm_connector_fill_modes,
183	.detect		= exynos_drm_connector_detect,
184	.destroy	= exynos_drm_connector_destroy,
185};
186
187struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
188						   struct drm_encoder *encoder)
189{
190	struct exynos_drm_connector *exynos_connector;
191	struct exynos_drm_display *display = exynos_drm_get_display(encoder);
192	struct drm_connector *connector;
193	int type;
194	int err;
195
196	exynos_connector = kzalloc(sizeof(*exynos_connector), GFP_KERNEL);
197	if (!exynos_connector)
198		return NULL;
199
200	connector = &exynos_connector->drm_connector;
201
202	switch (display->type) {
203	case EXYNOS_DISPLAY_TYPE_HDMI:
204		type = DRM_MODE_CONNECTOR_HDMIA;
205		connector->interlace_allowed = true;
206		connector->polled = DRM_CONNECTOR_POLL_HPD;
207		break;
208	case EXYNOS_DISPLAY_TYPE_VIDI:
209		type = DRM_MODE_CONNECTOR_VIRTUAL;
210		connector->polled = DRM_CONNECTOR_POLL_HPD;
211		break;
212	default:
213		type = DRM_MODE_CONNECTOR_Unknown;
214		break;
215	}
216
217	drm_connector_init(dev, connector, &exynos_connector_funcs, type);
218	drm_connector_helper_add(connector, &exynos_connector_helper_funcs);
219
220	err = drm_connector_register(connector);
221	if (err)
222		goto err_connector;
223
224	exynos_connector->encoder_id = encoder->base.id;
225	exynos_connector->display = display;
226	connector->dpms = DRM_MODE_DPMS_OFF;
227	connector->encoder = encoder;
228
229	err = drm_mode_connector_attach_encoder(connector, encoder);
230	if (err) {
231		DRM_ERROR("failed to attach a connector to a encoder\n");
232		goto err_sysfs;
233	}
234
235	DRM_DEBUG_KMS("connector has been created\n");
236
237	return connector;
238
239err_sysfs:
240	drm_connector_unregister(connector);
241err_connector:
242	drm_connector_cleanup(connector);
243	kfree(exynos_connector);
244	return NULL;
245}
246