Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.10-rc1 483 lines 12 kB view raw
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 <drm/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/* platform device pointer for common drm hdmi device. */ 33static struct platform_device *exynos_drm_hdmi_pdev; 34 35/* Common hdmi subdrv needs to access the hdmi and mixer though context. 36* These should be initialied by the repective drivers */ 37static struct exynos_drm_hdmi_context *hdmi_ctx; 38static struct exynos_drm_hdmi_context *mixer_ctx; 39 40/* these callback points shoud be set by specific drivers. */ 41static struct exynos_hdmi_ops *hdmi_ops; 42static struct exynos_mixer_ops *mixer_ops; 43 44struct drm_hdmi_context { 45 struct exynos_drm_subdrv subdrv; 46 struct exynos_drm_hdmi_context *hdmi_ctx; 47 struct exynos_drm_hdmi_context *mixer_ctx; 48 49 bool enabled[MIXER_WIN_NR]; 50}; 51 52int exynos_platform_device_hdmi_register(void) 53{ 54 struct platform_device *pdev; 55 56 if (exynos_drm_hdmi_pdev) 57 return -EEXIST; 58 59 pdev = platform_device_register_simple( 60 "exynos-drm-hdmi", -1, NULL, 0); 61 if (IS_ERR(pdev)) 62 return PTR_ERR(pdev); 63 64 exynos_drm_hdmi_pdev = pdev; 65 66 return 0; 67} 68 69void exynos_platform_device_hdmi_unregister(void) 70{ 71 if (exynos_drm_hdmi_pdev) { 72 platform_device_unregister(exynos_drm_hdmi_pdev); 73 exynos_drm_hdmi_pdev = NULL; 74 } 75} 76 77void exynos_hdmi_drv_attach(struct exynos_drm_hdmi_context *ctx) 78{ 79 if (ctx) 80 hdmi_ctx = ctx; 81} 82 83void exynos_mixer_drv_attach(struct exynos_drm_hdmi_context *ctx) 84{ 85 if (ctx) 86 mixer_ctx = ctx; 87} 88 89void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops) 90{ 91 DRM_DEBUG_KMS("%s\n", __FILE__); 92 93 if (ops) 94 hdmi_ops = ops; 95} 96 97void exynos_mixer_ops_register(struct exynos_mixer_ops *ops) 98{ 99 DRM_DEBUG_KMS("%s\n", __FILE__); 100 101 if (ops) 102 mixer_ops = ops; 103} 104 105static bool drm_hdmi_is_connected(struct device *dev) 106{ 107 struct drm_hdmi_context *ctx = to_context(dev); 108 109 DRM_DEBUG_KMS("%s\n", __FILE__); 110 111 if (hdmi_ops && hdmi_ops->is_connected) 112 return hdmi_ops->is_connected(ctx->hdmi_ctx->ctx); 113 114 return false; 115} 116 117static struct edid *drm_hdmi_get_edid(struct device *dev, 118 struct drm_connector *connector) 119{ 120 struct drm_hdmi_context *ctx = to_context(dev); 121 122 DRM_DEBUG_KMS("%s\n", __FILE__); 123 124 if (hdmi_ops && hdmi_ops->get_edid) 125 return hdmi_ops->get_edid(ctx->hdmi_ctx->ctx, connector); 126 127 return NULL; 128} 129 130static int drm_hdmi_check_timing(struct device *dev, void *timing) 131{ 132 struct drm_hdmi_context *ctx = to_context(dev); 133 int ret = 0; 134 135 DRM_DEBUG_KMS("%s\n", __FILE__); 136 137 /* 138 * Both, mixer and hdmi should be able to handle the requested mode. 139 * If any of the two fails, return mode as BAD. 140 */ 141 142 if (mixer_ops && mixer_ops->check_timing) 143 ret = mixer_ops->check_timing(ctx->mixer_ctx->ctx, timing); 144 145 if (ret) 146 return ret; 147 148 if (hdmi_ops && hdmi_ops->check_timing) 149 return hdmi_ops->check_timing(ctx->hdmi_ctx->ctx, timing); 150 151 return 0; 152} 153 154static int drm_hdmi_power_on(struct device *dev, int mode) 155{ 156 struct drm_hdmi_context *ctx = to_context(dev); 157 158 DRM_DEBUG_KMS("%s\n", __FILE__); 159 160 if (hdmi_ops && hdmi_ops->power_on) 161 return hdmi_ops->power_on(ctx->hdmi_ctx->ctx, mode); 162 163 return 0; 164} 165 166static struct exynos_drm_display_ops drm_hdmi_display_ops = { 167 .type = EXYNOS_DISPLAY_TYPE_HDMI, 168 .is_connected = drm_hdmi_is_connected, 169 .get_edid = drm_hdmi_get_edid, 170 .check_timing = drm_hdmi_check_timing, 171 .power_on = drm_hdmi_power_on, 172}; 173 174static int drm_hdmi_enable_vblank(struct device *subdrv_dev) 175{ 176 struct drm_hdmi_context *ctx = to_context(subdrv_dev); 177 struct exynos_drm_subdrv *subdrv = &ctx->subdrv; 178 struct exynos_drm_manager *manager = subdrv->manager; 179 180 DRM_DEBUG_KMS("%s\n", __FILE__); 181 182 if (mixer_ops && mixer_ops->enable_vblank) 183 return mixer_ops->enable_vblank(ctx->mixer_ctx->ctx, 184 manager->pipe); 185 186 return 0; 187} 188 189static void drm_hdmi_disable_vblank(struct device *subdrv_dev) 190{ 191 struct drm_hdmi_context *ctx = to_context(subdrv_dev); 192 193 DRM_DEBUG_KMS("%s\n", __FILE__); 194 195 if (mixer_ops && mixer_ops->disable_vblank) 196 return mixer_ops->disable_vblank(ctx->mixer_ctx->ctx); 197} 198 199static void drm_hdmi_wait_for_vblank(struct device *subdrv_dev) 200{ 201 struct drm_hdmi_context *ctx = to_context(subdrv_dev); 202 203 DRM_DEBUG_KMS("%s\n", __FILE__); 204 205 if (mixer_ops && mixer_ops->wait_for_vblank) 206 mixer_ops->wait_for_vblank(ctx->mixer_ctx->ctx); 207} 208 209static void drm_hdmi_mode_fixup(struct device *subdrv_dev, 210 struct drm_connector *connector, 211 const struct drm_display_mode *mode, 212 struct drm_display_mode *adjusted_mode) 213{ 214 struct drm_display_mode *m; 215 int mode_ok; 216 217 DRM_DEBUG_KMS("%s\n", __FILE__); 218 219 drm_mode_set_crtcinfo(adjusted_mode, 0); 220 221 mode_ok = drm_hdmi_check_timing(subdrv_dev, adjusted_mode); 222 223 /* just return if user desired mode exists. */ 224 if (mode_ok == 0) 225 return; 226 227 /* 228 * otherwise, find the most suitable mode among modes and change it 229 * to adjusted_mode. 230 */ 231 list_for_each_entry(m, &connector->modes, head) { 232 mode_ok = drm_hdmi_check_timing(subdrv_dev, m); 233 234 if (mode_ok == 0) { 235 struct drm_mode_object base; 236 struct list_head head; 237 238 DRM_INFO("desired mode doesn't exist so\n"); 239 DRM_INFO("use the most suitable mode among modes.\n"); 240 241 DRM_DEBUG_KMS("Adjusted Mode: [%d]x[%d] [%d]Hz\n", 242 m->hdisplay, m->vdisplay, m->vrefresh); 243 244 /* preserve display mode header while copying. */ 245 head = adjusted_mode->head; 246 base = adjusted_mode->base; 247 memcpy(adjusted_mode, m, sizeof(*m)); 248 adjusted_mode->head = head; 249 adjusted_mode->base = base; 250 break; 251 } 252 } 253} 254 255static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode) 256{ 257 struct drm_hdmi_context *ctx = to_context(subdrv_dev); 258 259 DRM_DEBUG_KMS("%s\n", __FILE__); 260 261 if (hdmi_ops && hdmi_ops->mode_set) 262 hdmi_ops->mode_set(ctx->hdmi_ctx->ctx, mode); 263} 264 265static void drm_hdmi_get_max_resol(struct device *subdrv_dev, 266 unsigned int *width, unsigned int *height) 267{ 268 struct drm_hdmi_context *ctx = to_context(subdrv_dev); 269 270 DRM_DEBUG_KMS("%s\n", __FILE__); 271 272 if (hdmi_ops && hdmi_ops->get_max_resol) 273 hdmi_ops->get_max_resol(ctx->hdmi_ctx->ctx, width, height); 274} 275 276static void drm_hdmi_commit(struct device *subdrv_dev) 277{ 278 struct drm_hdmi_context *ctx = to_context(subdrv_dev); 279 280 DRM_DEBUG_KMS("%s\n", __FILE__); 281 282 if (hdmi_ops && hdmi_ops->commit) 283 hdmi_ops->commit(ctx->hdmi_ctx->ctx); 284} 285 286static void drm_hdmi_dpms(struct device *subdrv_dev, int mode) 287{ 288 struct drm_hdmi_context *ctx = to_context(subdrv_dev); 289 290 DRM_DEBUG_KMS("%s\n", __FILE__); 291 292 if (mixer_ops && mixer_ops->dpms) 293 mixer_ops->dpms(ctx->mixer_ctx->ctx, mode); 294 295 if (hdmi_ops && hdmi_ops->dpms) 296 hdmi_ops->dpms(ctx->hdmi_ctx->ctx, mode); 297} 298 299static void drm_hdmi_apply(struct device *subdrv_dev) 300{ 301 struct drm_hdmi_context *ctx = to_context(subdrv_dev); 302 int i; 303 304 DRM_DEBUG_KMS("%s\n", __FILE__); 305 306 for (i = 0; i < MIXER_WIN_NR; i++) { 307 if (!ctx->enabled[i]) 308 continue; 309 if (mixer_ops && mixer_ops->win_commit) 310 mixer_ops->win_commit(ctx->mixer_ctx->ctx, i); 311 } 312 313 if (hdmi_ops && hdmi_ops->commit) 314 hdmi_ops->commit(ctx->hdmi_ctx->ctx); 315} 316 317static struct exynos_drm_manager_ops drm_hdmi_manager_ops = { 318 .dpms = drm_hdmi_dpms, 319 .apply = drm_hdmi_apply, 320 .enable_vblank = drm_hdmi_enable_vblank, 321 .disable_vblank = drm_hdmi_disable_vblank, 322 .wait_for_vblank = drm_hdmi_wait_for_vblank, 323 .mode_fixup = drm_hdmi_mode_fixup, 324 .mode_set = drm_hdmi_mode_set, 325 .get_max_resol = drm_hdmi_get_max_resol, 326 .commit = drm_hdmi_commit, 327}; 328 329static void drm_mixer_mode_set(struct device *subdrv_dev, 330 struct exynos_drm_overlay *overlay) 331{ 332 struct drm_hdmi_context *ctx = to_context(subdrv_dev); 333 334 DRM_DEBUG_KMS("%s\n", __FILE__); 335 336 if (mixer_ops && mixer_ops->win_mode_set) 337 mixer_ops->win_mode_set(ctx->mixer_ctx->ctx, overlay); 338} 339 340static void drm_mixer_commit(struct device *subdrv_dev, int zpos) 341{ 342 struct drm_hdmi_context *ctx = to_context(subdrv_dev); 343 int win = (zpos == DEFAULT_ZPOS) ? MIXER_DEFAULT_WIN : zpos; 344 345 DRM_DEBUG_KMS("%s\n", __FILE__); 346 347 if (win < 0 || win > MIXER_WIN_NR) { 348 DRM_ERROR("mixer window[%d] is wrong\n", win); 349 return; 350 } 351 352 if (mixer_ops && mixer_ops->win_commit) 353 mixer_ops->win_commit(ctx->mixer_ctx->ctx, win); 354 355 ctx->enabled[win] = true; 356} 357 358static void drm_mixer_disable(struct device *subdrv_dev, int zpos) 359{ 360 struct drm_hdmi_context *ctx = to_context(subdrv_dev); 361 int win = (zpos == DEFAULT_ZPOS) ? MIXER_DEFAULT_WIN : zpos; 362 363 DRM_DEBUG_KMS("%s\n", __FILE__); 364 365 if (win < 0 || win > MIXER_WIN_NR) { 366 DRM_ERROR("mixer window[%d] is wrong\n", win); 367 return; 368 } 369 370 if (mixer_ops && mixer_ops->win_disable) 371 mixer_ops->win_disable(ctx->mixer_ctx->ctx, win); 372 373 ctx->enabled[win] = false; 374} 375 376static struct exynos_drm_overlay_ops drm_hdmi_overlay_ops = { 377 .mode_set = drm_mixer_mode_set, 378 .commit = drm_mixer_commit, 379 .disable = drm_mixer_disable, 380}; 381 382static struct exynos_drm_manager hdmi_manager = { 383 .pipe = -1, 384 .ops = &drm_hdmi_manager_ops, 385 .overlay_ops = &drm_hdmi_overlay_ops, 386 .display_ops = &drm_hdmi_display_ops, 387}; 388 389static int hdmi_subdrv_probe(struct drm_device *drm_dev, 390 struct device *dev) 391{ 392 struct exynos_drm_subdrv *subdrv = to_subdrv(dev); 393 struct drm_hdmi_context *ctx; 394 395 DRM_DEBUG_KMS("%s\n", __FILE__); 396 397 if (!hdmi_ctx) { 398 DRM_ERROR("hdmi context not initialized.\n"); 399 return -EFAULT; 400 } 401 402 if (!mixer_ctx) { 403 DRM_ERROR("mixer context not initialized.\n"); 404 return -EFAULT; 405 } 406 407 ctx = get_ctx_from_subdrv(subdrv); 408 409 if (!ctx) { 410 DRM_ERROR("no drm hdmi context.\n"); 411 return -EFAULT; 412 } 413 414 ctx->hdmi_ctx = hdmi_ctx; 415 ctx->mixer_ctx = mixer_ctx; 416 417 ctx->hdmi_ctx->drm_dev = drm_dev; 418 ctx->mixer_ctx->drm_dev = drm_dev; 419 420 if (mixer_ops->iommu_on) 421 mixer_ops->iommu_on(ctx->mixer_ctx->ctx, true); 422 423 return 0; 424} 425 426static void hdmi_subdrv_remove(struct drm_device *drm_dev, struct device *dev) 427{ 428 struct drm_hdmi_context *ctx; 429 struct exynos_drm_subdrv *subdrv = to_subdrv(dev); 430 431 ctx = get_ctx_from_subdrv(subdrv); 432 433 if (mixer_ops->iommu_on) 434 mixer_ops->iommu_on(ctx->mixer_ctx->ctx, false); 435} 436 437static int exynos_drm_hdmi_probe(struct platform_device *pdev) 438{ 439 struct device *dev = &pdev->dev; 440 struct exynos_drm_subdrv *subdrv; 441 struct drm_hdmi_context *ctx; 442 443 DRM_DEBUG_KMS("%s\n", __FILE__); 444 445 ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); 446 if (!ctx) { 447 DRM_LOG_KMS("failed to alloc common hdmi context.\n"); 448 return -ENOMEM; 449 } 450 451 subdrv = &ctx->subdrv; 452 453 subdrv->dev = dev; 454 subdrv->manager = &hdmi_manager; 455 subdrv->probe = hdmi_subdrv_probe; 456 subdrv->remove = hdmi_subdrv_remove; 457 458 platform_set_drvdata(pdev, subdrv); 459 460 exynos_drm_subdrv_register(subdrv); 461 462 return 0; 463} 464 465static int exynos_drm_hdmi_remove(struct platform_device *pdev) 466{ 467 struct drm_hdmi_context *ctx = platform_get_drvdata(pdev); 468 469 DRM_DEBUG_KMS("%s\n", __FILE__); 470 471 exynos_drm_subdrv_unregister(&ctx->subdrv); 472 473 return 0; 474} 475 476struct platform_driver exynos_drm_common_hdmi_driver = { 477 .probe = exynos_drm_hdmi_probe, 478 .remove = exynos_drm_hdmi_remove, 479 .driver = { 480 .name = "exynos-drm-hdmi", 481 .owner = THIS_MODULE, 482 }, 483};