Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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	struct drm_mode_object *obj;
121	struct drm_encoder *encoder;
122
123	obj = drm_mode_object_find(dev, exynos_connector->encoder_id,
124				   DRM_MODE_OBJECT_ENCODER);
125	if (!obj) {
126		DRM_DEBUG_KMS("Unknown ENCODER ID %d\n",
127				exynos_connector->encoder_id);
128		return NULL;
129	}
130
131	encoder = obj_to_encoder(obj);
132
133	return encoder;
134}
135
136static struct drm_connector_helper_funcs exynos_connector_helper_funcs = {
137	.get_modes	= exynos_drm_connector_get_modes,
138	.mode_valid	= exynos_drm_connector_mode_valid,
139	.best_encoder	= exynos_drm_best_encoder,
140};
141
142static int exynos_drm_connector_fill_modes(struct drm_connector *connector,
143				unsigned int max_width, unsigned int max_height)
144{
145	struct exynos_drm_connector *exynos_connector =
146					to_exynos_connector(connector);
147	struct exynos_drm_display *display = exynos_connector->display;
148	unsigned int width, height;
149
150	width = max_width;
151	height = max_height;
152
153	/*
154	 * if specific driver want to find desired_mode using maxmum
155	 * resolution then get max width and height from that driver.
156	 */
157	if (display->ops->get_max_resol)
158		display->ops->get_max_resol(display, &width, &height);
159
160	return drm_helper_probe_single_connector_modes(connector, width,
161							height);
162}
163
164/* get detection status of display device. */
165static enum drm_connector_status
166exynos_drm_connector_detect(struct drm_connector *connector, bool force)
167{
168	struct exynos_drm_connector *exynos_connector =
169					to_exynos_connector(connector);
170	struct exynos_drm_display *display = exynos_connector->display;
171	enum drm_connector_status status = connector_status_disconnected;
172
173	if (display->ops->is_connected) {
174		if (display->ops->is_connected(display))
175			status = connector_status_connected;
176		else
177			status = connector_status_disconnected;
178	}
179
180	return status;
181}
182
183static void exynos_drm_connector_destroy(struct drm_connector *connector)
184{
185	struct exynos_drm_connector *exynos_connector =
186		to_exynos_connector(connector);
187
188	drm_sysfs_connector_remove(connector);
189	drm_connector_cleanup(connector);
190	kfree(exynos_connector);
191}
192
193static struct drm_connector_funcs exynos_connector_funcs = {
194	.dpms		= drm_helper_connector_dpms,
195	.fill_modes	= exynos_drm_connector_fill_modes,
196	.detect		= exynos_drm_connector_detect,
197	.destroy	= exynos_drm_connector_destroy,
198};
199
200struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
201						   struct drm_encoder *encoder)
202{
203	struct exynos_drm_connector *exynos_connector;
204	struct exynos_drm_display *display = exynos_drm_get_display(encoder);
205	struct drm_connector *connector;
206	int type;
207	int err;
208
209	exynos_connector = kzalloc(sizeof(*exynos_connector), GFP_KERNEL);
210	if (!exynos_connector)
211		return NULL;
212
213	connector = &exynos_connector->drm_connector;
214
215	switch (display->type) {
216	case EXYNOS_DISPLAY_TYPE_HDMI:
217		type = DRM_MODE_CONNECTOR_HDMIA;
218		connector->interlace_allowed = true;
219		connector->polled = DRM_CONNECTOR_POLL_HPD;
220		break;
221	case EXYNOS_DISPLAY_TYPE_VIDI:
222		type = DRM_MODE_CONNECTOR_VIRTUAL;
223		connector->polled = DRM_CONNECTOR_POLL_HPD;
224		break;
225	default:
226		type = DRM_MODE_CONNECTOR_Unknown;
227		break;
228	}
229
230	drm_connector_init(dev, connector, &exynos_connector_funcs, type);
231	drm_connector_helper_add(connector, &exynos_connector_helper_funcs);
232
233	err = drm_sysfs_connector_add(connector);
234	if (err)
235		goto err_connector;
236
237	exynos_connector->encoder_id = encoder->base.id;
238	exynos_connector->display = display;
239	connector->dpms = DRM_MODE_DPMS_OFF;
240	connector->encoder = encoder;
241
242	err = drm_mode_connector_attach_encoder(connector, encoder);
243	if (err) {
244		DRM_ERROR("failed to attach a connector to a encoder\n");
245		goto err_sysfs;
246	}
247
248	DRM_DEBUG_KMS("connector has been created\n");
249
250	return connector;
251
252err_sysfs:
253	drm_sysfs_connector_remove(connector);
254err_connector:
255	drm_connector_cleanup(connector);
256	kfree(exynos_connector);
257	return NULL;
258}