1 /*
2  * Copyright (C) 2011 Samsung Electronics Co.Ltd
3  * Authors:
4  *	Inki Dae <inki.dae@samsung.com>
5  *	Seung-Woo Kim <sw0312.kim@samsung.com>
6  *
7  * This program is free software; you can redistribute  it and/or modify it
8  * under  the terms of  the GNU General  Public License as published by the
9  * Free Software Foundation;  either version 2 of the  License, or (at your
10  * option) any later version.
11  *
12  */
13 
14 #include "drmP.h"
15 
16 #include <linux/kernel.h>
17 #include <linux/wait.h>
18 #include <linux/module.h>
19 #include <linux/platform_device.h>
20 #include <linux/pm_runtime.h>
21 
22 #include <drm/exynos_drm.h>
23 
24 #include "exynos_drm_drv.h"
25 #include "exynos_drm_hdmi.h"
26 
27 #define to_context(dev)		platform_get_drvdata(to_platform_device(dev))
28 #define to_subdrv(dev)		to_context(dev)
29 #define get_ctx_from_subdrv(subdrv)	container_of(subdrv,\
30 					struct drm_hdmi_context, subdrv);
31 
32 /* these callback points shoud be set by specific drivers. */
33 static struct exynos_hdmi_ops *hdmi_ops;
34 static struct exynos_mixer_ops *mixer_ops;
35 
36 struct drm_hdmi_context {
37 	struct exynos_drm_subdrv	subdrv;
38 	struct exynos_drm_hdmi_context	*hdmi_ctx;
39 	struct exynos_drm_hdmi_context	*mixer_ctx;
40 };
41 
exynos_hdmi_ops_register(struct exynos_hdmi_ops * ops)42 void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops)
43 {
44 	DRM_DEBUG_KMS("%s\n", __FILE__);
45 
46 	if (ops)
47 		hdmi_ops = ops;
48 }
49 
exynos_mixer_ops_register(struct exynos_mixer_ops * ops)50 void exynos_mixer_ops_register(struct exynos_mixer_ops *ops)
51 {
52 	DRM_DEBUG_KMS("%s\n", __FILE__);
53 
54 	if (ops)
55 		mixer_ops = ops;
56 }
57 
drm_hdmi_is_connected(struct device * dev)58 static bool drm_hdmi_is_connected(struct device *dev)
59 {
60 	struct drm_hdmi_context *ctx = to_context(dev);
61 
62 	DRM_DEBUG_KMS("%s\n", __FILE__);
63 
64 	if (hdmi_ops && hdmi_ops->is_connected)
65 		return hdmi_ops->is_connected(ctx->hdmi_ctx->ctx);
66 
67 	return false;
68 }
69 
drm_hdmi_get_edid(struct device * dev,struct drm_connector * connector,u8 * edid,int len)70 static int drm_hdmi_get_edid(struct device *dev,
71 		struct drm_connector *connector, u8 *edid, int len)
72 {
73 	struct drm_hdmi_context *ctx = to_context(dev);
74 
75 	DRM_DEBUG_KMS("%s\n", __FILE__);
76 
77 	if (hdmi_ops && hdmi_ops->get_edid)
78 		return hdmi_ops->get_edid(ctx->hdmi_ctx->ctx, connector, edid,
79 					  len);
80 
81 	return 0;
82 }
83 
drm_hdmi_check_timing(struct device * dev,void * timing)84 static int drm_hdmi_check_timing(struct device *dev, void *timing)
85 {
86 	struct drm_hdmi_context *ctx = to_context(dev);
87 
88 	DRM_DEBUG_KMS("%s\n", __FILE__);
89 
90 	if (hdmi_ops && hdmi_ops->check_timing)
91 		return hdmi_ops->check_timing(ctx->hdmi_ctx->ctx, timing);
92 
93 	return 0;
94 }
95 
drm_hdmi_power_on(struct device * dev,int mode)96 static int drm_hdmi_power_on(struct device *dev, int mode)
97 {
98 	struct drm_hdmi_context *ctx = to_context(dev);
99 
100 	DRM_DEBUG_KMS("%s\n", __FILE__);
101 
102 	if (hdmi_ops && hdmi_ops->power_on)
103 		return hdmi_ops->power_on(ctx->hdmi_ctx->ctx, mode);
104 
105 	return 0;
106 }
107 
108 static struct exynos_drm_display_ops drm_hdmi_display_ops = {
109 	.type = EXYNOS_DISPLAY_TYPE_HDMI,
110 	.is_connected = drm_hdmi_is_connected,
111 	.get_edid = drm_hdmi_get_edid,
112 	.check_timing = drm_hdmi_check_timing,
113 	.power_on = drm_hdmi_power_on,
114 };
115 
drm_hdmi_enable_vblank(struct device * subdrv_dev)116 static int drm_hdmi_enable_vblank(struct device *subdrv_dev)
117 {
118 	struct drm_hdmi_context *ctx = to_context(subdrv_dev);
119 	struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
120 	struct exynos_drm_manager *manager = subdrv->manager;
121 
122 	DRM_DEBUG_KMS("%s\n", __FILE__);
123 
124 	if (mixer_ops && mixer_ops->enable_vblank)
125 		return mixer_ops->enable_vblank(ctx->mixer_ctx->ctx,
126 						manager->pipe);
127 
128 	return 0;
129 }
130 
drm_hdmi_disable_vblank(struct device * subdrv_dev)131 static void drm_hdmi_disable_vblank(struct device *subdrv_dev)
132 {
133 	struct drm_hdmi_context *ctx = to_context(subdrv_dev);
134 
135 	DRM_DEBUG_KMS("%s\n", __FILE__);
136 
137 	if (mixer_ops && mixer_ops->disable_vblank)
138 		return mixer_ops->disable_vblank(ctx->mixer_ctx->ctx);
139 }
140 
drm_hdmi_mode_fixup(struct device * subdrv_dev,struct drm_connector * connector,struct drm_display_mode * mode,struct drm_display_mode * adjusted_mode)141 static void drm_hdmi_mode_fixup(struct device *subdrv_dev,
142 				struct drm_connector *connector,
143 				struct drm_display_mode *mode,
144 				struct drm_display_mode *adjusted_mode)
145 {
146 	struct drm_hdmi_context *ctx = to_context(subdrv_dev);
147 
148 	DRM_DEBUG_KMS("%s\n", __FILE__);
149 
150 	if (hdmi_ops && hdmi_ops->mode_fixup)
151 		hdmi_ops->mode_fixup(ctx->hdmi_ctx->ctx, connector, mode,
152 				     adjusted_mode);
153 }
154 
drm_hdmi_mode_set(struct device * subdrv_dev,void * mode)155 static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode)
156 {
157 	struct drm_hdmi_context *ctx = to_context(subdrv_dev);
158 
159 	DRM_DEBUG_KMS("%s\n", __FILE__);
160 
161 	if (hdmi_ops && hdmi_ops->mode_set)
162 		hdmi_ops->mode_set(ctx->hdmi_ctx->ctx, mode);
163 }
164 
drm_hdmi_get_max_resol(struct device * subdrv_dev,unsigned int * width,unsigned int * height)165 static void drm_hdmi_get_max_resol(struct device *subdrv_dev,
166 				unsigned int *width, unsigned int *height)
167 {
168 	struct drm_hdmi_context *ctx = to_context(subdrv_dev);
169 
170 	DRM_DEBUG_KMS("%s\n", __FILE__);
171 
172 	if (hdmi_ops && hdmi_ops->get_max_resol)
173 		hdmi_ops->get_max_resol(ctx->hdmi_ctx->ctx, width, height);
174 }
175 
drm_hdmi_commit(struct device * subdrv_dev)176 static void drm_hdmi_commit(struct device *subdrv_dev)
177 {
178 	struct drm_hdmi_context *ctx = to_context(subdrv_dev);
179 
180 	DRM_DEBUG_KMS("%s\n", __FILE__);
181 
182 	if (hdmi_ops && hdmi_ops->commit)
183 		hdmi_ops->commit(ctx->hdmi_ctx->ctx);
184 }
185 
drm_hdmi_dpms(struct device * subdrv_dev,int mode)186 static void drm_hdmi_dpms(struct device *subdrv_dev, int mode)
187 {
188 	struct drm_hdmi_context *ctx = to_context(subdrv_dev);
189 
190 	DRM_DEBUG_KMS("%s\n", __FILE__);
191 
192 	switch (mode) {
193 	case DRM_MODE_DPMS_ON:
194 		break;
195 	case DRM_MODE_DPMS_STANDBY:
196 	case DRM_MODE_DPMS_SUSPEND:
197 	case DRM_MODE_DPMS_OFF:
198 		if (hdmi_ops && hdmi_ops->disable)
199 			hdmi_ops->disable(ctx->hdmi_ctx->ctx);
200 		break;
201 	default:
202 		DRM_DEBUG_KMS("unkown dps mode: %d\n", mode);
203 		break;
204 	}
205 }
206 
207 static struct exynos_drm_manager_ops drm_hdmi_manager_ops = {
208 	.dpms = drm_hdmi_dpms,
209 	.enable_vblank = drm_hdmi_enable_vblank,
210 	.disable_vblank = drm_hdmi_disable_vblank,
211 	.mode_fixup = drm_hdmi_mode_fixup,
212 	.mode_set = drm_hdmi_mode_set,
213 	.get_max_resol = drm_hdmi_get_max_resol,
214 	.commit = drm_hdmi_commit,
215 };
216 
drm_mixer_mode_set(struct device * subdrv_dev,struct exynos_drm_overlay * overlay)217 static void drm_mixer_mode_set(struct device *subdrv_dev,
218 		struct exynos_drm_overlay *overlay)
219 {
220 	struct drm_hdmi_context *ctx = to_context(subdrv_dev);
221 
222 	DRM_DEBUG_KMS("%s\n", __FILE__);
223 
224 	if (mixer_ops && mixer_ops->win_mode_set)
225 		mixer_ops->win_mode_set(ctx->mixer_ctx->ctx, overlay);
226 }
227 
drm_mixer_commit(struct device * subdrv_dev,int zpos)228 static void drm_mixer_commit(struct device *subdrv_dev, int zpos)
229 {
230 	struct drm_hdmi_context *ctx = to_context(subdrv_dev);
231 
232 	DRM_DEBUG_KMS("%s\n", __FILE__);
233 
234 	if (mixer_ops && mixer_ops->win_commit)
235 		mixer_ops->win_commit(ctx->mixer_ctx->ctx, zpos);
236 }
237 
drm_mixer_disable(struct device * subdrv_dev,int zpos)238 static void drm_mixer_disable(struct device *subdrv_dev, int zpos)
239 {
240 	struct drm_hdmi_context *ctx = to_context(subdrv_dev);
241 
242 	DRM_DEBUG_KMS("%s\n", __FILE__);
243 
244 	if (mixer_ops && mixer_ops->win_disable)
245 		mixer_ops->win_disable(ctx->mixer_ctx->ctx, zpos);
246 }
247 
248 static struct exynos_drm_overlay_ops drm_hdmi_overlay_ops = {
249 	.mode_set = drm_mixer_mode_set,
250 	.commit = drm_mixer_commit,
251 	.disable = drm_mixer_disable,
252 };
253 
254 static struct exynos_drm_manager hdmi_manager = {
255 	.pipe		= -1,
256 	.ops		= &drm_hdmi_manager_ops,
257 	.overlay_ops	= &drm_hdmi_overlay_ops,
258 	.display_ops	= &drm_hdmi_display_ops,
259 };
260 
hdmi_subdrv_probe(struct drm_device * drm_dev,struct device * dev)261 static int hdmi_subdrv_probe(struct drm_device *drm_dev,
262 		struct device *dev)
263 {
264 	struct exynos_drm_subdrv *subdrv = to_subdrv(dev);
265 	struct drm_hdmi_context *ctx;
266 	struct platform_device *pdev = to_platform_device(dev);
267 	struct exynos_drm_common_hdmi_pd *pd;
268 
269 	DRM_DEBUG_KMS("%s\n", __FILE__);
270 
271 	pd = pdev->dev.platform_data;
272 
273 	if (!pd) {
274 		DRM_DEBUG_KMS("platform data is null.\n");
275 		return -EFAULT;
276 	}
277 
278 	if (!pd->hdmi_dev) {
279 		DRM_DEBUG_KMS("hdmi device is null.\n");
280 		return -EFAULT;
281 	}
282 
283 	if (!pd->mixer_dev) {
284 		DRM_DEBUG_KMS("mixer device is null.\n");
285 		return -EFAULT;
286 	}
287 
288 	ctx = get_ctx_from_subdrv(subdrv);
289 
290 	ctx->hdmi_ctx = (struct exynos_drm_hdmi_context *)
291 				to_context(pd->hdmi_dev);
292 	if (!ctx->hdmi_ctx) {
293 		DRM_DEBUG_KMS("hdmi context is null.\n");
294 		return -EFAULT;
295 	}
296 
297 	ctx->hdmi_ctx->drm_dev = drm_dev;
298 
299 	ctx->mixer_ctx = (struct exynos_drm_hdmi_context *)
300 				to_context(pd->mixer_dev);
301 	if (!ctx->mixer_ctx) {
302 		DRM_DEBUG_KMS("mixer context is null.\n");
303 		return -EFAULT;
304 	}
305 
306 	ctx->mixer_ctx->drm_dev = drm_dev;
307 
308 	return 0;
309 }
310 
exynos_drm_hdmi_probe(struct platform_device * pdev)311 static int __devinit exynos_drm_hdmi_probe(struct platform_device *pdev)
312 {
313 	struct device *dev = &pdev->dev;
314 	struct exynos_drm_subdrv *subdrv;
315 	struct drm_hdmi_context *ctx;
316 
317 	DRM_DEBUG_KMS("%s\n", __FILE__);
318 
319 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
320 	if (!ctx) {
321 		DRM_LOG_KMS("failed to alloc common hdmi context.\n");
322 		return -ENOMEM;
323 	}
324 
325 	subdrv = &ctx->subdrv;
326 
327 	subdrv->dev = dev;
328 	subdrv->manager = &hdmi_manager;
329 	subdrv->probe = hdmi_subdrv_probe;
330 
331 	platform_set_drvdata(pdev, subdrv);
332 
333 	exynos_drm_subdrv_register(subdrv);
334 
335 	return 0;
336 }
337 
hdmi_runtime_suspend(struct device * dev)338 static int hdmi_runtime_suspend(struct device *dev)
339 {
340 	DRM_DEBUG_KMS("%s\n", __FILE__);
341 
342 	return 0;
343 }
344 
hdmi_runtime_resume(struct device * dev)345 static int hdmi_runtime_resume(struct device *dev)
346 {
347 	DRM_DEBUG_KMS("%s\n", __FILE__);
348 
349 	return 0;
350 }
351 
352 static const struct dev_pm_ops hdmi_pm_ops = {
353 	.runtime_suspend = hdmi_runtime_suspend,
354 	.runtime_resume	 = hdmi_runtime_resume,
355 };
356 
exynos_drm_hdmi_remove(struct platform_device * pdev)357 static int __devexit exynos_drm_hdmi_remove(struct platform_device *pdev)
358 {
359 	struct drm_hdmi_context *ctx = platform_get_drvdata(pdev);
360 
361 	DRM_DEBUG_KMS("%s\n", __FILE__);
362 
363 	exynos_drm_subdrv_unregister(&ctx->subdrv);
364 	kfree(ctx);
365 
366 	return 0;
367 }
368 
369 struct platform_driver exynos_drm_common_hdmi_driver = {
370 	.probe		= exynos_drm_hdmi_probe,
371 	.remove		= __devexit_p(exynos_drm_hdmi_remove),
372 	.driver		= {
373 		.name	= "exynos-drm-hdmi",
374 		.owner	= THIS_MODULE,
375 		.pm = &hdmi_pm_ops,
376 	},
377 };
378