11c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae/*
21c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae * Copyright (c) 2011 Samsung Electronics Co., Ltd.
31c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae * Authors:
41c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae *	Inki Dae <inki.dae@samsung.com>
51c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae *	Joonyoung Shim <jy0922.shim@samsung.com>
61c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae *	Seung-Woo Kim <sw0312.kim@samsung.com>
71c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae *
81c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae * Permission is hereby granted, free of charge, to any person obtaining a
91c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae * copy of this software and associated documentation files (the "Software"),
101c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae * to deal in the Software without restriction, including without limitation
111c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae * the rights to use, copy, modify, merge, publish, distribute, sublicense,
121c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae * and/or sell copies of the Software, and to permit persons to whom the
131c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae * Software is furnished to do so, subject to the following conditions:
141c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae *
151c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae * The above copyright notice and this permission notice (including the next
161c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae * paragraph) shall be included in all copies or substantial portions of the
171c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae * Software.
181c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae *
191c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
201c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
211c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
221c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
231c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
241c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
251c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae * OTHER DEALINGS IN THE SOFTWARE.
261c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae */
271c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
281c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae#include "drmP.h"
291c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae#include "drm_crtc_helper.h"
301c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
31607c50d429371797f198ffc34afb239eadd1c655Eun-Chul Kim#include <drm/exynos_drm.h>
321c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae#include "exynos_drm_drv.h"
331c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae#include "exynos_drm_encoder.h"
341c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
351c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae#define MAX_EDID 256
361c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae#define to_exynos_connector(x)	container_of(x, struct exynos_drm_connector,\
371c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae				drm_connector)
381c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
391c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Daestruct exynos_drm_connector {
401c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	struct drm_connector	drm_connector;
41adb6b1596743e93e50fad2ff26d9604cda4361abInki Dae	uint32_t		encoder_id;
42adb6b1596743e93e50fad2ff26d9604cda4361abInki Dae	struct exynos_drm_manager *manager;
431c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae};
441c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
451c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae/* convert exynos_video_timings to drm_display_mode */
461c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Daestatic inline void
471c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Daeconvert_to_display_mode(struct drm_display_mode *mode,
48607c50d429371797f198ffc34afb239eadd1c655Eun-Chul Kim			struct exynos_drm_panel_info *panel)
491c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae{
50607c50d429371797f198ffc34afb239eadd1c655Eun-Chul Kim	struct fb_videomode *timing = &panel->timing;
511c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	DRM_DEBUG_KMS("%s\n", __FILE__);
521c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
531c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	mode->clock = timing->pixclock / 1000;
548b58dfe0290cb57e3f8601b197f00c23fa39a60dSeung-Woo Kim	mode->vrefresh = timing->refresh;
551c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
561c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	mode->hdisplay = timing->xres;
571c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	mode->hsync_start = mode->hdisplay + timing->left_margin;
581c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	mode->hsync_end = mode->hsync_start + timing->hsync_len;
591c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	mode->htotal = mode->hsync_end + timing->right_margin;
601c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
611c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	mode->vdisplay = timing->yres;
621c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	mode->vsync_start = mode->vdisplay + timing->upper_margin;
631c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	mode->vsync_end = mode->vsync_start + timing->vsync_len;
641c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	mode->vtotal = mode->vsync_end + timing->lower_margin;
65607c50d429371797f198ffc34afb239eadd1c655Eun-Chul Kim	mode->width_mm = panel->width_mm;
66607c50d429371797f198ffc34afb239eadd1c655Eun-Chul Kim	mode->height_mm = panel->height_mm;
678b58dfe0290cb57e3f8601b197f00c23fa39a60dSeung-Woo Kim
688b58dfe0290cb57e3f8601b197f00c23fa39a60dSeung-Woo Kim	if (timing->vmode & FB_VMODE_INTERLACED)
698b58dfe0290cb57e3f8601b197f00c23fa39a60dSeung-Woo Kim		mode->flags |= DRM_MODE_FLAG_INTERLACE;
708b58dfe0290cb57e3f8601b197f00c23fa39a60dSeung-Woo Kim
718b58dfe0290cb57e3f8601b197f00c23fa39a60dSeung-Woo Kim	if (timing->vmode & FB_VMODE_DOUBLE)
728b58dfe0290cb57e3f8601b197f00c23fa39a60dSeung-Woo Kim		mode->flags |= DRM_MODE_FLAG_DBLSCAN;
731c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae}
741c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
751c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae/* convert drm_display_mode to exynos_video_timings */
761c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Daestatic inline void
771c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Daeconvert_to_video_timing(struct fb_videomode *timing,
781c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae			struct drm_display_mode *mode)
791c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae{
801c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	DRM_DEBUG_KMS("%s\n", __FILE__);
811c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
821c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	memset(timing, 0, sizeof(*timing));
831c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
841c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	timing->pixclock = mode->clock * 1000;
858b58dfe0290cb57e3f8601b197f00c23fa39a60dSeung-Woo Kim	timing->refresh = drm_mode_vrefresh(mode);
861c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
871c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	timing->xres = mode->hdisplay;
881c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	timing->left_margin = mode->hsync_start - mode->hdisplay;
891c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	timing->hsync_len = mode->hsync_end - mode->hsync_start;
901c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	timing->right_margin = mode->htotal - mode->hsync_end;
911c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
921c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	timing->yres = mode->vdisplay;
931c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	timing->upper_margin = mode->vsync_start - mode->vdisplay;
941c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	timing->vsync_len = mode->vsync_end - mode->vsync_start;
951c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	timing->lower_margin = mode->vtotal - mode->vsync_end;
961c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
971c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
981c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae		timing->vmode = FB_VMODE_INTERLACED;
991c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	else
1001c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae		timing->vmode = FB_VMODE_NONINTERLACED;
1011c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
1021c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
1031c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae		timing->vmode |= FB_VMODE_DOUBLE;
1041c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae}
1051c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
1061c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Daestatic int exynos_drm_connector_get_modes(struct drm_connector *connector)
1071c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae{
108adb6b1596743e93e50fad2ff26d9604cda4361abInki Dae	struct exynos_drm_connector *exynos_connector =
109adb6b1596743e93e50fad2ff26d9604cda4361abInki Dae					to_exynos_connector(connector);
110adb6b1596743e93e50fad2ff26d9604cda4361abInki Dae	struct exynos_drm_manager *manager = exynos_connector->manager;
11174ccc539bcebdb24afb74194223f92a96a7285edInki Dae	struct exynos_drm_display_ops *display_ops = manager->display_ops;
1121c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	unsigned int count;
1131c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
1141c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	DRM_DEBUG_KMS("%s\n", __FILE__);
1151c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
11674ccc539bcebdb24afb74194223f92a96a7285edInki Dae	if (!display_ops) {
11774ccc539bcebdb24afb74194223f92a96a7285edInki Dae		DRM_DEBUG_KMS("display_ops is null.\n");
1181c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae		return 0;
1191c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	}
1201c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
1211c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	/*
1221c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	 * if get_edid() exists then get_edid() callback of hdmi side
1231c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	 * is called to get edid data through i2c interface else
1241c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	 * get timing from the FIMD driver(display controller).
1251c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	 *
1261c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	 * P.S. in case of lcd panel, count is always 1 if success
1271c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	 * because lcd panel has only one mode.
1281c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	 */
12974ccc539bcebdb24afb74194223f92a96a7285edInki Dae	if (display_ops->get_edid) {
1301c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae		int ret;
1311c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae		void *edid;
1321c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
1331c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae		edid = kzalloc(MAX_EDID, GFP_KERNEL);
1341c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae		if (!edid) {
1351c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae			DRM_ERROR("failed to allocate edid\n");
1361c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae			return 0;
1371c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae		}
1381c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
13974ccc539bcebdb24afb74194223f92a96a7285edInki Dae		ret = display_ops->get_edid(manager->dev, connector,
1401c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae						edid, MAX_EDID);
1411c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae		if (ret < 0) {
1421c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae			DRM_ERROR("failed to get edid data.\n");
1431c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae			kfree(edid);
1441c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae			edid = NULL;
1451c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae			return 0;
1461c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae		}
1471c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
1481c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae		drm_mode_connector_update_edid_property(connector, edid);
1491c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae		count = drm_add_edid_modes(connector, edid);
1501c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
1511c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae		kfree(connector->display_info.raw_edid);
1521c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae		connector->display_info.raw_edid = edid;
1531c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	} else {
1541c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae		struct drm_display_mode *mode = drm_mode_create(connector->dev);
155607c50d429371797f198ffc34afb239eadd1c655Eun-Chul Kim		struct exynos_drm_panel_info *panel;
1561c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
157607c50d429371797f198ffc34afb239eadd1c655Eun-Chul Kim		if (display_ops->get_panel)
158607c50d429371797f198ffc34afb239eadd1c655Eun-Chul Kim			panel = display_ops->get_panel(manager->dev);
1591c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae		else {
1601c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae			drm_mode_destroy(connector->dev, mode);
1611c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae			return 0;
1621c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae		}
1631c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
164607c50d429371797f198ffc34afb239eadd1c655Eun-Chul Kim		convert_to_display_mode(mode, panel);
165607c50d429371797f198ffc34afb239eadd1c655Eun-Chul Kim		connector->display_info.width_mm = mode->width_mm;
166607c50d429371797f198ffc34afb239eadd1c655Eun-Chul Kim		connector->display_info.height_mm = mode->height_mm;
1671c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
1681c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae		mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
1691c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae		drm_mode_set_name(mode);
1701c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae		drm_mode_probed_add(connector, mode);
1711c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
1721c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae		count = 1;
1731c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	}
1741c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
1751c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	return count;
1761c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae}
1771c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
1781c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Daestatic int exynos_drm_connector_mode_valid(struct drm_connector *connector,
1791c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae					    struct drm_display_mode *mode)
1801c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae{
181adb6b1596743e93e50fad2ff26d9604cda4361abInki Dae	struct exynos_drm_connector *exynos_connector =
182adb6b1596743e93e50fad2ff26d9604cda4361abInki Dae					to_exynos_connector(connector);
183adb6b1596743e93e50fad2ff26d9604cda4361abInki Dae	struct exynos_drm_manager *manager = exynos_connector->manager;
18474ccc539bcebdb24afb74194223f92a96a7285edInki Dae	struct exynos_drm_display_ops *display_ops = manager->display_ops;
1851c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	struct fb_videomode timing;
1861c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	int ret = MODE_BAD;
1871c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
1881c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	DRM_DEBUG_KMS("%s\n", __FILE__);
1891c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
1901c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	convert_to_video_timing(&timing, mode);
1911c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
19274ccc539bcebdb24afb74194223f92a96a7285edInki Dae	if (display_ops && display_ops->check_timing)
19374ccc539bcebdb24afb74194223f92a96a7285edInki Dae		if (!display_ops->check_timing(manager->dev, (void *)&timing))
1941c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae			ret = MODE_OK;
1951c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
1961c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	return ret;
1971c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae}
1981c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
1991c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Daestruct drm_encoder *exynos_drm_best_encoder(struct drm_connector *connector)
2001c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae{
201adb6b1596743e93e50fad2ff26d9604cda4361abInki Dae	struct drm_device *dev = connector->dev;
202adb6b1596743e93e50fad2ff26d9604cda4361abInki Dae	struct exynos_drm_connector *exynos_connector =
203adb6b1596743e93e50fad2ff26d9604cda4361abInki Dae					to_exynos_connector(connector);
204adb6b1596743e93e50fad2ff26d9604cda4361abInki Dae	struct drm_mode_object *obj;
205adb6b1596743e93e50fad2ff26d9604cda4361abInki Dae	struct drm_encoder *encoder;
206adb6b1596743e93e50fad2ff26d9604cda4361abInki Dae
2071c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	DRM_DEBUG_KMS("%s\n", __FILE__);
2081c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
209adb6b1596743e93e50fad2ff26d9604cda4361abInki Dae	obj = drm_mode_object_find(dev, exynos_connector->encoder_id,
210adb6b1596743e93e50fad2ff26d9604cda4361abInki Dae				   DRM_MODE_OBJECT_ENCODER);
211adb6b1596743e93e50fad2ff26d9604cda4361abInki Dae	if (!obj) {
212adb6b1596743e93e50fad2ff26d9604cda4361abInki Dae		DRM_DEBUG_KMS("Unknown ENCODER ID %d\n",
213adb6b1596743e93e50fad2ff26d9604cda4361abInki Dae				exynos_connector->encoder_id);
214adb6b1596743e93e50fad2ff26d9604cda4361abInki Dae		return NULL;
215adb6b1596743e93e50fad2ff26d9604cda4361abInki Dae	}
216adb6b1596743e93e50fad2ff26d9604cda4361abInki Dae
217adb6b1596743e93e50fad2ff26d9604cda4361abInki Dae	encoder = obj_to_encoder(obj);
218adb6b1596743e93e50fad2ff26d9604cda4361abInki Dae
219adb6b1596743e93e50fad2ff26d9604cda4361abInki Dae	return encoder;
2201c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae}
2211c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
2221c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Daestatic struct drm_connector_helper_funcs exynos_connector_helper_funcs = {
2231c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	.get_modes	= exynos_drm_connector_get_modes,
2241c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	.mode_valid	= exynos_drm_connector_mode_valid,
2251c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	.best_encoder	= exynos_drm_best_encoder,
2261c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae};
2271c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
2281c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae/* get detection status of display device. */
2291c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Daestatic enum drm_connector_status
2301c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Daeexynos_drm_connector_detect(struct drm_connector *connector, bool force)
2311c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae{
232adb6b1596743e93e50fad2ff26d9604cda4361abInki Dae	struct exynos_drm_connector *exynos_connector =
233adb6b1596743e93e50fad2ff26d9604cda4361abInki Dae					to_exynos_connector(connector);
234adb6b1596743e93e50fad2ff26d9604cda4361abInki Dae	struct exynos_drm_manager *manager = exynos_connector->manager;
23574ccc539bcebdb24afb74194223f92a96a7285edInki Dae	struct exynos_drm_display_ops *display_ops =
23674ccc539bcebdb24afb74194223f92a96a7285edInki Dae					manager->display_ops;
2371c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	enum drm_connector_status status = connector_status_disconnected;
2381c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
2391c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	DRM_DEBUG_KMS("%s\n", __FILE__);
2401c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
24174ccc539bcebdb24afb74194223f92a96a7285edInki Dae	if (display_ops && display_ops->is_connected) {
24274ccc539bcebdb24afb74194223f92a96a7285edInki Dae		if (display_ops->is_connected(manager->dev))
2431c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae			status = connector_status_connected;
2441c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae		else
2451c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae			status = connector_status_disconnected;
2461c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	}
2471c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
2481c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	return status;
2491c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae}
2501c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
2511c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Daestatic void exynos_drm_connector_destroy(struct drm_connector *connector)
2521c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae{
2531c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	struct exynos_drm_connector *exynos_connector =
2541c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae		to_exynos_connector(connector);
2551c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
2561c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	DRM_DEBUG_KMS("%s\n", __FILE__);
2571c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
2581c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	drm_sysfs_connector_remove(connector);
2591c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	drm_connector_cleanup(connector);
2601c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	kfree(exynos_connector);
2611c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae}
2621c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
2631c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Daestatic struct drm_connector_funcs exynos_connector_funcs = {
2641c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	.dpms		= drm_helper_connector_dpms,
2651c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	.fill_modes	= drm_helper_probe_single_connector_modes,
2661c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	.detect		= exynos_drm_connector_detect,
2671c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	.destroy	= exynos_drm_connector_destroy,
2681c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae};
2691c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
2701c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Daestruct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
2711c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae						   struct drm_encoder *encoder)
2721c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae{
2731c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	struct exynos_drm_connector *exynos_connector;
2741c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
2751c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	struct drm_connector *connector;
2761c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	int type;
2771c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	int err;
2781c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
2791c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	DRM_DEBUG_KMS("%s\n", __FILE__);
2801c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
2811c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	exynos_connector = kzalloc(sizeof(*exynos_connector), GFP_KERNEL);
2821c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	if (!exynos_connector) {
2831c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae		DRM_ERROR("failed to allocate connector\n");
2841c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae		return NULL;
2851c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	}
2861c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
2871c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	connector = &exynos_connector->drm_connector;
2881c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
28974ccc539bcebdb24afb74194223f92a96a7285edInki Dae	switch (manager->display_ops->type) {
2901c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	case EXYNOS_DISPLAY_TYPE_HDMI:
2911c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae		type = DRM_MODE_CONNECTOR_HDMIA;
2921b17b206560c433ae9e8f8409f3f3842949a74c8Seung-Woo Kim		connector->interlace_allowed = true;
2931b17b206560c433ae9e8f8409f3f3842949a74c8Seung-Woo Kim		connector->polled = DRM_CONNECTOR_POLL_HPD;
2941c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae		break;
2951c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	default:
2961c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae		type = DRM_MODE_CONNECTOR_Unknown;
2971c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae		break;
2981c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	}
2991c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
3001c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	drm_connector_init(dev, connector, &exynos_connector_funcs, type);
3011c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	drm_connector_helper_add(connector, &exynos_connector_helper_funcs);
3021c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
3031c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	err = drm_sysfs_connector_add(connector);
3041c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	if (err)
3051c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae		goto err_connector;
3061c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
307adb6b1596743e93e50fad2ff26d9604cda4361abInki Dae	exynos_connector->encoder_id = encoder->base.id;
308adb6b1596743e93e50fad2ff26d9604cda4361abInki Dae	exynos_connector->manager = manager;
3091c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	connector->encoder = encoder;
310adb6b1596743e93e50fad2ff26d9604cda4361abInki Dae
3111c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	err = drm_mode_connector_attach_encoder(connector, encoder);
3121c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	if (err) {
3131c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae		DRM_ERROR("failed to attach a connector to a encoder\n");
3141c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae		goto err_sysfs;
3151c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	}
3161c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
3171c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	DRM_DEBUG_KMS("connector has been created\n");
3181c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
3191c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	return connector;
3201c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
3211c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Daeerr_sysfs:
3221c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	drm_sysfs_connector_remove(connector);
3231c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Daeerr_connector:
3241c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	drm_connector_cleanup(connector);
3251c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	kfree(exynos_connector);
3261c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae	return NULL;
3271c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae}
3281c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki Dae
3291c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki DaeMODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
3301c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki DaeMODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
3311c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki DaeMODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
3321c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki DaeMODULE_DESCRIPTION("Samsung SoC DRM Connector Driver");
3331c248b7d2960faec3e1b8f3f9c5d9d0df28e0a3cInki DaeMODULE_LICENSE("GPL");
334