Commit 080be03d authored by Sean Paul's avatar Sean Paul Committed by Inki Dae

drm/exynos: Split manager/display/subdrv

This patch splits display and manager from subdrv. The result is that
crtc functions can directly call into manager callbacks and encoder
functions can directly call into display callbacks. This will allow
us to remove the exynos_drm_hdmi shim and support mixer/hdmi & fimd/dp
with common code.
Signed-off-by: default avatarSean Paul <seanpaul@chromium.org>
Signed-off-by: default avatarInki Dae <inki.dae@samsung.com>
parent 3f283d93
......@@ -23,26 +23,20 @@
drm_connector)
struct exynos_drm_connector {
struct drm_connector drm_connector;
uint32_t encoder_id;
struct exynos_drm_manager *manager;
struct drm_connector drm_connector;
uint32_t encoder_id;
struct exynos_drm_display *display;
};
static int exynos_drm_connector_get_modes(struct drm_connector *connector)
{
struct exynos_drm_connector *exynos_connector =
to_exynos_connector(connector);
struct exynos_drm_manager *manager = exynos_connector->manager;
struct exynos_drm_display_ops *display_ops = manager->display_ops;
struct exynos_drm_display *display = exynos_connector->display;
struct edid *edid = NULL;
unsigned int count = 0;
int ret;
if (!display_ops) {
DRM_DEBUG_KMS("display_ops is null.\n");
return 0;
}
/*
* if get_edid() exists then get_edid() callback of hdmi side
* is called to get edid data through i2c interface else
......@@ -51,8 +45,8 @@ static int exynos_drm_connector_get_modes(struct drm_connector *connector)
* P.S. in case of lcd panel, count is always 1 if success
* because lcd panel has only one mode.
*/
if (display_ops->get_edid) {
edid = display_ops->get_edid(manager->dev, connector);
if (display->ops->get_edid) {
edid = display->ops->get_edid(display, connector);
if (IS_ERR_OR_NULL(edid)) {
ret = PTR_ERR(edid);
edid = NULL;
......@@ -75,8 +69,8 @@ static int exynos_drm_connector_get_modes(struct drm_connector *connector)
return 0;
}
if (display_ops->get_panel)
panel = display_ops->get_panel(manager->dev);
if (display->ops->get_panel)
panel = display->ops->get_panel(display);
else {
drm_mode_destroy(connector->dev, mode);
return 0;
......@@ -105,14 +99,13 @@ static int exynos_drm_connector_mode_valid(struct drm_connector *connector,
{
struct exynos_drm_connector *exynos_connector =
to_exynos_connector(connector);
struct exynos_drm_manager *manager = exynos_connector->manager;
struct exynos_drm_display_ops *display_ops = manager->display_ops;
struct exynos_drm_display *display = exynos_connector->display;
int ret = MODE_BAD;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (display_ops && display_ops->check_mode)
if (!display_ops->check_mode(manager->dev, mode))
if (display->ops->check_mode)
if (!display->ops->check_mode(display, mode))
ret = MODE_OK;
return ret;
......@@ -151,8 +144,7 @@ static int exynos_drm_connector_fill_modes(struct drm_connector *connector,
{
struct exynos_drm_connector *exynos_connector =
to_exynos_connector(connector);
struct exynos_drm_manager *manager = exynos_connector->manager;
struct exynos_drm_manager_ops *ops = manager->ops;
struct exynos_drm_display *display = exynos_connector->display;
unsigned int width, height;
width = max_width;
......@@ -162,8 +154,8 @@ static int exynos_drm_connector_fill_modes(struct drm_connector *connector,
* if specific driver want to find desired_mode using maxmum
* resolution then get max width and height from that driver.
*/
if (ops && ops->get_max_resol)
ops->get_max_resol(manager, &width, &height);
if (display->ops->get_max_resol)
display->ops->get_max_resol(display, &width, &height);
return drm_helper_probe_single_connector_modes(connector, width,
height);
......@@ -175,13 +167,11 @@ exynos_drm_connector_detect(struct drm_connector *connector, bool force)
{
struct exynos_drm_connector *exynos_connector =
to_exynos_connector(connector);
struct exynos_drm_manager *manager = exynos_connector->manager;
struct exynos_drm_display_ops *display_ops =
manager->display_ops;
struct exynos_drm_display *display = exynos_connector->display;
enum drm_connector_status status = connector_status_disconnected;
if (display_ops && display_ops->is_connected) {
if (display_ops->is_connected(manager->dev))
if (display->ops->is_connected) {
if (display->ops->is_connected(display))
status = connector_status_connected;
else
status = connector_status_disconnected;
......@@ -211,7 +201,7 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
struct drm_encoder *encoder)
{
struct exynos_drm_connector *exynos_connector;
struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
struct exynos_drm_display *display = exynos_drm_get_display(encoder);
struct drm_connector *connector;
int type;
int err;
......@@ -222,7 +212,7 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
connector = &exynos_connector->drm_connector;
switch (manager->display_ops->type) {
switch (display->type) {
case EXYNOS_DISPLAY_TYPE_HDMI:
type = DRM_MODE_CONNECTOR_HDMIA;
connector->interlace_allowed = true;
......@@ -245,7 +235,7 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
goto err_connector;
exynos_connector->encoder_id = encoder->base.id;
exynos_connector->manager = manager;
exynos_connector->display = display;
connector->dpms = DRM_MODE_DPMS_OFF;
connector->encoder = encoder;
......
......@@ -14,24 +14,31 @@
#include <drm/drmP.h>
#include "exynos_drm_drv.h"
#include "exynos_drm_crtc.h"
#include "exynos_drm_encoder.h"
#include "exynos_drm_connector.h"
#include "exynos_drm_fbdev.h"
static LIST_HEAD(exynos_drm_subdrv_list);
static LIST_HEAD(exynos_drm_manager_list);
static LIST_HEAD(exynos_drm_display_list);
static int exynos_drm_create_enc_conn(struct drm_device *dev,
struct exynos_drm_subdrv *subdrv)
struct exynos_drm_display *display)
{
struct drm_encoder *encoder;
struct drm_connector *connector;
struct exynos_drm_manager *manager;
int ret;
unsigned long possible_crtcs = 0;
subdrv->manager->dev = subdrv->dev;
/* Find possible crtcs for this display */
list_for_each_entry(manager, &exynos_drm_manager_list, list)
if (manager->type == display->type)
possible_crtcs |= 1 << manager->pipe;
/* create and initialize a encoder for this sub driver. */
encoder = exynos_drm_encoder_create(dev, subdrv->manager,
(1 << MAX_CRTC) - 1);
encoder = exynos_drm_encoder_create(dev, display, possible_crtcs);
if (!encoder) {
DRM_ERROR("failed to create encoder\n");
return -EFAULT;
......@@ -48,8 +55,8 @@ static int exynos_drm_create_enc_conn(struct drm_device *dev,
goto err_destroy_encoder;
}
subdrv->encoder = encoder;
subdrv->connector = connector;
display->encoder = encoder;
display->connector = connector;
return 0;
......@@ -58,21 +65,6 @@ static int exynos_drm_create_enc_conn(struct drm_device *dev,
return ret;
}
static void exynos_drm_destroy_enc_conn(struct exynos_drm_subdrv *subdrv)
{
if (subdrv->encoder) {
struct drm_encoder *encoder = subdrv->encoder;
encoder->funcs->destroy(encoder);
subdrv->encoder = NULL;
}
if (subdrv->connector) {
struct drm_connector *connector = subdrv->connector;
connector->funcs->destroy(connector);
subdrv->connector = NULL;
}
}
static int exynos_drm_subdrv_probe(struct drm_device *dev,
struct exynos_drm_subdrv *subdrv)
{
......@@ -104,10 +96,98 @@ static void exynos_drm_subdrv_remove(struct drm_device *dev,
subdrv->remove(dev, subdrv->dev);
}
int exynos_drm_initialize_managers(struct drm_device *dev)
{
struct exynos_drm_manager *manager, *n;
int ret, pipe = 0;
list_for_each_entry(manager, &exynos_drm_manager_list, list) {
if (manager->ops->initialize) {
ret = manager->ops->initialize(manager, dev, pipe);
if (ret) {
DRM_ERROR("Mgr init [%d] failed with %d\n",
manager->type, ret);
goto err;
}
}
manager->drm_dev = dev;
manager->pipe = pipe++;
ret = exynos_drm_crtc_create(manager);
if (ret) {
DRM_ERROR("CRTC create [%d] failed with %d\n",
manager->type, ret);
goto err;
}
}
return 0;
err:
list_for_each_entry_safe(manager, n, &exynos_drm_manager_list, list) {
if (pipe-- > 0)
exynos_drm_manager_unregister(manager);
else
list_del(&manager->list);
}
return ret;
}
void exynos_drm_remove_managers(struct drm_device *dev)
{
struct exynos_drm_manager *manager, *n;
list_for_each_entry_safe(manager, n, &exynos_drm_manager_list, list)
exynos_drm_manager_unregister(manager);
}
int exynos_drm_initialize_displays(struct drm_device *dev)
{
struct exynos_drm_display *display, *n;
int ret, initialized = 0;
list_for_each_entry(display, &exynos_drm_display_list, list) {
if (display->ops->initialize) {
ret = display->ops->initialize(display, dev);
if (ret) {
DRM_ERROR("Display init [%d] failed with %d\n",
display->type, ret);
goto err;
}
}
initialized++;
ret = exynos_drm_create_enc_conn(dev, display);
if (ret) {
DRM_ERROR("Encoder create [%d] failed with %d\n",
display->type, ret);
goto err;
}
}
return 0;
err:
list_for_each_entry_safe(display, n, &exynos_drm_display_list, list) {
if (initialized-- > 0)
exynos_drm_display_unregister(display);
else
list_del(&display->list);
}
return ret;
}
void exynos_drm_remove_displays(struct drm_device *dev)
{
struct exynos_drm_display *display, *n;
list_for_each_entry_safe(display, n, &exynos_drm_display_list, list)
exynos_drm_display_unregister(display);
}
int exynos_drm_device_register(struct drm_device *dev)
{
struct exynos_drm_subdrv *subdrv, *n;
unsigned int fine_cnt = 0;
int err;
if (!dev)
......@@ -120,30 +200,8 @@ int exynos_drm_device_register(struct drm_device *dev)
list_del(&subdrv->list);
continue;
}
/*
* if manager is null then it means that this sub driver
* doesn't need encoder and connector.
*/
if (!subdrv->manager) {
fine_cnt++;
continue;
}
err = exynos_drm_create_enc_conn(dev, subdrv);
if (err) {
DRM_DEBUG("failed to create encoder and connector.\n");
exynos_drm_subdrv_remove(dev, subdrv);
list_del(&subdrv->list);
continue;
}
fine_cnt++;
}
if (!fine_cnt)
return -EINVAL;
return 0;
}
EXPORT_SYMBOL_GPL(exynos_drm_device_register);
......@@ -159,13 +217,44 @@ int exynos_drm_device_unregister(struct drm_device *dev)
list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) {
exynos_drm_subdrv_remove(dev, subdrv);
exynos_drm_destroy_enc_conn(subdrv);
}
return 0;
}
EXPORT_SYMBOL_GPL(exynos_drm_device_unregister);
int exynos_drm_manager_register(struct exynos_drm_manager *manager)
{
BUG_ON(!manager->ops);
list_add_tail(&manager->list, &exynos_drm_manager_list);
return 0;
}
int exynos_drm_manager_unregister(struct exynos_drm_manager *manager)
{
if (manager->ops->remove)
manager->ops->remove(manager);
list_del(&manager->list);
return 0;
}
int exynos_drm_display_register(struct exynos_drm_display *display)
{
BUG_ON(!display->ops);
list_add_tail(&display->list, &exynos_drm_display_list);
return 0;
}
int exynos_drm_display_unregister(struct exynos_drm_display *display)
{
if (display->ops->remove)
display->ops->remove(display);
list_del(&display->list);
return 0;
}
int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv)
{
if (!subdrv)
......
......@@ -33,6 +33,7 @@ enum exynos_crtc_mode {
*
* @drm_crtc: crtc object.
* @drm_plane: pointer of private plane object for this crtc
* @manager: the manager associated with this crtc
* @pipe: a crtc index created at load() with a new crtc object creation
* and the crtc object would be set to private->crtc array
* to get a crtc object corresponding to this pipe from private->crtc
......@@ -46,6 +47,7 @@ enum exynos_crtc_mode {
struct exynos_drm_crtc {
struct drm_crtc drm_crtc;
struct drm_plane *plane;
struct exynos_drm_manager *manager;
unsigned int pipe;
unsigned int dpms;
enum exynos_crtc_mode mode;
......@@ -56,6 +58,7 @@ struct exynos_drm_crtc {
static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
struct exynos_drm_manager *manager = exynos_crtc->manager;
DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode);
......@@ -71,7 +74,9 @@ static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
drm_vblank_off(crtc->dev, exynos_crtc->pipe);
}
exynos_drm_fn_encoder(crtc, &mode, exynos_drm_encoder_crtc_dpms);
if (manager->ops->dpms)
manager->ops->dpms(manager, mode);
exynos_crtc->dpms = mode;
}
......@@ -83,9 +88,15 @@ static void exynos_drm_crtc_prepare(struct drm_crtc *crtc)
static void exynos_drm_crtc_commit(struct drm_crtc *crtc)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
struct exynos_drm_manager *manager = exynos_crtc->manager;
exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
exynos_plane_commit(exynos_crtc->plane);
if (manager->ops->commit)
manager->ops->commit(manager);
exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_ON);
}
......@@ -107,7 +118,6 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
struct drm_plane *plane = exynos_crtc->plane;
unsigned int crtc_w;
unsigned int crtc_h;
int pipe = exynos_crtc->pipe;
int ret;
/*
......@@ -127,8 +137,6 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
plane->crtc = crtc;
plane->fb = crtc->fb;
exynos_drm_fn_encoder(crtc, &pipe, exynos_drm_encoder_crtc_pipe);
return 0;
}
......@@ -318,21 +326,24 @@ static void exynos_drm_crtc_attach_mode_property(struct drm_crtc *crtc)
drm_object_attach_property(&crtc->base, prop, 0);
}
int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr)
int exynos_drm_crtc_create(struct exynos_drm_manager *manager)
{
struct exynos_drm_crtc *exynos_crtc;
struct exynos_drm_private *private = dev->dev_private;
struct exynos_drm_private *private = manager->drm_dev->dev_private;
struct drm_crtc *crtc;
exynos_crtc = kzalloc(sizeof(*exynos_crtc), GFP_KERNEL);
if (!exynos_crtc)
return -ENOMEM;
exynos_crtc->pipe = nr;
exynos_crtc->dpms = DRM_MODE_DPMS_OFF;
init_waitqueue_head(&exynos_crtc->pending_flip_queue);
atomic_set(&exynos_crtc->pending_flip, 0);
exynos_crtc->plane = exynos_plane_init(dev, 1 << nr, true);
exynos_crtc->dpms = DRM_MODE_DPMS_OFF;
exynos_crtc->manager = manager;
exynos_crtc->pipe = manager->pipe;
exynos_crtc->plane = exynos_plane_init(manager->drm_dev,
1 << manager->pipe, true);
if (!exynos_crtc->plane) {
kfree(exynos_crtc);
return -ENOMEM;
......@@ -340,9 +351,9 @@ int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr)
crtc = &exynos_crtc->drm_crtc;
private->crtc[nr] = crtc;
private->crtc[manager->pipe] = crtc;
drm_crtc_init(dev, crtc, &exynos_crtc_funcs);
drm_crtc_init(manager->drm_dev, crtc, &exynos_crtc_funcs);
drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs);
exynos_drm_crtc_attach_mode_property(crtc);
......@@ -350,39 +361,41 @@ int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr)
return 0;
}
int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc)
int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe)
{
struct exynos_drm_private *private = dev->dev_private;
struct exynos_drm_crtc *exynos_crtc =
to_exynos_crtc(private->crtc[crtc]);
to_exynos_crtc(private->crtc[pipe]);
struct exynos_drm_manager *manager = exynos_crtc->manager;
if (exynos_crtc->dpms != DRM_MODE_DPMS_ON)
return -EPERM;
exynos_drm_fn_encoder(private->crtc[crtc], &crtc,
exynos_drm_enable_vblank);
if (manager->ops->enable_vblank)
manager->ops->enable_vblank(manager);
return 0;
}
void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc)
void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe)
{
struct exynos_drm_private *private = dev->dev_private;
struct exynos_drm_crtc *exynos_crtc =
to_exynos_crtc(private->crtc[crtc]);
to_exynos_crtc(private->crtc[pipe]);
struct exynos_drm_manager *manager = exynos_crtc->manager;
if (exynos_crtc->dpms != DRM_MODE_DPMS_ON)
return;
exynos_drm_fn_encoder(private->crtc[crtc], &crtc,
exynos_drm_disable_vblank);
if (manager->ops->disable_vblank)
manager->ops->disable_vblank(manager);
}
void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int crtc)
void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe)
{
struct exynos_drm_private *dev_priv = dev->dev_private;
struct drm_pending_vblank_event *e, *t;
struct drm_crtc *drm_crtc = dev_priv->crtc[crtc];
struct drm_crtc *drm_crtc = dev_priv->crtc[pipe];
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(drm_crtc);
unsigned long flags;
......@@ -391,15 +404,71 @@ void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int crtc)
list_for_each_entry_safe(e, t, &dev_priv->pageflip_event_list,
base.link) {
/* if event's pipe isn't same as crtc then ignore it. */
if (crtc != e->pipe)
if (pipe != e->pipe)
continue;
list_del(&e->base.link);
drm_send_vblank_event(dev, -1, e);
drm_vblank_put(dev, crtc);
drm_vblank_put(dev, pipe);
atomic_set(&exynos_crtc->pending_flip, 0);
wake_up(&exynos_crtc->pending_flip_queue);
}
spin_unlock_irqrestore(&dev->event_lock, flags);
}
void exynos_drm_crtc_plane_mode_set(struct drm_crtc *crtc,
struct exynos_drm_overlay *overlay)
{
struct exynos_drm_manager *manager = to_exynos_crtc(crtc)->manager;
if (manager->ops->win_mode_set)
manager->ops->win_mode_set(manager, overlay);
}
void exynos_drm_crtc_plane_commit(struct drm_crtc *crtc, int zpos)
{
struct exynos_drm_manager *manager = to_exynos_crtc(crtc)->manager;
if (manager->ops->win_commit)
manager->ops->win_commit(manager, zpos);
}
void exynos_drm_crtc_plane_enable(struct drm_crtc *crtc, int zpos)
{
struct exynos_drm_manager *manager = to_exynos_crtc(crtc)->manager;
if (manager->ops->win_enable)
manager->ops->win_enable(manager, zpos);
}
void exynos_drm_crtc_plane_disable(struct drm_crtc *crtc, int zpos)
{
struct exynos_drm_manager *manager = to_exynos_crtc(crtc)->manager;
if (manager->ops->win_disable)
manager->ops->win_disable(manager, zpos);
}
void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb)
{
struct exynos_drm_manager *manager;
struct drm_device *dev = fb->dev;
struct drm_crtc *crtc;
/*
* make sure that overlay data are updated to real hardware
* for all encoders.
*/
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
manager = to_exynos_crtc(crtc)->manager;
/*
* wait for vblank interrupt
* - this makes sure that overlay data are updated to
* real hardware.
*/
if (manager->ops->wait_for_vblank)
manager->ops->wait_for_vblank(manager);
}
}
......@@ -15,9 +15,21 @@
#ifndef _EXYNOS_DRM_CRTC_H_
#define _EXYNOS_DRM_CRTC_H_
int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr);
int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc);
void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc);
void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int crtc);
struct drm_device;
struct drm_crtc;
struct exynos_drm_manager;
struct exynos_drm_overlay;
int exynos_drm_crtc_create(struct exynos_drm_manager *manager);
int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe);
void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe);
void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe);
void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb);
void exynos_drm_crtc_plane_mode_set(struct drm_crtc *crtc,
struct exynos_drm_overlay *overlay);
void exynos_drm_crtc_plane_commit(struct drm_crtc *crtc, int zpos);
void exynos_drm_crtc_plane_enable(struct drm_crtc *crtc, int zpos);
void exynos_drm_crtc_plane_disable(struct drm_crtc *crtc, int zpos);
#endif
......@@ -74,15 +74,9 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
exynos_drm_mode_config_init(dev);
/*
* EXYNOS4 is enough to have two CRTCs and each crtc would be used
* without dependency of hardware.
*/
for (nr = 0; nr < MAX_CRTC; nr++) {
ret = exynos_drm_crtc_create(dev, nr);
if (ret)
goto err_release_iommu_mapping;
}
ret = exynos_drm_initialize_managers(dev);
if (ret)
goto err_mode_config_cleanup;
for (nr = 0; nr < MAX_PLANE; nr++) {
struct drm_plane *plane;
......@@ -90,12 +84,16 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
plane = exynos_plane_init(dev, possible_crtcs, false);
if (!plane)
goto err_release_iommu_mapping;
goto err_manager_cleanup;
}
ret = exynos_drm_initialize_displays(dev);
if (ret)
goto err_manager_cleanup;
ret = drm_vblank_init(dev, MAX_CRTC);
if (ret)
goto err_release_iommu_mapping;
goto err_display_cleanup;
/*
* probe sub drivers such as display controller and hdmi driver,
......@@ -129,7 +127,12 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
exynos_drm_device_unregister(dev);
err_vblank:
drm_vblank_cleanup(dev);
err_release_iommu_mapping:
err_display_cleanup:
exynos_drm_remove_displays(dev);
err_manager_cleanup:
exynos_drm_remove_managers(dev);
err_mode_config_cleanup:
drm_mode_config_cleanup(dev);
drm_release_iommu_mapping(dev);
err_crtc:
drm_mode_config_cleanup(dev);
......@@ -144,6 +147,8 @@ static int exynos_drm_unload(struct drm_device *dev)
exynos_drm_device_unregister(dev);
drm_vblank_cleanup(dev);
drm_kms_helper_poll_fini(dev);
exynos_drm_remove_displays(dev);
exynos_drm_remove_managers(dev);
drm_mode_config_cleanup(dev);
drm_release_iommu_mapping(dev);
......
......@@ -122,34 +122,68 @@ struct exynos_drm_overlay {
* Exynos DRM Display Structure.
* - this structure is common to analog tv, digital tv and lcd panel.
*
* @type: one of EXYNOS_DISPLAY_TYPE_LCD and HDMI.
* @initialize: initializes the display with drm_dev
* @remove: cleans up the display for removal
* @is_connected: check for that display is connected or not.
* @get_max_resol: get maximum resolution to specific hardware.
* @get_edid: get edid modes from display driver.
* @get_panel: get panel object from display driver.
* @mode_fixup: fix mode data comparing to hw specific display mode.
* @mode_set: convert drm_display_mode to hw specific display mode and
* would be called by encoder->mode_set().
* @check_mode: check if mode is valid or not.
* @dpms: display device on or off.
* @commit: apply changes to hw
*/
struct exynos_drm_display;
struct exynos_drm_display_ops {
int (*initialize)(struct exynos_drm_display *display,
struct drm_device *drm_dev);
void (*remove)(struct exynos_drm_display *display);
bool (*is_connected)(struct exynos_drm_display *display);
void (*get_max_resol)(struct exynos_drm_display *display,
unsigned int *width,
unsigned int *height);
struct edid *(*get_edid)(struct exynos_drm_display *display,
struct drm_connector *connector);
void *(*get_panel)(struct exynos_drm_display *display);
void (*mode_fixup)(struct exynos_drm_display *display,
struct drm_connector *connector,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
void (*mode_set)(struct exynos_drm_display *display,
struct drm_display_mode *mode);
int (*check_mode)(struct exynos_drm_display *display,
struct drm_display_mode *mode);
void (*dpms)(struct exynos_drm_display *display, int mode);
void (*commit)(struct exynos_drm_display *display);
};
/*
* Exynos drm display structure, maps 1:1 with an encoder/connector
*
* @list: the list entry for this manager
* @type: one of EXYNOS_DISPLAY_TYPE_LCD and HDMI.
* @encoder: encoder object this display maps to
* @connector: connector object this display maps to
* @ops: pointer to callbacks for exynos drm specific functionality
* @ctx: A pointer to the display's implementation specific context
*/
struct exynos_drm_display {
struct list_head list;
enum exynos_drm_output_type type;
int (*initialize)(struct device *dev, struct drm_device *drm_dev);
bool (*is_connected)(struct device *dev);
struct edid *(*get_edid)(struct device *dev,
struct drm_connector *connector);
void *(*get_panel)(struct device *dev);
int (*check_mode)(struct device *dev, struct drm_display_mode *mode);
int (*dpms)(struct device *dev, int mode);
struct drm_encoder *encoder;
struct drm_connector *connector;
struct exynos_drm_display_ops *ops;
void *ctx;
};
/*
* Exynos drm manager ops
*
* @initialize: initializes the manager with drm_dev
* @remove: cleans up the manager for removal
* @dpms: control device power.
* @mode_fixup: fix mode data comparing to hw specific display mode.
* @mode_set: convert drm_display_mode to hw specific display mode and
* would be called by encoder->mode_set().
* @get_max_resol: get maximum resolution to specific hardware.
* @commit: set current hw specific display mode to hw.
* @enable_vblank: specific driver callback for enabling vblank interrupt.
* @disable_vblank: specific driver callback for disabling vblank interrupt.
......@@ -163,15 +197,9 @@ struct exynos_drm_display_ops {
struct exynos_drm_manager;
struct exynos_drm_manager_ops {
int (*initialize)(struct exynos_drm_manager *mgr,
struct drm_device *drm_dev);
struct drm_device *drm_dev, int pipe);
void (*remove)(struct exynos_drm_manager *mgr);
void (*dpms)(struct exynos_drm_manager *mgr, int mode);
void (*mode_fixup)(struct exynos_drm_manager *mgr,
struct drm_connector *connector,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
void (*mode_set)(struct exynos_drm_manager *mgr, void *mode);
void (*get_max_resol)(struct exynos_drm_manager *mgr,
unsigned int *width, unsigned int *height);
void (*commit)(struct exynos_drm_manager *mgr);
int (*enable_vblank)(struct exynos_drm_manager *mgr);
void (*disable_vblank)(struct exynos_drm_manager *mgr);
......@@ -184,25 +212,21 @@ struct exynos_drm_manager_ops {
};
/*
* Exynos drm common manager structure.
* Exynos drm common manager structure, maps 1:1 with a crtc
*
* @dev: pointer to device object for subdrv device driver.
* sub drivers such as display controller or hdmi driver,
* have their own device object.
* @ops: pointer to callbacks for exynos drm specific framebuffer.
* these callbacks should be set by specific drivers such fimd
* or hdmi driver and are used to control hardware global registers.
* @display: pointer to callbacks for exynos drm specific framebuffer.
* these callbacks should be set by specific drivers such fimd
* or hdmi driver and are used to control display devices such as
* analog tv, digital tv and lcd panel and also get timing data for them.
* @list: the list entry for this manager
* @type: one of EXYNOS_DISPLAY_TYPE_LCD and HDMI.
* @drm_dev: pointer to the drm device
* @pipe: the pipe number for this crtc/manager
* @ops: pointer to callbacks for exynos drm specific functionality
* @ctx: A pointer to the manager's implementation specific context
*/
struct exynos_drm_manager {
struct device *dev;
struct list_head list;
enum exynos_drm_output_type type;
struct drm_device *drm_dev;
int pipe;
struct exynos_drm_manager_ops *ops;
struct exynos_drm_display_ops *display_ops;
void *ctx;
};
......@@ -268,14 +292,11 @@ struct exynos_drm_private {
* by probe callback.
* @open: this would be called with drm device file open.
* @close: this would be called with drm device file close.
* @encoder: encoder object owned by this sub driver.
* @connector: connector object owned by this sub driver.
*/
struct exynos_drm_subdrv {
struct list_head list;
struct device *dev;
struct drm_device *drm_dev;
struct exynos_drm_manager *manager;
int (*probe)(struct drm_device *drm_dev, struct device *dev);
void (*remove)(struct drm_device *drm_dev, struct device *dev);
......@@ -283,9 +304,6 @@ struct exynos_drm_subdrv {
struct drm_file *file);
void (*close)(struct drm_device *drm_dev, struct device *dev,
struct drm_file *file);
struct drm_encoder *encoder;
struct drm_connector *connector;
};
/*
......@@ -300,6 +318,16 @@ int exynos_drm_device_register(struct drm_device *dev);
*/
int exynos_drm_device_unregister(struct drm_device *dev);
int exynos_drm_initialize_managers(struct drm_device *dev);
void exynos_drm_remove_managers(struct drm_device *dev);
int exynos_drm_initialize_displays(struct drm_device *dev);
void exynos_drm_remove_displays(struct drm_device *dev);
int exynos_drm_manager_register(struct exynos_drm_manager *manager);
int exynos_drm_manager_unregister(struct exynos_drm_manager *manager);
int exynos_drm_display_register(struct exynos_drm_display *display);
int exynos_drm_display_unregister(struct exynos_drm_display *display);
/*
* this function would be called by sub drivers such as display controller
* or hdmi driver to register this sub driver object to exynos drm driver
......
This diff is collapsed.
......@@ -18,20 +18,8 @@ struct exynos_drm_manager;
void exynos_drm_encoder_setup(struct drm_device *dev);
struct drm_encoder *exynos_drm_encoder_create(struct drm_device *dev,
struct exynos_drm_manager *mgr,
unsigned long possible_crtcs);
struct exynos_drm_manager *
exynos_drm_get_manager(struct drm_encoder *encoder);
void exynos_drm_fn_encoder(struct drm_crtc *crtc, void *data,
void (*fn)(struct drm_encoder *, void *));
void exynos_drm_enable_vblank(struct drm_encoder *encoder, void *data);
void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data);
void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data);
void exynos_drm_encoder_crtc_pipe(struct drm_encoder *encoder, void *data);
void exynos_drm_encoder_plane_mode_set(struct drm_encoder *encoder, void *data);
void exynos_drm_encoder_plane_commit(struct drm_encoder *encoder, void *data);
void exynos_drm_encoder_plane_enable(struct drm_encoder *encoder, void *data);
void exynos_drm_encoder_plane_disable(struct drm_encoder *encoder, void *data);
void exynos_drm_encoder_complete_scanout(struct drm_framebuffer *fb);
struct exynos_drm_display *mgr,
unsigned long possible_crtcs);
struct exynos_drm_display *exynos_drm_get_display(struct drm_encoder *encoder);
#endif
......@@ -22,7 +22,7 @@
#include "exynos_drm_fb.h"
#include "exynos_drm_gem.h"
#include "exynos_drm_iommu.h"
#include "exynos_drm_encoder.h"
#include "exynos_drm_crtc.h"
#define to_exynos_fb(x) container_of(x, struct exynos_drm_fb, fb)
......@@ -71,7 +71,7 @@ static void exynos_drm_fb_destroy(struct drm_framebuffer *fb)
unsigned int i;
/* make sure that overlay data are updated before relesing fb. */
exynos_drm_encoder_complete_scanout(fb);
exynos_drm_crtc_complete_scanout(fb);
drm_framebuffer_cleanup(fb);
......
This diff is collapsed.
This diff is collapsed.
......@@ -19,10 +19,12 @@
* 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;
};
......
......@@ -13,7 +13,7 @@
#include <drm/exynos_drm.h>
#include "exynos_drm_drv.h"
#include "exynos_drm_encoder.h"
#include "exynos_drm_crtc.h"
#include "exynos_drm_fb.h"
#include "exynos_drm_gem.h"
#include "exynos_drm_plane.h"
......@@ -139,7 +139,7 @@ int exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
overlay->crtc_x, overlay->crtc_y,
overlay->crtc_width, overlay->crtc_height);
exynos_drm_fn_encoder(crtc, overlay, exynos_drm_encoder_plane_mode_set);
exynos_drm_crtc_plane_mode_set(crtc, overlay);
return 0;
}
......@@ -149,8 +149,7 @@ void exynos_plane_commit(struct drm_plane *plane)
struct exynos_plane *exynos_plane = to_exynos_plane(plane);
struct exynos_drm_overlay *overlay = &exynos_plane->overlay;
exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
exynos_drm_encoder_plane_commit);
exynos_drm_crtc_plane_commit(plane->crtc, overlay->zpos);
}
void exynos_plane_dpms(struct drm_plane *plane, int mode)
......@@ -162,17 +161,13 @@ void exynos_plane_dpms(struct drm_plane *plane, int mode)
if (exynos_plane->enabled)
return;
exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
exynos_drm_encoder_plane_enable);
exynos_drm_crtc_plane_enable(plane->crtc, overlay->zpos);
exynos_plane->enabled = true;
} else {
if (!exynos_plane->enabled)
return;
exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
exynos_drm_encoder_plane_disable);
exynos_drm_crtc_plane_disable(plane->crtc, overlay->zpos);
exynos_plane->enabled = false;
}
}
......
......@@ -45,7 +45,7 @@ struct vidi_win_data {
};
struct vidi_context {
struct exynos_drm_subdrv subdrv;
struct drm_device *drm_dev;
struct drm_crtc *crtc;
struct vidi_win_data win_data[WINDOWS_NR];
struct edid *raw_edid;
......@@ -58,6 +58,7 @@ struct vidi_context {
bool direct_vblank;
struct work_struct work;
struct mutex lock;
int pipe;
};
static const char fake_edid_info[] = {
......@@ -85,10 +86,9 @@ static const char fake_edid_info[] = {
0x00, 0x00, 0x00, 0x06
};
static bool vidi_display_is_connected(struct device *dev)
static bool vidi_display_is_connected(struct exynos_drm_display *display)
{
struct exynos_drm_manager *mgr = get_vidi_mgr(dev);
struct vidi_context *ctx = mgr->ctx;
struct vidi_context *ctx = display->ctx;
/*
* connection request would come from user side
......@@ -97,11 +97,10 @@ static bool vidi_display_is_connected(struct device *dev)
return ctx->connected ? true : false;
}
static struct edid *vidi_get_edid(struct device *dev,
static struct edid *vidi_get_edid(struct exynos_drm_display *display,
struct drm_connector *connector)
{
struct exynos_drm_manager *mgr = get_vidi_mgr(dev);
struct vidi_context *ctx = mgr->ctx;
struct vidi_context *ctx = display->ctx;
struct edid *edid;
/*
......@@ -122,14 +121,8 @@ static struct edid *vidi_get_edid(struct device *dev,
return edid;
}
static void *vidi_get_panel(struct device *dev)
{
/* TODO. */
return NULL;
}
static int vidi_check_mode(struct device *dev, struct drm_display_mode *mode)
static int vidi_check_mode(struct exynos_drm_display *display,
struct drm_display_mode *mode)
{
/* TODO. */
......@@ -137,13 +130,16 @@ static int vidi_check_mode(struct device *dev, struct drm_display_mode *mode)
}
static struct exynos_drm_display_ops vidi_display_ops = {
.type = EXYNOS_DISPLAY_TYPE_VIDI,
.is_connected = vidi_display_is_connected,
.get_edid = vidi_get_edid,
.get_panel = vidi_get_panel,
.check_mode = vidi_check_mode,
};
static struct exynos_drm_display vidi_display = {
.type = EXYNOS_DISPLAY_TYPE_VIDI,
.ops = &vidi_display_ops,
};
static void vidi_dpms(struct exynos_drm_manager *mgr, int mode)
{
struct vidi_context *ctx = mgr->ctx;
......@@ -323,7 +319,38 @@ static void vidi_win_disable(struct exynos_drm_manager *mgr, int zpos)
/* TODO. */
}
static int vidi_mgr_initialize(struct exynos_drm_manager *mgr,
struct drm_device *drm_dev, int pipe)
{
struct vidi_context *ctx = mgr->ctx;
DRM_ERROR("vidi initialize ct=%p dev=%p pipe=%d\n", ctx, drm_dev, pipe);
ctx->drm_dev = drm_dev;
ctx->pipe = pipe;
/*
* enable drm irq mode.
* - with irq_enabled = 1, we can use the vblank feature.
*
* P.S. note that we wouldn't use drm irq handler but
* just specific driver own one instead because
* drm framework supports only one irq handler.
*/
drm_dev->irq_enabled = 1;
/*
* with vblank_disable_allowed = 1, vblank interrupt will be disabled
* by drm timer once a current process gives up ownership of
* vblank event.(after drm_vblank_put function is called)
*/
drm_dev->vblank_disable_allowed = 1;
return 0;
}
static struct exynos_drm_manager_ops vidi_manager_ops = {
.initialize = vidi_mgr_initialize,
.dpms = vidi_dpms,
.commit = vidi_commit,
.enable_vblank = vidi_enable_vblank,
......@@ -334,19 +361,16 @@ static struct exynos_drm_manager_ops vidi_manager_ops = {
};
static struct exynos_drm_manager vidi_manager = {
.pipe = -1,
.ops = &vidi_manager_ops,
.display_ops = &vidi_display_ops,
.type = EXYNOS_DISPLAY_TYPE_VIDI,
.ops = &vidi_manager_ops,
};
static void vidi_fake_vblank_handler(struct work_struct *work)
{
struct vidi_context *ctx = container_of(work, struct vidi_context,
work);
struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
struct exynos_drm_manager *manager = subdrv->manager;
if (manager->pipe < 0)
if (ctx->pipe < 0)
return;
/* refresh rate is about 50Hz. */
......@@ -355,7 +379,7 @@ static void vidi_fake_vblank_handler(struct work_struct *work)
mutex_lock(&ctx->lock);
if (ctx->direct_vblank) {
drm_handle_vblank(subdrv->drm_dev, manager->pipe);
drm_handle_vblank(ctx->drm_dev, ctx->pipe);
ctx->direct_vblank = false;
mutex_unlock(&ctx->lock);
return;
......@@ -363,34 +387,7 @@ static void vidi_fake_vblank_handler(struct work_struct *work)
mutex_unlock(&ctx->lock);
exynos_drm_crtc_finish_pageflip(subdrv->drm_dev, manager->pipe);
}
static int vidi_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
{
/*
* enable drm irq mode.
* - with irq_enabled = true, we can use the vblank feature.
*
* P.S. note that we wouldn't use drm irq handler but
* just specific driver own one instead because
* drm framework supports only one irq handler.
*/
drm_dev->irq_enabled = true;
/*
* with vblank_disable_allowed = true, vblank interrupt will be disabled
* by drm timer once a current process gives up ownership of
* vblank event.(after drm_vblank_put function is called)
*/
drm_dev->vblank_disable_allowed = true;
return 0;
}
static void vidi_subdrv_remove(struct drm_device *drm_dev, struct device *dev)
{
/* TODO. */
exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
}
static int vidi_power_on(struct exynos_drm_manager *mgr, bool enable)
......@@ -460,7 +457,7 @@ static int vidi_store_connection(struct device *dev,
DRM_DEBUG_KMS("requested connection.\n");
drm_helper_hpd_irq_event(ctx->subdrv.drm_dev);
drm_helper_hpd_irq_event(ctx->drm_dev);
return len;
}
......@@ -473,8 +470,7 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,
{
struct vidi_context *ctx = NULL;
struct drm_encoder *encoder;
struct exynos_drm_manager *manager;
struct exynos_drm_display_ops *display_ops;
struct exynos_drm_display *display;
struct drm_exynos_vidi_connection *vidi = data;
if (!vidi) {
......@@ -489,11 +485,10 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,
list_for_each_entry(encoder, &drm_dev->mode_config.encoder_list,
head) {
manager = exynos_drm_get_manager(encoder);
display_ops = manager->display_ops;
display = exynos_drm_get_display(encoder);
if (display_ops->type == EXYNOS_DISPLAY_TYPE_VIDI) {
ctx = manager->ctx;
if (display->type == EXYNOS_DISPLAY_TYPE_VIDI) {
ctx = display->ctx;
break;
}
}
......@@ -532,7 +527,7 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,
}
ctx->connected = vidi->connection;
drm_helper_hpd_irq_event(ctx->subdrv.drm_dev);
drm_helper_hpd_irq_event(ctx->drm_dev);
return 0;
}
......@@ -541,7 +536,6 @@ static int vidi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct vidi_context *ctx;
struct exynos_drm_subdrv *subdrv;
int ret;
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
......@@ -553,12 +547,7 @@ static int vidi_probe(struct platform_device *pdev)
INIT_WORK(&ctx->work, vidi_fake_vblank_handler);
vidi_manager.ctx = ctx;
subdrv = &ctx->subdrv;
subdrv->dev = dev;
subdrv->manager = &vidi_manager;
subdrv->probe = vidi_subdrv_probe;
subdrv->remove = vidi_subdrv_remove;
vidi_display.ctx = ctx;
mutex_init(&ctx->lock);
......@@ -568,7 +557,8 @@ static int vidi_probe(struct platform_device *pdev)
if (ret < 0)
DRM_INFO("failed to create connection sysfs.\n");
exynos_drm_subdrv_register(subdrv);
exynos_drm_manager_register(&vidi_manager);
exynos_drm_display_register(&vidi_display);
return 0;
}
......@@ -577,7 +567,8 @@ static int vidi_remove(struct platform_device *pdev)
{
struct vidi_context *ctx = platform_get_drvdata(pdev);
exynos_drm_subdrv_unregister(&ctx->subdrv);
exynos_drm_display_unregister(&vidi_display);
exynos_drm_manager_unregister(&vidi_manager);
if (ctx->raw_edid != (struct edid *)fake_edid_info) {
kfree(ctx->raw_edid);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment