drm/exynos: Remove exynos_drm_hdmi shim
This patch trims exynos_drm_hdmi out of the driver. The reason it existed in the first place was to make up for the mixture of display/overlay/manager ops being spread across hdmi and mixer. With that code now rationalized, mixer and hdmi map directly to exynos_drm_crtc and exynos_drm_encoder, respectively. Since there is a 1:1 mapping, we no longer need this layer. Signed-off-by: Sean Paul <seanpaul@chromium.org> Signed-off-by: Inki Dae <inki.dae@samsung.com>
This commit is contained in:
Родитель
2b7681326d
Коммит
f041b257a8
|
@ -11,8 +11,7 @@ exynosdrm-y := exynos_drm_drv.o exynos_drm_encoder.o exynos_drm_connector.o \
|
|||
exynosdrm-$(CONFIG_DRM_EXYNOS_IOMMU) += exynos_drm_iommu.o
|
||||
exynosdrm-$(CONFIG_DRM_EXYNOS_DMABUF) += exynos_drm_dmabuf.o
|
||||
exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o
|
||||
exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o \
|
||||
exynos_drm_hdmi.o
|
||||
exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o
|
||||
exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI) += exynos_drm_vidi.o
|
||||
exynosdrm-$(CONFIG_DRM_EXYNOS_G2D) += exynos_drm_g2d.o
|
||||
exynosdrm-$(CONFIG_DRM_EXYNOS_IPP) += exynos_drm_ipp.o
|
||||
|
|
|
@ -370,13 +370,6 @@ static int __init exynos_drm_init(void)
|
|||
ret = platform_driver_register(&mixer_driver);
|
||||
if (ret < 0)
|
||||
goto out_mixer;
|
||||
ret = platform_driver_register(&exynos_drm_common_hdmi_driver);
|
||||
if (ret < 0)
|
||||
goto out_common_hdmi;
|
||||
|
||||
ret = exynos_platform_device_hdmi_register();
|
||||
if (ret < 0)
|
||||
goto out_common_hdmi_dev;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_VIDI
|
||||
|
@ -469,10 +462,6 @@ out_vidi:
|
|||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_HDMI
|
||||
exynos_platform_device_hdmi_unregister();
|
||||
out_common_hdmi_dev:
|
||||
platform_driver_unregister(&exynos_drm_common_hdmi_driver);
|
||||
out_common_hdmi:
|
||||
platform_driver_unregister(&mixer_driver);
|
||||
out_mixer:
|
||||
platform_driver_unregister(&hdmi_driver);
|
||||
|
@ -514,8 +503,6 @@ static void __exit exynos_drm_exit(void)
|
|||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_HDMI
|
||||
exynos_platform_device_hdmi_unregister();
|
||||
platform_driver_unregister(&exynos_drm_common_hdmi_driver);
|
||||
platform_driver_unregister(&mixer_driver);
|
||||
platform_driver_unregister(&hdmi_driver);
|
||||
#endif
|
||||
|
|
|
@ -1,416 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Samsung Electronics Co.Ltd
|
||||
* Authors:
|
||||
* Inki Dae <inki.dae@samsung.com>
|
||||
* Seung-Woo Kim <sw0312.kim@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <drm/exynos_drm.h>
|
||||
|
||||
#include "exynos_drm_drv.h"
|
||||
#include "exynos_drm_hdmi.h"
|
||||
|
||||
/* platform device pointer for common drm hdmi device. */
|
||||
static struct platform_device *exynos_drm_hdmi_pdev;
|
||||
|
||||
/* Common hdmi subdrv needs to access the hdmi and mixer though context.
|
||||
* These should be initialied by the repective drivers */
|
||||
static struct exynos_drm_hdmi_context *hdmi_ctx;
|
||||
static struct exynos_drm_hdmi_context *mixer_ctx;
|
||||
|
||||
/* these callback points shoud be set by specific drivers. */
|
||||
static struct exynos_hdmi_ops *hdmi_ops;
|
||||
static struct exynos_mixer_ops *mixer_ops;
|
||||
|
||||
struct drm_hdmi_context {
|
||||
struct exynos_drm_hdmi_context *hdmi_ctx;
|
||||
struct exynos_drm_hdmi_context *mixer_ctx;
|
||||
|
||||
bool enabled[MIXER_WIN_NR];
|
||||
};
|
||||
|
||||
int exynos_platform_device_hdmi_register(void)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
|
||||
if (exynos_drm_hdmi_pdev)
|
||||
return -EEXIST;
|
||||
|
||||
pdev = platform_device_register_simple(
|
||||
"exynos-drm-hdmi", -1, NULL, 0);
|
||||
if (IS_ERR(pdev))
|
||||
return PTR_ERR(pdev);
|
||||
|
||||
exynos_drm_hdmi_pdev = pdev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void exynos_platform_device_hdmi_unregister(void)
|
||||
{
|
||||
if (exynos_drm_hdmi_pdev) {
|
||||
platform_device_unregister(exynos_drm_hdmi_pdev);
|
||||
exynos_drm_hdmi_pdev = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void exynos_hdmi_drv_attach(struct exynos_drm_hdmi_context *ctx)
|
||||
{
|
||||
if (ctx)
|
||||
hdmi_ctx = ctx;
|
||||
}
|
||||
|
||||
void exynos_mixer_drv_attach(struct exynos_drm_hdmi_context *ctx)
|
||||
{
|
||||
if (ctx)
|
||||
mixer_ctx = ctx;
|
||||
}
|
||||
|
||||
void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops)
|
||||
{
|
||||
if (ops)
|
||||
hdmi_ops = ops;
|
||||
}
|
||||
|
||||
void exynos_mixer_ops_register(struct exynos_mixer_ops *ops)
|
||||
{
|
||||
if (ops)
|
||||
mixer_ops = ops;
|
||||
}
|
||||
|
||||
static int drm_hdmi_display_initialize(struct exynos_drm_display *display,
|
||||
struct drm_device *drm_dev)
|
||||
{
|
||||
struct drm_hdmi_context *ctx = display->ctx;
|
||||
|
||||
if (hdmi_ops && hdmi_ops->initialize)
|
||||
return hdmi_ops->initialize(ctx->hdmi_ctx->ctx, drm_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static bool drm_hdmi_is_connected(struct exynos_drm_display *display)
|
||||
{
|
||||
struct drm_hdmi_context *ctx = display->ctx;
|
||||
|
||||
if (hdmi_ops && hdmi_ops->is_connected)
|
||||
return hdmi_ops->is_connected(ctx->hdmi_ctx->ctx);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct edid *drm_hdmi_get_edid(struct exynos_drm_display *display,
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
struct drm_hdmi_context *ctx = display->ctx;
|
||||
|
||||
if (hdmi_ops && hdmi_ops->get_edid)
|
||||
return hdmi_ops->get_edid(ctx->hdmi_ctx->ctx, connector);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
static int drm_hdmi_check_mode_ctx(struct drm_hdmi_context *ctx,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
* Both, mixer and hdmi should be able to handle the requested mode.
|
||||
* If any of the two fails, return mode as BAD.
|
||||
*/
|
||||
|
||||
if (mixer_ops && mixer_ops->check_mode)
|
||||
ret = mixer_ops->check_mode(ctx->mixer_ctx->ctx, mode);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (hdmi_ops && hdmi_ops->check_mode)
|
||||
return hdmi_ops->check_mode(ctx->hdmi_ctx->ctx, mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int drm_hdmi_check_mode(struct exynos_drm_display *display,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct drm_hdmi_context *ctx = display->ctx;
|
||||
|
||||
return drm_hdmi_check_mode_ctx(ctx, mode);
|
||||
}
|
||||
|
||||
static void drm_hdmi_display_dpms(struct exynos_drm_display *display, int mode)
|
||||
{
|
||||
struct drm_hdmi_context *ctx = display->ctx;
|
||||
|
||||
if (hdmi_ops && hdmi_ops->dpms)
|
||||
hdmi_ops->dpms(ctx->hdmi_ctx->ctx, mode);
|
||||
}
|
||||
|
||||
static void drm_hdmi_mode_fixup(struct exynos_drm_display *display,
|
||||
struct drm_connector *connector,
|
||||
const struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct drm_hdmi_context *ctx = display->ctx;
|
||||
struct drm_display_mode *m;
|
||||
int mode_ok;
|
||||
|
||||
drm_mode_set_crtcinfo(adjusted_mode, 0);
|
||||
|
||||
mode_ok = drm_hdmi_check_mode_ctx(ctx, adjusted_mode);
|
||||
|
||||
/* just return if user desired mode exists. */
|
||||
if (mode_ok == 0)
|
||||
return;
|
||||
|
||||
/*
|
||||
* otherwise, find the most suitable mode among modes and change it
|
||||
* to adjusted_mode.
|
||||
*/
|
||||
list_for_each_entry(m, &connector->modes, head) {
|
||||
mode_ok = drm_hdmi_check_mode_ctx(ctx, m);
|
||||
|
||||
if (mode_ok == 0) {
|
||||
struct drm_mode_object base;
|
||||
struct list_head head;
|
||||
|
||||
DRM_INFO("desired mode doesn't exist so\n");
|
||||
DRM_INFO("use the most suitable mode among modes.\n");
|
||||
|
||||
DRM_DEBUG_KMS("Adjusted Mode: [%d]x[%d] [%d]Hz\n",
|
||||
m->hdisplay, m->vdisplay, m->vrefresh);
|
||||
|
||||
/* preserve display mode header while copying. */
|
||||
head = adjusted_mode->head;
|
||||
base = adjusted_mode->base;
|
||||
memcpy(adjusted_mode, m, sizeof(*m));
|
||||
adjusted_mode->head = head;
|
||||
adjusted_mode->base = base;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void drm_hdmi_mode_set(struct exynos_drm_display *display,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct drm_hdmi_context *ctx = display->ctx;
|
||||
|
||||
if (hdmi_ops && hdmi_ops->mode_set)
|
||||
hdmi_ops->mode_set(ctx->hdmi_ctx->ctx, mode);
|
||||
}
|
||||
|
||||
static void drm_hdmi_get_max_resol(struct exynos_drm_display *display,
|
||||
unsigned int *width, unsigned int *height)
|
||||
{
|
||||
struct drm_hdmi_context *ctx = display->ctx;
|
||||
|
||||
if (hdmi_ops && hdmi_ops->get_max_resol)
|
||||
hdmi_ops->get_max_resol(ctx->hdmi_ctx->ctx, width, height);
|
||||
}
|
||||
|
||||
static struct exynos_drm_display_ops drm_hdmi_display_ops = {
|
||||
.initialize = drm_hdmi_display_initialize,
|
||||
.is_connected = drm_hdmi_is_connected,
|
||||
.get_edid = drm_hdmi_get_edid,
|
||||
.check_mode = drm_hdmi_check_mode,
|
||||
.dpms = drm_hdmi_display_dpms,
|
||||
.mode_fixup = drm_hdmi_mode_fixup,
|
||||
.mode_set = drm_hdmi_mode_set,
|
||||
.get_max_resol = drm_hdmi_get_max_resol,
|
||||
};
|
||||
|
||||
static struct exynos_drm_display hdmi_display = {
|
||||
.type = EXYNOS_DISPLAY_TYPE_HDMI,
|
||||
.ops = &drm_hdmi_display_ops,
|
||||
};
|
||||
|
||||
static int drm_hdmi_enable_vblank(struct exynos_drm_manager *mgr)
|
||||
{
|
||||
struct drm_hdmi_context *ctx = mgr->ctx;
|
||||
|
||||
if (mixer_ops && mixer_ops->enable_vblank)
|
||||
return mixer_ops->enable_vblank(ctx->mixer_ctx->ctx, mgr->pipe);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void drm_hdmi_disable_vblank(struct exynos_drm_manager *mgr)
|
||||
{
|
||||
struct drm_hdmi_context *ctx = mgr->ctx;
|
||||
|
||||
if (mixer_ops && mixer_ops->disable_vblank)
|
||||
return mixer_ops->disable_vblank(ctx->mixer_ctx->ctx);
|
||||
}
|
||||
|
||||
static void drm_hdmi_wait_for_vblank(struct exynos_drm_manager *mgr)
|
||||
{
|
||||
struct drm_hdmi_context *ctx = mgr->ctx;
|
||||
|
||||
if (mixer_ops && mixer_ops->wait_for_vblank)
|
||||
mixer_ops->wait_for_vblank(ctx->mixer_ctx->ctx);
|
||||
}
|
||||
|
||||
static void drm_hdmi_commit(struct exynos_drm_manager *mgr)
|
||||
{
|
||||
struct drm_hdmi_context *ctx = mgr->ctx;
|
||||
|
||||
if (hdmi_ops && hdmi_ops->commit)
|
||||
hdmi_ops->commit(ctx->hdmi_ctx->ctx);
|
||||
}
|
||||
|
||||
static int drm_hdmi_mgr_initialize(struct exynos_drm_manager *mgr,
|
||||
struct drm_device *drm_dev, int pipe)
|
||||
{
|
||||
struct drm_hdmi_context *ctx = mgr->ctx;
|
||||
int ret = 0;
|
||||
|
||||
if (!hdmi_ctx) {
|
||||
DRM_ERROR("hdmi context not initialized.\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (!mixer_ctx) {
|
||||
DRM_ERROR("mixer context not initialized.\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
ctx->hdmi_ctx = hdmi_ctx;
|
||||
ctx->mixer_ctx = mixer_ctx;
|
||||
|
||||
if (mixer_ops && mixer_ops->initialize)
|
||||
ret = mixer_ops->initialize(ctx->mixer_ctx->ctx, drm_dev);
|
||||
|
||||
if (mixer_ops->iommu_on)
|
||||
mixer_ops->iommu_on(ctx->mixer_ctx->ctx, true);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void drm_hdmi_mgr_remove(struct exynos_drm_manager *mgr)
|
||||
{
|
||||
struct drm_hdmi_context *ctx = mgr->ctx;
|
||||
|
||||
if (mixer_ops->iommu_on)
|
||||
mixer_ops->iommu_on(ctx->mixer_ctx->ctx, false);
|
||||
}
|
||||
|
||||
static void drm_hdmi_dpms(struct exynos_drm_manager *mgr, int mode)
|
||||
{
|
||||
struct drm_hdmi_context *ctx = mgr->ctx;
|
||||
|
||||
if (mixer_ops && mixer_ops->dpms)
|
||||
mixer_ops->dpms(ctx->mixer_ctx->ctx, mode);
|
||||
|
||||
if (hdmi_ops && hdmi_ops->dpms)
|
||||
hdmi_ops->dpms(ctx->hdmi_ctx->ctx, mode);
|
||||
}
|
||||
|
||||
static void drm_mixer_win_mode_set(struct exynos_drm_manager *mgr,
|
||||
struct exynos_drm_overlay *overlay)
|
||||
{
|
||||
struct drm_hdmi_context *ctx = mgr->ctx;
|
||||
|
||||
if (mixer_ops && mixer_ops->win_mode_set)
|
||||
mixer_ops->win_mode_set(ctx->mixer_ctx->ctx, overlay);
|
||||
}
|
||||
|
||||
static void drm_mixer_win_commit(struct exynos_drm_manager *mgr, int zpos)
|
||||
{
|
||||
struct drm_hdmi_context *ctx = mgr->ctx;
|
||||
int win = (zpos == DEFAULT_ZPOS) ? MIXER_DEFAULT_WIN : zpos;
|
||||
|
||||
if (win < 0 || win >= MIXER_WIN_NR) {
|
||||
DRM_ERROR("mixer window[%d] is wrong\n", win);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mixer_ops && mixer_ops->win_commit)
|
||||
mixer_ops->win_commit(ctx->mixer_ctx->ctx, win);
|
||||
|
||||
ctx->enabled[win] = true;
|
||||
}
|
||||
|
||||
static void drm_mixer_win_disable(struct exynos_drm_manager *mgr, int zpos)
|
||||
{
|
||||
struct drm_hdmi_context *ctx = mgr->ctx;
|
||||
int win = (zpos == DEFAULT_ZPOS) ? MIXER_DEFAULT_WIN : zpos;
|
||||
|
||||
if (win < 0 || win >= MIXER_WIN_NR) {
|
||||
DRM_ERROR("mixer window[%d] is wrong\n", win);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mixer_ops && mixer_ops->win_disable)
|
||||
mixer_ops->win_disable(ctx->mixer_ctx->ctx, win);
|
||||
|
||||
ctx->enabled[win] = false;
|
||||
}
|
||||
|
||||
static struct exynos_drm_manager_ops drm_hdmi_manager_ops = {
|
||||
.initialize = drm_hdmi_mgr_initialize,
|
||||
.remove = drm_hdmi_mgr_remove,
|
||||
.dpms = drm_hdmi_dpms,
|
||||
.enable_vblank = drm_hdmi_enable_vblank,
|
||||
.disable_vblank = drm_hdmi_disable_vblank,
|
||||
.wait_for_vblank = drm_hdmi_wait_for_vblank,
|
||||
.commit = drm_hdmi_commit,
|
||||
.win_mode_set = drm_mixer_win_mode_set,
|
||||
.win_commit = drm_mixer_win_commit,
|
||||
.win_disable = drm_mixer_win_disable,
|
||||
};
|
||||
|
||||
static struct exynos_drm_manager hdmi_manager = {
|
||||
.type = EXYNOS_DISPLAY_TYPE_HDMI,
|
||||
.ops = &drm_hdmi_manager_ops,
|
||||
};
|
||||
|
||||
static int exynos_drm_hdmi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct drm_hdmi_context *ctx;
|
||||
|
||||
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
hdmi_manager.ctx = ctx;
|
||||
hdmi_display.ctx = ctx;
|
||||
|
||||
exynos_drm_manager_register(&hdmi_manager);
|
||||
exynos_drm_display_register(&hdmi_display);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exynos_drm_hdmi_remove(struct platform_device *pdev)
|
||||
{
|
||||
exynos_drm_display_unregister(&hdmi_display);
|
||||
exynos_drm_manager_unregister(&hdmi_manager);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct platform_driver exynos_drm_common_hdmi_driver = {
|
||||
.probe = exynos_drm_hdmi_probe,
|
||||
.remove = exynos_drm_hdmi_remove,
|
||||
.driver = {
|
||||
.name = "exynos-drm-hdmi",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
|
@ -1,69 +0,0 @@
|
|||
/* exynos_drm_hdmi.h
|
||||
*
|
||||
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
|
||||
* Authoer: Inki Dae <inki.dae@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef _EXYNOS_DRM_HDMI_H_
|
||||
#define _EXYNOS_DRM_HDMI_H_
|
||||
|
||||
#define MIXER_WIN_NR 3
|
||||
#define MIXER_DEFAULT_WIN 0
|
||||
|
||||
/*
|
||||
* exynos hdmi common context structure.
|
||||
*
|
||||
* @drm_dev: pointer to drm_device.
|
||||
* @pipe: pipe for mixer
|
||||
* @ctx: pointer to the context of specific device driver.
|
||||
* this context should be hdmi_context or mixer_context.
|
||||
*/
|
||||
struct exynos_drm_hdmi_context {
|
||||
int pipe;
|
||||
void *ctx;
|
||||
};
|
||||
|
||||
struct exynos_hdmi_ops {
|
||||
/* display */
|
||||
int (*initialize)(void *ctx, struct drm_device *drm_dev);
|
||||
bool (*is_connected)(void *ctx);
|
||||
struct edid *(*get_edid)(void *ctx,
|
||||
struct drm_connector *connector);
|
||||
int (*check_mode)(void *ctx, struct drm_display_mode *mode);
|
||||
void (*dpms)(void *ctx, int mode);
|
||||
|
||||
/* manager */
|
||||
void (*mode_set)(void *ctx, struct drm_display_mode *mode);
|
||||
void (*get_max_resol)(void *ctx, unsigned int *width,
|
||||
unsigned int *height);
|
||||
void (*commit)(void *ctx);
|
||||
};
|
||||
|
||||
struct exynos_mixer_ops {
|
||||
/* manager */
|
||||
int (*initialize)(void *ctx, struct drm_device *drm_dev);
|
||||
int (*iommu_on)(void *ctx, bool enable);
|
||||
int (*enable_vblank)(void *ctx, int pipe);
|
||||
void (*disable_vblank)(void *ctx);
|
||||
void (*wait_for_vblank)(void *ctx);
|
||||
void (*dpms)(void *ctx, int mode);
|
||||
|
||||
/* overlay */
|
||||
void (*win_mode_set)(void *ctx, struct exynos_drm_overlay *overlay);
|
||||
void (*win_commit)(void *ctx, int zpos);
|
||||
void (*win_disable)(void *ctx, int zpos);
|
||||
|
||||
/* display */
|
||||
int (*check_mode)(void *ctx, struct drm_display_mode *mode);
|
||||
};
|
||||
|
||||
void exynos_hdmi_drv_attach(struct exynos_drm_hdmi_context *ctx);
|
||||
void exynos_mixer_drv_attach(struct exynos_drm_hdmi_context *ctx);
|
||||
void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops);
|
||||
void exynos_mixer_ops_register(struct exynos_mixer_ops *ops);
|
||||
#endif
|
|
@ -40,14 +40,14 @@
|
|||
#include <drm/exynos_drm.h>
|
||||
|
||||
#include "exynos_drm_drv.h"
|
||||
#include "exynos_drm_hdmi.h"
|
||||
#include "exynos_mixer.h"
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <media/s5p_hdmi.h>
|
||||
|
||||
#define MAX_WIDTH 1920
|
||||
#define MAX_HEIGHT 1080
|
||||
#define get_hdmi_context(dev) platform_get_drvdata(to_platform_device(dev))
|
||||
#define get_hdmi_display(dev) platform_get_drvdata(to_platform_device(dev))
|
||||
|
||||
/* AVI header and aspect ratio */
|
||||
#define HDMI_AVI_VERSION 0x02
|
||||
|
@ -178,7 +178,6 @@ struct hdmi_context {
|
|||
struct mutex hdmi_mutex;
|
||||
|
||||
void __iomem *regs;
|
||||
void *parent_ctx;
|
||||
int irq;
|
||||
|
||||
struct i2c_client *ddc_port;
|
||||
|
@ -791,26 +790,28 @@ static void hdmi_reg_infoframe(struct hdmi_context *hdata,
|
|||
}
|
||||
}
|
||||
|
||||
static int hdmi_initialize(void *ctx, struct drm_device *drm_dev)
|
||||
static int hdmi_initialize(struct exynos_drm_display *display,
|
||||
struct drm_device *drm_dev)
|
||||
{
|
||||
struct hdmi_context *hdata = ctx;
|
||||
struct hdmi_context *hdata = display->ctx;
|
||||
|
||||
hdata->drm_dev = drm_dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool hdmi_is_connected(void *ctx)
|
||||
static bool hdmi_is_connected(struct exynos_drm_display *display)
|
||||
{
|
||||
struct hdmi_context *hdata = ctx;
|
||||
struct hdmi_context *hdata = display->ctx;
|
||||
|
||||
return hdata->hpd;
|
||||
}
|
||||
|
||||
static struct edid *hdmi_get_edid(void *ctx, struct drm_connector *connector)
|
||||
static struct edid *hdmi_get_edid(struct exynos_drm_display *display,
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
struct edid *raw_edid;
|
||||
struct hdmi_context *hdata = ctx;
|
||||
struct hdmi_context *hdata = display->ctx;
|
||||
|
||||
if (!hdata->ddc_port)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
@ -849,9 +850,10 @@ static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int hdmi_check_mode(void *ctx, struct drm_display_mode *mode)
|
||||
static int hdmi_check_mode(struct exynos_drm_display *display,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct hdmi_context *hdata = ctx;
|
||||
struct hdmi_context *hdata = display->ctx;
|
||||
int ret;
|
||||
|
||||
DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
|
||||
|
@ -859,12 +861,62 @@ static int hdmi_check_mode(void *ctx, struct drm_display_mode *mode)
|
|||
(mode->flags & DRM_MODE_FLAG_INTERLACE) ? true :
|
||||
false, mode->clock * 1000);
|
||||
|
||||
ret = mixer_check_mode(mode);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = hdmi_find_phy_conf(hdata, mode->clock * 1000);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hdmi_mode_fixup(struct exynos_drm_display *display,
|
||||
struct drm_connector *connector,
|
||||
const struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct drm_display_mode *m;
|
||||
int mode_ok;
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
drm_mode_set_crtcinfo(adjusted_mode, 0);
|
||||
|
||||
mode_ok = hdmi_check_mode(display, adjusted_mode);
|
||||
|
||||
/* just return if user desired mode exists. */
|
||||
if (mode_ok == 0)
|
||||
return;
|
||||
|
||||
/*
|
||||
* otherwise, find the most suitable mode among modes and change it
|
||||
* to adjusted_mode.
|
||||
*/
|
||||
list_for_each_entry(m, &connector->modes, head) {
|
||||
mode_ok = hdmi_check_mode(display, m);
|
||||
|
||||
if (mode_ok == 0) {
|
||||
struct drm_mode_object base;
|
||||
struct list_head head;
|
||||
|
||||
DRM_INFO("desired mode doesn't exist so\n");
|
||||
DRM_INFO("use the most suitable mode among modes.\n");
|
||||
|
||||
DRM_DEBUG_KMS("Adjusted Mode: [%d]x[%d] [%d]Hz\n",
|
||||
m->hdisplay, m->vdisplay, m->vrefresh);
|
||||
|
||||
/* preserve display mode header while copying. */
|
||||
head = adjusted_mode->head;
|
||||
base = adjusted_mode->base;
|
||||
memcpy(adjusted_mode, m, sizeof(*m));
|
||||
adjusted_mode->head = head;
|
||||
adjusted_mode->base = base;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void hdmi_set_acr(u32 freq, u8 *acr)
|
||||
{
|
||||
u32 n, cts;
|
||||
|
@ -1692,9 +1744,10 @@ static void hdmi_v14_mode_set(struct hdmi_context *hdata,
|
|||
hdmi_set_reg(tg->tg_3d, 1, 0x0);
|
||||
}
|
||||
|
||||
static void hdmi_mode_set(void *ctx, struct drm_display_mode *mode)
|
||||
static void hdmi_mode_set(struct exynos_drm_display *display,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct hdmi_context *hdata = ctx;
|
||||
struct hdmi_context *hdata = display->ctx;
|
||||
struct drm_display_mode *m = mode;
|
||||
|
||||
DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%s\n",
|
||||
|
@ -1708,16 +1761,16 @@ static void hdmi_mode_set(void *ctx, struct drm_display_mode *mode)
|
|||
hdmi_v14_mode_set(hdata, mode);
|
||||
}
|
||||
|
||||
static void hdmi_get_max_resol(void *ctx, unsigned int *width,
|
||||
unsigned int *height)
|
||||
static void hdmi_get_max_resol(struct exynos_drm_display *display,
|
||||
unsigned int *width, unsigned int *height)
|
||||
{
|
||||
*width = MAX_WIDTH;
|
||||
*height = MAX_HEIGHT;
|
||||
}
|
||||
|
||||
static void hdmi_commit(void *ctx)
|
||||
static void hdmi_commit(struct exynos_drm_display *display)
|
||||
{
|
||||
struct hdmi_context *hdata = ctx;
|
||||
struct hdmi_context *hdata = display->ctx;
|
||||
|
||||
mutex_lock(&hdata->hdmi_mutex);
|
||||
if (!hdata->powered) {
|
||||
|
@ -1729,8 +1782,9 @@ static void hdmi_commit(void *ctx)
|
|||
hdmi_conf_apply(hdata);
|
||||
}
|
||||
|
||||
static void hdmi_poweron(struct hdmi_context *hdata)
|
||||
static void hdmi_poweron(struct exynos_drm_display *display)
|
||||
{
|
||||
struct hdmi_context *hdata = display->ctx;
|
||||
struct hdmi_resources *res = &hdata->res;
|
||||
|
||||
mutex_lock(&hdata->hdmi_mutex);
|
||||
|
@ -1751,11 +1805,12 @@ static void hdmi_poweron(struct hdmi_context *hdata)
|
|||
clk_prepare_enable(res->sclk_hdmi);
|
||||
|
||||
hdmiphy_poweron(hdata);
|
||||
hdmi_commit(hdata);
|
||||
hdmi_commit(display);
|
||||
}
|
||||
|
||||
static void hdmi_poweroff(struct hdmi_context *hdata)
|
||||
static void hdmi_poweroff(struct exynos_drm_display *display)
|
||||
{
|
||||
struct hdmi_context *hdata = display->ctx;
|
||||
struct hdmi_resources *res = &hdata->res;
|
||||
|
||||
mutex_lock(&hdata->hdmi_mutex);
|
||||
|
@ -1783,9 +1838,9 @@ out:
|
|||
mutex_unlock(&hdata->hdmi_mutex);
|
||||
}
|
||||
|
||||
static void hdmi_dpms(void *ctx, int mode)
|
||||
static void hdmi_dpms(struct exynos_drm_display *display, int mode)
|
||||
{
|
||||
struct hdmi_context *hdata = ctx;
|
||||
struct hdmi_context *hdata = display->ctx;
|
||||
|
||||
DRM_DEBUG_KMS("mode %d\n", mode);
|
||||
|
||||
|
@ -1806,24 +1861,26 @@ static void hdmi_dpms(void *ctx, int mode)
|
|||
}
|
||||
}
|
||||
|
||||
static struct exynos_hdmi_ops hdmi_ops = {
|
||||
/* display */
|
||||
static struct exynos_drm_display_ops hdmi_display_ops = {
|
||||
.initialize = hdmi_initialize,
|
||||
.is_connected = hdmi_is_connected,
|
||||
.get_max_resol = hdmi_get_max_resol,
|
||||
.get_edid = hdmi_get_edid,
|
||||
.check_mode = hdmi_check_mode,
|
||||
.dpms = hdmi_dpms,
|
||||
|
||||
/* manager */
|
||||
.mode_fixup = hdmi_mode_fixup,
|
||||
.mode_set = hdmi_mode_set,
|
||||
.get_max_resol = hdmi_get_max_resol,
|
||||
.dpms = hdmi_dpms,
|
||||
.commit = hdmi_commit,
|
||||
};
|
||||
|
||||
static struct exynos_drm_display hdmi_display = {
|
||||
.type = EXYNOS_DISPLAY_TYPE_HDMI,
|
||||
.ops = &hdmi_display_ops,
|
||||
};
|
||||
|
||||
static irqreturn_t hdmi_irq_thread(int irq, void *arg)
|
||||
{
|
||||
struct exynos_drm_hdmi_context *ctx = arg;
|
||||
struct hdmi_context *hdata = ctx->ctx;
|
||||
struct hdmi_context *hdata = arg;
|
||||
|
||||
mutex_lock(&hdata->hdmi_mutex);
|
||||
hdata->hpd = gpio_get_value(hdata->hpd_gpio);
|
||||
|
@ -1945,7 +2002,6 @@ static struct of_device_id hdmi_match_types[] = {
|
|||
static int hdmi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct exynos_drm_hdmi_context *drm_hdmi_ctx;
|
||||
struct hdmi_context *hdata;
|
||||
struct s5p_hdmi_platform_data *pdata;
|
||||
struct resource *res;
|
||||
|
@ -1960,20 +2016,13 @@ static int hdmi_probe(struct platform_device *pdev)
|
|||
if (!pdata)
|
||||
return -EINVAL;
|
||||
|
||||
drm_hdmi_ctx = devm_kzalloc(dev, sizeof(*drm_hdmi_ctx), GFP_KERNEL);
|
||||
if (!drm_hdmi_ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
hdata = devm_kzalloc(dev, sizeof(struct hdmi_context), GFP_KERNEL);
|
||||
if (!hdata)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&hdata->hdmi_mutex);
|
||||
|
||||
drm_hdmi_ctx->ctx = (void *)hdata;
|
||||
hdata->parent_ctx = (void *)drm_hdmi_ctx;
|
||||
|
||||
platform_set_drvdata(pdev, drm_hdmi_ctx);
|
||||
platform_set_drvdata(pdev, &hdmi_display);
|
||||
|
||||
match = of_match_node(hdmi_match_types, dev->of_node);
|
||||
if (!match)
|
||||
|
@ -2038,17 +2087,14 @@ static int hdmi_probe(struct platform_device *pdev)
|
|||
ret = devm_request_threaded_irq(dev, hdata->irq, NULL,
|
||||
hdmi_irq_thread, IRQF_TRIGGER_RISING |
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
"hdmi", drm_hdmi_ctx);
|
||||
"hdmi", hdata);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to register hdmi interrupt\n");
|
||||
goto err_hdmiphy;
|
||||
}
|
||||
|
||||
/* Attach HDMI Driver to common hdmi. */
|
||||
exynos_hdmi_drv_attach(drm_hdmi_ctx);
|
||||
|
||||
/* register specific callbacks to common hdmi. */
|
||||
exynos_hdmi_ops_register(&hdmi_ops);
|
||||
hdmi_display.ctx = hdata;
|
||||
exynos_drm_display_register(&hdmi_display);
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
|
@ -2064,8 +2110,8 @@ err_ddc:
|
|||
static int hdmi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
|
||||
struct hdmi_context *hdata = ctx->ctx;
|
||||
struct exynos_drm_display *display = get_hdmi_display(dev);
|
||||
struct hdmi_context *hdata = display->ctx;
|
||||
|
||||
pm_runtime_disable(dev);
|
||||
|
||||
|
@ -2078,8 +2124,8 @@ static int hdmi_remove(struct platform_device *pdev)
|
|||
#ifdef CONFIG_PM_SLEEP
|
||||
static int hdmi_suspend(struct device *dev)
|
||||
{
|
||||
struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
|
||||
struct hdmi_context *hdata = ctx->ctx;
|
||||
struct exynos_drm_display *display = get_hdmi_display(dev);
|
||||
struct hdmi_context *hdata = display->ctx;
|
||||
|
||||
disable_irq(hdata->irq);
|
||||
|
||||
|
@ -2092,15 +2138,15 @@ static int hdmi_suspend(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
hdmi_poweroff(hdata);
|
||||
hdmi_poweroff(display);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdmi_resume(struct device *dev)
|
||||
{
|
||||
struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
|
||||
struct hdmi_context *hdata = ctx->ctx;
|
||||
struct exynos_drm_display *display = get_hdmi_display(dev);
|
||||
struct hdmi_context *hdata = display->ctx;
|
||||
|
||||
hdata->hpd = gpio_get_value(hdata->hpd_gpio);
|
||||
|
||||
|
@ -2111,7 +2157,7 @@ static int hdmi_resume(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
hdmi_poweron(hdata);
|
||||
hdmi_poweron(display);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2120,20 +2166,18 @@ static int hdmi_resume(struct device *dev)
|
|||
#ifdef CONFIG_PM_RUNTIME
|
||||
static int hdmi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
|
||||
struct hdmi_context *hdata = ctx->ctx;
|
||||
struct exynos_drm_display *display = get_hdmi_display(dev);
|
||||
|
||||
hdmi_poweroff(hdata);
|
||||
hdmi_poweroff(display);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdmi_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
|
||||
struct hdmi_context *hdata = ctx->ctx;
|
||||
struct exynos_drm_display *display = get_hdmi_display(dev);
|
||||
|
||||
hdmi_poweron(hdata);
|
||||
hdmi_poweron(display);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -36,10 +36,13 @@
|
|||
|
||||
#include "exynos_drm_drv.h"
|
||||
#include "exynos_drm_crtc.h"
|
||||
#include "exynos_drm_hdmi.h"
|
||||
#include "exynos_drm_iommu.h"
|
||||
#include "exynos_mixer.h"
|
||||
|
||||
#define get_mixer_context(dev) platform_get_drvdata(to_platform_device(dev))
|
||||
#define get_mixer_manager(dev) platform_get_drvdata(to_platform_device(dev))
|
||||
|
||||
#define MIXER_WIN_NR 3
|
||||
#define MIXER_DEFAULT_WIN 0
|
||||
|
||||
struct hdmi_win_data {
|
||||
dma_addr_t dma_addr;
|
||||
|
@ -95,7 +98,6 @@ struct mixer_context {
|
|||
struct mixer_resources mixer_res;
|
||||
struct hdmi_win_data win_data[MIXER_WIN_NR];
|
||||
enum mixer_version_id mxr_ver;
|
||||
void *parent_ctx;
|
||||
wait_queue_head_t wait_vsync_queue;
|
||||
atomic_t wait_vsync_event;
|
||||
};
|
||||
|
@ -827,12 +829,14 @@ static int vp_resources_init(struct mixer_context *mixer_ctx)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int mixer_initialize(void *ctx, struct drm_device *drm_dev)
|
||||
static int mixer_initialize(struct exynos_drm_manager *mgr,
|
||||
struct drm_device *drm_dev, int pipe)
|
||||
{
|
||||
int ret;
|
||||
struct mixer_context *mixer_ctx = ctx;
|
||||
struct mixer_context *mixer_ctx = mgr->ctx;
|
||||
|
||||
mixer_ctx->drm_dev = drm_dev;
|
||||
mixer_ctx->pipe = pipe;
|
||||
|
||||
/* acquire resources: regs, irqs, clocks */
|
||||
ret = mixer_resources_init(mixer_ctx);
|
||||
|
@ -850,29 +854,29 @@ static int mixer_initialize(void *ctx, struct drm_device *drm_dev)
|
|||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
if (!is_drm_iommu_supported(mixer_ctx->drm_dev))
|
||||
return 0;
|
||||
|
||||
return drm_iommu_attach_device(mixer_ctx->drm_dev, mixer_ctx->dev);
|
||||
}
|
||||
|
||||
static int mixer_iommu_on(void *ctx, bool enable)
|
||||
static void mixer_mgr_remove(struct exynos_drm_manager *mgr)
|
||||
{
|
||||
struct mixer_context *mdata = ctx;
|
||||
struct mixer_context *mixer_ctx = mgr->ctx;
|
||||
|
||||
if (is_drm_iommu_supported(mdata->drm_dev)) {
|
||||
if (enable)
|
||||
return drm_iommu_attach_device(mdata->drm_dev,
|
||||
mdata->dev);
|
||||
|
||||
drm_iommu_detach_device(mdata->drm_dev, mdata->dev);
|
||||
}
|
||||
return 0;
|
||||
if (is_drm_iommu_supported(mixer_ctx->drm_dev))
|
||||
drm_iommu_detach_device(mixer_ctx->drm_dev, mixer_ctx->dev);
|
||||
}
|
||||
|
||||
static int mixer_enable_vblank(void *ctx, int pipe)
|
||||
static int mixer_enable_vblank(struct exynos_drm_manager *mgr)
|
||||
{
|
||||
struct mixer_context *mixer_ctx = ctx;
|
||||
struct mixer_context *mixer_ctx = mgr->ctx;
|
||||
struct mixer_resources *res = &mixer_ctx->mixer_res;
|
||||
|
||||
mixer_ctx->pipe = pipe;
|
||||
if (!mixer_ctx->powered) {
|
||||
mixer_ctx->int_en |= MXR_INT_EN_VSYNC;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* enable vsync interrupt */
|
||||
mixer_reg_writemask(res, MXR_INT_EN, MXR_INT_EN_VSYNC,
|
||||
|
@ -881,19 +885,19 @@ static int mixer_enable_vblank(void *ctx, int pipe)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void mixer_disable_vblank(void *ctx)
|
||||
static void mixer_disable_vblank(struct exynos_drm_manager *mgr)
|
||||
{
|
||||
struct mixer_context *mixer_ctx = ctx;
|
||||
struct mixer_context *mixer_ctx = mgr->ctx;
|
||||
struct mixer_resources *res = &mixer_ctx->mixer_res;
|
||||
|
||||
/* disable vsync interrupt */
|
||||
mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC);
|
||||
}
|
||||
|
||||
static void mixer_win_mode_set(void *ctx,
|
||||
struct exynos_drm_overlay *overlay)
|
||||
static void mixer_win_mode_set(struct exynos_drm_manager *mgr,
|
||||
struct exynos_drm_overlay *overlay)
|
||||
{
|
||||
struct mixer_context *mixer_ctx = ctx;
|
||||
struct mixer_context *mixer_ctx = mgr->ctx;
|
||||
struct hdmi_win_data *win_data;
|
||||
int win;
|
||||
|
||||
|
@ -942,9 +946,10 @@ static void mixer_win_mode_set(void *ctx,
|
|||
win_data->scan_flags = overlay->scan_flag;
|
||||
}
|
||||
|
||||
static void mixer_win_commit(void *ctx, int win)
|
||||
static void mixer_win_commit(struct exynos_drm_manager *mgr, int zpos)
|
||||
{
|
||||
struct mixer_context *mixer_ctx = ctx;
|
||||
struct mixer_context *mixer_ctx = mgr->ctx;
|
||||
int win = zpos == DEFAULT_ZPOS ? MIXER_DEFAULT_WIN : zpos;
|
||||
|
||||
DRM_DEBUG_KMS("win: %d\n", win);
|
||||
|
||||
|
@ -963,10 +968,11 @@ static void mixer_win_commit(void *ctx, int win)
|
|||
mixer_ctx->win_data[win].enabled = true;
|
||||
}
|
||||
|
||||
static void mixer_win_disable(void *ctx, int win)
|
||||
static void mixer_win_disable(struct exynos_drm_manager *mgr, int zpos)
|
||||
{
|
||||
struct mixer_context *mixer_ctx = ctx;
|
||||
struct mixer_context *mixer_ctx = mgr->ctx;
|
||||
struct mixer_resources *res = &mixer_ctx->mixer_res;
|
||||
int win = zpos == DEFAULT_ZPOS ? MIXER_DEFAULT_WIN : zpos;
|
||||
unsigned long flags;
|
||||
|
||||
DRM_DEBUG_KMS("win: %d\n", win);
|
||||
|
@ -990,32 +996,9 @@ static void mixer_win_disable(void *ctx, int win)
|
|||
mixer_ctx->win_data[win].enabled = false;
|
||||
}
|
||||
|
||||
static int mixer_check_mode(void *ctx, struct drm_display_mode *mode)
|
||||
static void mixer_wait_for_vblank(struct exynos_drm_manager *mgr)
|
||||
{
|
||||
struct mixer_context *mixer_ctx = ctx;
|
||||
u32 w, h;
|
||||
|
||||
w = mode->hdisplay;
|
||||
h = mode->vdisplay;
|
||||
|
||||
DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d\n",
|
||||
mode->hdisplay, mode->vdisplay, mode->vrefresh,
|
||||
(mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
|
||||
|
||||
if (mixer_ctx->mxr_ver == MXR_VER_0_0_0_16 ||
|
||||
mixer_ctx->mxr_ver == MXR_VER_128_0_0_184)
|
||||
return 0;
|
||||
|
||||
if ((w >= 464 && w <= 720 && h >= 261 && h <= 576) ||
|
||||
(w >= 1024 && w <= 1280 && h >= 576 && h <= 720) ||
|
||||
(w >= 1664 && w <= 1920 && h >= 936 && h <= 1080))
|
||||
return 0;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
static void mixer_wait_for_vblank(void *ctx)
|
||||
{
|
||||
struct mixer_context *mixer_ctx = ctx;
|
||||
struct mixer_context *mixer_ctx = mgr->ctx;
|
||||
|
||||
mutex_lock(&mixer_ctx->mixer_mutex);
|
||||
if (!mixer_ctx->powered) {
|
||||
|
@ -1036,21 +1019,23 @@ static void mixer_wait_for_vblank(void *ctx)
|
|||
DRM_DEBUG_KMS("vblank wait timed out.\n");
|
||||
}
|
||||
|
||||
static void mixer_window_suspend(struct mixer_context *ctx)
|
||||
static void mixer_window_suspend(struct exynos_drm_manager *mgr)
|
||||
{
|
||||
struct mixer_context *ctx = mgr->ctx;
|
||||
struct hdmi_win_data *win_data;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MIXER_WIN_NR; i++) {
|
||||
win_data = &ctx->win_data[i];
|
||||
win_data->resume = win_data->enabled;
|
||||
mixer_win_disable(ctx, i);
|
||||
mixer_win_disable(mgr, i);
|
||||
}
|
||||
mixer_wait_for_vblank(ctx);
|
||||
mixer_wait_for_vblank(mgr);
|
||||
}
|
||||
|
||||
static void mixer_window_resume(struct mixer_context *ctx)
|
||||
static void mixer_window_resume(struct exynos_drm_manager *mgr)
|
||||
{
|
||||
struct mixer_context *ctx = mgr->ctx;
|
||||
struct hdmi_win_data *win_data;
|
||||
int i;
|
||||
|
||||
|
@ -1059,12 +1044,13 @@ static void mixer_window_resume(struct mixer_context *ctx)
|
|||
win_data->enabled = win_data->resume;
|
||||
win_data->resume = false;
|
||||
if (win_data->enabled)
|
||||
mixer_win_commit(ctx, i);
|
||||
mixer_win_commit(mgr, i);
|
||||
}
|
||||
}
|
||||
|
||||
static void mixer_poweron(struct mixer_context *ctx)
|
||||
static void mixer_poweron(struct exynos_drm_manager *mgr)
|
||||
{
|
||||
struct mixer_context *ctx = mgr->ctx;
|
||||
struct mixer_resources *res = &ctx->mixer_res;
|
||||
|
||||
mutex_lock(&ctx->mixer_mutex);
|
||||
|
@ -1084,11 +1070,12 @@ static void mixer_poweron(struct mixer_context *ctx)
|
|||
mixer_reg_write(res, MXR_INT_EN, ctx->int_en);
|
||||
mixer_win_reset(ctx);
|
||||
|
||||
mixer_window_resume(ctx);
|
||||
mixer_window_resume(mgr);
|
||||
}
|
||||
|
||||
static void mixer_poweroff(struct mixer_context *ctx)
|
||||
static void mixer_poweroff(struct exynos_drm_manager *mgr)
|
||||
{
|
||||
struct mixer_context *ctx = mgr->ctx;
|
||||
struct mixer_resources *res = &ctx->mixer_res;
|
||||
|
||||
mutex_lock(&ctx->mixer_mutex);
|
||||
|
@ -1096,7 +1083,7 @@ static void mixer_poweroff(struct mixer_context *ctx)
|
|||
goto out;
|
||||
mutex_unlock(&ctx->mixer_mutex);
|
||||
|
||||
mixer_window_suspend(ctx);
|
||||
mixer_window_suspend(mgr);
|
||||
|
||||
ctx->int_en = mixer_reg_read(res, MXR_INT_EN);
|
||||
|
||||
|
@ -1113,9 +1100,9 @@ out:
|
|||
mutex_unlock(&ctx->mixer_mutex);
|
||||
}
|
||||
|
||||
static void mixer_dpms(void *ctx, int mode)
|
||||
static void mixer_dpms(struct exynos_drm_manager *mgr, int mode)
|
||||
{
|
||||
struct mixer_context *mixer_ctx = ctx;
|
||||
struct mixer_context *mixer_ctx = mgr->ctx;
|
||||
|
||||
switch (mode) {
|
||||
case DRM_MODE_DPMS_ON:
|
||||
|
@ -1134,20 +1121,41 @@ static void mixer_dpms(void *ctx, int mode)
|
|||
}
|
||||
}
|
||||
|
||||
static struct exynos_mixer_ops mixer_ops = {
|
||||
/* manager */
|
||||
/* Only valid for Mixer version 16.0.33.0 */
|
||||
int mixer_check_mode(struct drm_display_mode *mode)
|
||||
{
|
||||
u32 w, h;
|
||||
|
||||
w = mode->hdisplay;
|
||||
h = mode->vdisplay;
|
||||
|
||||
DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d\n",
|
||||
mode->hdisplay, mode->vdisplay, mode->vrefresh,
|
||||
(mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
|
||||
|
||||
if ((w >= 464 && w <= 720 && h >= 261 && h <= 576) ||
|
||||
(w >= 1024 && w <= 1280 && h >= 576 && h <= 720) ||
|
||||
(w >= 1664 && w <= 1920 && h >= 936 && h <= 1080))
|
||||
return 0;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static struct exynos_drm_manager_ops mixer_manager_ops = {
|
||||
.initialize = mixer_initialize,
|
||||
.iommu_on = mixer_iommu_on,
|
||||
.remove = mixer_mgr_remove,
|
||||
.dpms = mixer_dpms,
|
||||
.enable_vblank = mixer_enable_vblank,
|
||||
.disable_vblank = mixer_disable_vblank,
|
||||
.wait_for_vblank = mixer_wait_for_vblank,
|
||||
.dpms = mixer_dpms,
|
||||
.win_mode_set = mixer_win_mode_set,
|
||||
.win_commit = mixer_win_commit,
|
||||
.win_disable = mixer_win_disable,
|
||||
};
|
||||
|
||||
/* display */
|
||||
.check_mode = mixer_check_mode,
|
||||
static struct exynos_drm_manager mixer_manager = {
|
||||
.type = EXYNOS_DISPLAY_TYPE_HDMI,
|
||||
.ops = &mixer_manager_ops,
|
||||
};
|
||||
|
||||
static struct mixer_drv_data exynos5420_mxr_drv_data = {
|
||||
|
@ -1195,20 +1203,16 @@ static struct of_device_id mixer_match_types[] = {
|
|||
static int mixer_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct exynos_drm_hdmi_context *drm_hdmi_ctx;
|
||||
struct mixer_context *ctx;
|
||||
struct mixer_drv_data *drv;
|
||||
|
||||
dev_info(dev, "probe start\n");
|
||||
|
||||
drm_hdmi_ctx = devm_kzalloc(dev, sizeof(*drm_hdmi_ctx),
|
||||
GFP_KERNEL);
|
||||
if (!drm_hdmi_ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx) {
|
||||
DRM_ERROR("failed to alloc mixer context.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
mutex_init(&ctx->mixer_mutex);
|
||||
|
||||
|
@ -1223,20 +1227,14 @@ static int mixer_probe(struct platform_device *pdev)
|
|||
|
||||
ctx->pdev = pdev;
|
||||
ctx->dev = dev;
|
||||
ctx->parent_ctx = (void *)drm_hdmi_ctx;
|
||||
drm_hdmi_ctx->ctx = (void *)ctx;
|
||||
ctx->vp_enabled = drv->is_vp_enabled;
|
||||
ctx->mxr_ver = drv->version;
|
||||
init_waitqueue_head(&ctx->wait_vsync_queue);
|
||||
atomic_set(&ctx->wait_vsync_event, 0);
|
||||
|
||||
platform_set_drvdata(pdev, drm_hdmi_ctx);
|
||||
|
||||
/* attach mixer driver to common hdmi. */
|
||||
exynos_mixer_drv_attach(drm_hdmi_ctx);
|
||||
|
||||
/* register specific callback point to common hdmi. */
|
||||
exynos_mixer_ops_register(&mixer_ops);
|
||||
mixer_manager.ctx = ctx;
|
||||
platform_set_drvdata(pdev, &mixer_manager);
|
||||
exynos_drm_manager_register(&mixer_manager);
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
|
@ -1255,30 +1253,28 @@ static int mixer_remove(struct platform_device *pdev)
|
|||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mixer_suspend(struct device *dev)
|
||||
{
|
||||
struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev);
|
||||
struct mixer_context *ctx = drm_hdmi_ctx->ctx;
|
||||
struct exynos_drm_manager *mgr = get_mixer_manager(dev);
|
||||
|
||||
if (pm_runtime_suspended(dev)) {
|
||||
DRM_DEBUG_KMS("Already suspended\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
mixer_poweroff(ctx);
|
||||
mixer_poweroff(mgr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mixer_resume(struct device *dev)
|
||||
{
|
||||
struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev);
|
||||
struct mixer_context *ctx = drm_hdmi_ctx->ctx;
|
||||
struct exynos_drm_manager *mgr = get_mixer_manager(dev);
|
||||
|
||||
if (!pm_runtime_suspended(dev)) {
|
||||
DRM_DEBUG_KMS("Already resumed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
mixer_poweron(ctx);
|
||||
mixer_poweron(mgr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1287,20 +1283,18 @@ static int mixer_resume(struct device *dev)
|
|||
#ifdef CONFIG_PM_RUNTIME
|
||||
static int mixer_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev);
|
||||
struct mixer_context *ctx = drm_hdmi_ctx->ctx;
|
||||
struct exynos_drm_manager *mgr = get_mixer_manager(dev);
|
||||
|
||||
mixer_poweroff(ctx);
|
||||
mixer_poweroff(mgr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mixer_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev);
|
||||
struct mixer_context *ctx = drm_hdmi_ctx->ctx;
|
||||
struct exynos_drm_manager *mgr = get_mixer_manager(dev);
|
||||
|
||||
mixer_poweron(ctx);
|
||||
mixer_poweron(mgr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Google, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef _EXYNOS_MIXER_H_
|
||||
#define _EXYNOS_MIXER_H_
|
||||
|
||||
/* This function returns 0 if the given timing is valid for the mixer */
|
||||
int mixer_check_mode(struct drm_display_mode *mode);
|
||||
|
||||
#endif
|
Загрузка…
Ссылка в новой задаче