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_ddc.o exynos_hdmiphy.o \
- exynos_drm_hdmi.o
+ exynos_ddc.o exynos_hdmiphy.o
exynosdrm-$(CONFIG_DRM_EXYNOS_DP) += exynos_dp_core.o exynos_dp_reg.o
exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI) += exynos_drm_vidi.o
#include <drm/exynos_drm.h>
#include "exynos_drm_drv.h"
#include "exynos_drm_encoder.h"
+#include "exynos_drm_display.h"
#define MAX_EDID 256
#define to_exynos_connector(x) container_of(x, struct exynos_drm_connector,\
struct exynos_drm_connector {
struct drm_connector drm_connector;
uint32_t encoder_id;
- struct exynos_drm_manager *manager;
+ struct exynos_drm_display *display;
};
/* convert exynos_video_timings to drm_display_mode */
timing->vmode |= FB_VMODE_DOUBLE;
}
-static int exynos_drm_connector_get_modes(struct drm_connector *connector)
+static struct exynos_drm_display *display_from_connector(
+ 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;
- unsigned int count;
+ return to_exynos_connector(connector)->display;
+}
- DRM_DEBUG_KMS("%s\n", __FILE__);
+static int exynos_drm_connector_get_edid(struct drm_connector *connector)
+{
+ struct exynos_drm_display *display = display_from_connector(connector);
+ int ret;
+ void *edid;
- if (!display_ops) {
- DRM_DEBUG_KMS("display_ops is null.\n");
- return 0;
+ if (!display->panel_ops->get_edid)
+ return -EINVAL;
+
+ edid = kzalloc(MAX_EDID, GFP_KERNEL);
+ if (!edid) {
+ DRM_ERROR("failed to allocate edid\n");
+ return -ENOMEM;
}
- /*
- * if get_edid() exists then get_edid() callback of hdmi side
- * is called to get edid data through i2c interface else
- * get timing from the FIMD driver(display controller).
- */
- if (display_ops->get_edid) {
- int ret;
- void *edid;
-
- edid = kzalloc(MAX_EDID, GFP_KERNEL);
- if (!edid) {
- DRM_ERROR("failed to allocate edid\n");
- return 0;
- }
-
- ret = display_ops->get_edid(manager->dev, connector,
- edid, MAX_EDID);
- if (ret < 0) {
- DRM_ERROR("failed to get edid data.\n");
- kfree(edid);
- edid = NULL;
- return 0;
- }
-
- drm_mode_connector_update_edid_property(connector, edid);
- count = drm_add_edid_modes(connector, edid);
-
- kfree(connector->display_info.raw_edid);
- connector->display_info.raw_edid = edid;
- } else {
- struct drm_display_mode *mode;
- struct exynos_drm_panel_info *panel;
-
- if (display_ops->get_panel)
- panel = display_ops->get_panel(manager->dev);
+ ret = display->panel_ops->get_edid(display->panel_ctx,
+ connector, edid, MAX_EDID);
+ if (ret) {
+ DRM_ERROR("Panel operation get_edid failed %d\n", ret);
+ goto err;
+ }
+
+ ret = drm_mode_connector_update_edid_property(connector, edid);
+ if (ret) {
+ DRM_ERROR("update edid property failed(%d)\n", ret);
+ goto err;
+ }
+
+ ret = drm_add_edid_modes(connector, edid);
+ if (ret < 0) {
+ DRM_ERROR("Add edid modes failed %d\n", ret);
+ goto err;
+ }
+
+ kfree(connector->display_info.raw_edid);
+ connector->display_info.raw_edid = edid;
+
+ return ret;
+
+err:
+ kfree(edid);
+ return ret;
+}
+
+static int exynos_drm_connector_get_panel(struct drm_connector *connector)
+{
+ struct exynos_drm_display *display = display_from_connector(connector);
+ struct drm_display_mode *mode;
+ struct exynos_drm_panel_info *panel;
+ int ret;
+
+ if (!display->controller_ops->get_panel)
+ return -EINVAL;
+
+ panel = display->controller_ops->get_panel(
+ display->controller_ctx);
+
+ for (ret = 0; panel && ret < MAX_NR_PANELS; ret++) {
+ if (panel[ret].timing.xres == -1 &&
+ panel[ret].timing.yres == -1)
+ break;
+
+ mode = drm_mode_create(connector->dev);
+ mode->type = DRM_MODE_TYPE_DRIVER;
+
+ /* Only the first panel is preferred mode */
+ if (ret)
+ mode->type |= DRM_MODE_TYPE_PREFERRED;
else
- return 0;
-
- for (count = 0;count < MAX_NR_PANELS;count++) {
- if(panel[count].timing.xres == -1 && panel[count].timing.yres == -1) {
- DRM_DEBUG_KMS("panel %p count %d\n",panel,count);
- break;
- }
- mode = drm_mode_create(connector->dev);
- /* Only the first panel is preferred mode */
- mode->type = count ? DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_USERDEF:
- DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
-
- convert_to_display_mode(mode, &panel[count]);
- connector->display_info.width_mm = mode->width_mm;
- connector->display_info.height_mm = mode->height_mm;
- drm_mode_set_name(mode);
- drm_mode_probed_add(connector, mode);
- }
+ mode->type |= DRM_MODE_TYPE_USERDEF;
+ convert_to_display_mode(mode, &panel[ret]);
+ connector->display_info.width_mm = mode->width_mm;
+ connector->display_info.height_mm = mode->height_mm;
+ drm_mode_set_name(mode);
+ drm_mode_probed_add(connector, mode);
}
- return count;
+ return ret;
+}
+
+static int exynos_drm_connector_get_modes(struct drm_connector *connector)
+{
+ int ret;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /*
+ * First try getting modes from EDID. If that doesn't yield any results,
+ * fall back to the panel call.
+ */
+ ret = exynos_drm_connector_get_edid(connector);
+ if (ret > 0)
+ return ret;
+
+ return exynos_drm_connector_get_panel(connector);
}
static int exynos_drm_connector_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
- 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 = display_from_connector(connector);
struct fb_videomode timing;
int ret = MODE_BAD;
convert_to_video_timing(&timing, mode);
- if (display_ops && display_ops->check_timing)
- if (!display_ops->check_timing(manager->dev, (void *)&timing))
- ret = MODE_OK;
+ if (!display->panel_ops->check_timing)
+ return ret;
+
+ if (!display->panel_ops->check_timing(display->panel_ctx, &timing))
+ ret = MODE_OK;
return ret;
}
static int exynos_drm_connector_fill_modes(struct drm_connector *connector,
unsigned int max_width, unsigned int max_height)
{
- 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 = display_from_connector(connector);
unsigned int width, height;
width = max_width;
height = max_height;
/*
- * if specific driver want to find desired_mode using maxmum
+ * If the specific driver wants to find desired_mode using maximum
* resolution then get max width and height from that driver.
*/
- if (ops && ops->get_max_resol)
- ops->get_max_resol(manager->dev, &width, &height);
+ if (display->panel_ops->get_max_res)
+ display->panel_ops->get_max_res(display->panel_ctx, &width,
+ &height);
return drm_helper_probe_single_connector_modes(connector, width,
height);
static enum drm_connector_status
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;
- enum drm_connector_status status = connector_status_disconnected;
+ struct exynos_drm_display *display = display_from_connector(connector);
+ enum drm_connector_status status;
DRM_DEBUG_KMS("%s\n", __FILE__);
- if (display_ops && display_ops->is_connected) {
- if (display_ops->is_connected(manager->dev))
- status = connector_status_connected;
- else
- status = connector_status_disconnected;
- }
+ if (display->panel_ops->is_connected &&
+ display->panel_ops->is_connected(display->panel_ctx))
+ status = connector_status_connected;
+ else
+ status = connector_status_disconnected;
return status;
}
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;
connector = &exynos_connector->drm_connector;
- switch (manager->display_ops->type) {
- case EXYNOS_DISPLAY_TYPE_HDMI:
+ switch (display->display_type) {
+ case EXYNOS_DRM_DISPLAY_TYPE_MIXER:
type = DRM_MODE_CONNECTOR_HDMIA;
connector->interlace_allowed = true;
connector->polled = DRM_CONNECTOR_POLL_HPD;
break;
- case EXYNOS_DISPLAY_TYPE_VIDI:
+ case EXYNOS_DRM_DISPLAY_TYPE_VIDI:
type = DRM_MODE_CONNECTOR_VIRTUAL;
connector->polled = DRM_CONNECTOR_POLL_HPD;
break;
- case EXYNOS_DISPLAY_TYPE_LCD:
+ case EXYNOS_DRM_DISPLAY_TYPE_FIMD:
type = DRM_MODE_CONNECTOR_eDP;
break;
default:
goto err_connector;
exynos_connector->encoder_id = encoder->base.id;
- exynos_connector->manager = manager;
+ exynos_connector->display = display;
connector->encoder = encoder;
err = drm_mode_connector_attach_encoder(connector, encoder);
#include "exynos_drm_encoder.h"
#include "exynos_drm_connector.h"
#include "exynos_drm_fbdev.h"
+#include "exynos_drm_display.h"
static LIST_HEAD(exynos_drm_subdrv_list);
static struct drm_device *drm_dev;
*
* P.S. note that this driver is considered for modularization.
*/
- ret = subdrv->probe(dev, subdrv->dev);
+ ret = subdrv->probe(dev, subdrv);
if (ret)
return ret;
}
- if (!subdrv->manager)
+ if (!subdrv->display)
return 0;
- subdrv->manager->dev = subdrv->dev;
-
/* create and initialize a encoder for this sub driver. */
- encoder = exynos_drm_encoder_create(dev, subdrv->manager,
+ encoder = exynos_drm_encoder_create(dev, subdrv->display,
(1 << MAX_CRTC) - 1);
if (!encoder) {
DRM_ERROR("failed to create encoder\n");
if (exynos_crtc->dpms != DRM_MODE_DPMS_ON) {
int mode = DRM_MODE_DPMS_ON;
+ /*
+ * TODO(seanpaul): This has the nasty habit of calling the
+ * underlying dpms/power callbacks twice on boot. This code
+ * needs to be cleaned up so this doesn't happen.
+ */
+
/*
* enable hardware(power on) to all encoders hdmi connected
* to current crtc.
--- /dev/null
+/*
+ * Copyright (C) 2012 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_DRM_PANEL_H_
+#define _EXYNOS_DRM_PANEL_H_
+
+/*
+ * Callbacks used to manipulate the panel (DP/HDMI/MIPI)
+ *
+ * @subdrv_probe: Used to associate drm_dev with panel context
+ * @is_connected: Returns true if the panel is connected
+ * @get_edid: Fills in edid with mode data from the panel
+ * @check_timing: Returns 0 if the given timing is valid for the panel
+ * @power: Sets the panel's power to mode
+ * @dpms: Same as power, but called in different places. Best to avoid it
+ * @mode_fixup: Copies and optionally alters mode to adjusted_mode
+ * @mode_set: Sets the panel to output mode
+ * @commit: Commits changes to the panel from mode_set
+ * @apply: Same as commit in most cases
+ * @get_max_res: Returns the maximum resolution in width/height
+ */
+struct exynos_panel_ops {
+ int (*subdrv_probe)(void *ctx, struct drm_device *drm_dev);
+ bool (*is_connected)(void *ctx);
+ int (*get_edid)(void *ctx, struct drm_connector *connector,
+ u8 *edid, int len);
+ int (*check_timing)(void *ctx, void *timing);
+ int (*power)(void *ctx, int mode);
+ int (*dpms)(void *ctx, int mode);
+ void (*mode_fixup)(void *ctx, struct drm_connector *connector,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode);
+ void (*mode_set)(void *ctx, void *mode);
+ void (*commit)(void *ctx);
+ void (*apply)(void *ctx);
+ void (*get_max_res)(void *ctx, unsigned int *width,
+ unsigned int *height);
+};
+
+/*
+ * Callbacks used to manipulate the controller (FIMD/Mixer)
+ *
+ * @subdrv_probe: Used to associate drm_dev with the controller context
+ * @get_panel: If we're not using edid, return the panel info
+ * @enable_vblank: Enable the controller's vblank interrupt and set pipe
+ * @disable_vblank: Disable the controller's vblank interrupt
+ * @power: Sets the controller's power to mode
+ * @dpms: Same as power, but called in different places. Best to avoid it
+ * @mode_set: Sets the controller to output mode
+ * @page_flip: Updates the controller's dma pointer
+ * @commit: Applies controller level settings (as opposed to window level)
+ * @apply: Commits the changes on all of the controller's windows
+ * @win_commit: Commits the changes on only one window
+ * @win_disable: Disables one of the controller's windows
+ */
+struct exynos_controller_ops {
+ int (*subdrv_probe)(void *ctx, struct drm_device *drm_dev);
+ struct exynos_drm_panel_info *(*get_panel)(void *ctx);
+ int (*enable_vblank)(void *ctx, int pipe);
+ void (*disable_vblank)(void *ctx);
+ int (*power)(void *ctx, int mode);
+ int (*dpms)(void *ctx, int mode);
+ void (*mode_set)(void *ctx, struct exynos_drm_overlay *overlay);
+ void (*page_flip)(void *ctx, struct exynos_drm_overlay *overlay);
+ void (*commit)(void *ctx);
+ void (*apply)(void *ctx);
+ void (*win_commit)(void *ctx, int zpos);
+ void (*win_disable)(void *ctx, int zpos);
+};
+
+enum exynos_drm_display_type {
+ EXYNOS_DRM_DISPLAY_TYPE_FIMD,
+ EXYNOS_DRM_DISPLAY_TYPE_MIXER,
+ EXYNOS_DRM_DISPLAY_TYPE_VIDI,
+ EXYNOS_DRM_DISPLAY_NUM_DISPLAYS,
+};
+
+/*
+ * The various bits we need to keep track of to manipulate the hardware from
+ * the connector/encoder/crtc drm callbacks.
+ *
+ * @display_type: The type of display, depends on which CONFIGs are enabled
+ * @subdrv: The exynos drm sub driver pointer
+ * @panel_ops: The panel callbacks to use
+ * @controller_ops: The controller callbacks to use
+ * @panel_ctx: The context pointer to pass to panel callbacks
+ * @controller_ctx: The context pointer to pass to controller callbacks
+ * @pipe: The current pipe number for this display
+ */
+struct exynos_drm_display {
+ enum exynos_drm_display_type display_type;
+ struct exynos_drm_subdrv *subdrv;
+ struct exynos_panel_ops *panel_ops;
+ struct exynos_controller_ops *controller_ops;
+ void *panel_ctx;
+ void *controller_ctx;
+ int pipe;
+};
+
+/*
+ * Used by the hardware drivers to attach panel and controller callbacks and
+ * contexts to a display.
+ */
+void exynos_display_attach_panel(enum exynos_drm_display_type type,
+ struct exynos_panel_ops *ops, void *ctx);
+void exynos_display_attach_controller(enum exynos_drm_display_type type,
+ struct exynos_controller_ops *ops, void *ctx);
+
+/* Initializes the given display to type */
+int exynos_display_init(struct exynos_drm_display *display,
+ enum exynos_drm_display_type type);
+
+/* Cleans up the given display */
+void exynos_display_remove(struct exynos_drm_display *display);
+
+#endif
#include "exynos_drm_plane.h"
#include "exynos_drm_vidi.h"
#include "exynos_drm_dmabuf.h"
+#include "exynos_drm_display.h"
#define DRIVER_NAME "exynos"
#define DRIVER_DESC "Samsung SoC DRM"
},
};
-static int __init exynos_drm_init(void)
+/* TODO (seanpaul): Once we remove platform drivers, we'll be calling the
+ * various panel/controller init functions directly. These init functions will
+ * return to us the ops and context, so we can get rid of these attach
+ * functions. Once the attach functions are gone, we can move this array of
+ * display pointers into the drm device's platform data.
+ *
+ * For now, we'll use a global to keep track of things.
+ */
+static struct exynos_drm_display *displays[EXYNOS_DRM_DISPLAY_NUM_DISPLAYS];
+
+void exynos_display_attach_panel(enum exynos_drm_display_type type,
+ struct exynos_panel_ops *ops, void *ctx)
{
+ int i;
+ for (i = 0; i < EXYNOS_DRM_DISPLAY_NUM_DISPLAYS; i++) {
+ if (displays[i]->display_type == type) {
+ displays[i]->panel_ctx = ctx;
+ displays[i]->panel_ops = ops;
+ return;
+ }
+ }
+}
+
+void exynos_display_attach_controller(enum exynos_drm_display_type type,
+ struct exynos_controller_ops *ops, void *ctx)
+{
+ int i;
+ for (i = 0; i < EXYNOS_DRM_DISPLAY_NUM_DISPLAYS; i++) {
+ if (displays[i]->display_type == type) {
+ displays[i]->controller_ctx = ctx;
+ displays[i]->controller_ops = ops;
+ return;
+ }
+ }
+}
+
+static int display_subdrv_probe(struct drm_device *drm_dev,
+ struct exynos_drm_subdrv *subdrv)
+{
+ struct exynos_drm_display *display = subdrv->display;
int ret;
+ if (!display->controller_ops || !display->panel_ops)
+ return -EINVAL;
+
+ if (display->controller_ops->subdrv_probe) {
+ ret = display->controller_ops->subdrv_probe(
+ display->controller_ctx, drm_dev);
+ if (ret)
+ return ret;
+ }
+
+ if (display->panel_ops->subdrv_probe) {
+ ret = display->panel_ops->subdrv_probe(display->panel_ctx,
+ drm_dev);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int exynos_display_init(struct exynos_drm_display *display,
+ enum exynos_drm_display_type type)
+{
+ struct exynos_drm_subdrv *subdrv;
+
+ subdrv = kzalloc(sizeof(*subdrv), GFP_KERNEL);
+ if (!subdrv) {
+ DRM_ERROR("Failed to allocate display subdrv\n");
+ return -ENOMEM;
+ }
+
+ display->display_type = type;
+ display->pipe = -1;
+ display->subdrv = subdrv;
+
+ subdrv->probe = display_subdrv_probe;
+ subdrv->display = display;
+ exynos_drm_subdrv_register(subdrv);
+
+ return 0;
+}
+
+void exynos_display_remove(struct exynos_drm_display *display)
+{
+ if (display->subdrv) {
+ exynos_drm_subdrv_unregister(display->subdrv);
+ kfree(display->subdrv);
+ }
+}
+
+static int __init exynos_drm_init(void)
+{
+ int ret, i;
+
DRM_DEBUG_DRIVER("%s\n", __FILE__);
+ for (i = 0; i < EXYNOS_DRM_DISPLAY_NUM_DISPLAYS; i++) {
+ displays[i] = kzalloc(sizeof(*displays[i]), GFP_KERNEL);
+ if (!displays[i]) {
+ ret = -ENOMEM;
+ goto out_display;
+ }
+
+ ret = exynos_display_init(displays[i], i);
+ if (ret)
+ goto out_display;
+ }
+
#ifdef CONFIG_DRM_EXYNOS_FIMD
ret = platform_driver_register(&fimd_driver);
if (ret < 0)
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;
#endif
#ifdef CONFIG_DRM_EXYNOS_VIDI
#endif
#ifdef CONFIG_DRM_EXYNOS_HDMI
- platform_driver_unregister(&exynos_drm_common_hdmi_driver);
-out_common_hdmi:
platform_driver_unregister(&mixer_driver);
out_mixer:
platform_driver_unregister(&hdmi_driver);
platform_driver_unregister(&fimd_driver);
out_fimd:
#endif
+out_display:
+ for (i = 0; i < EXYNOS_DRM_DISPLAY_NUM_DISPLAYS; i++) {
+ if (!displays[i])
+ continue;
+
+ exynos_display_remove(displays[i]);
+ kfree(displays[i]);
+ }
return ret;
}
static void __exit exynos_drm_exit(void)
{
+ int i;
+
DRM_DEBUG_DRIVER("%s\n", __FILE__);
platform_driver_unregister(&exynos_drm_platform_driver);
#ifdef CONFIG_DRM_EXYNOS_HDMI
- platform_driver_unregister(&exynos_drm_common_hdmi_driver);
platform_driver_unregister(&mixer_driver);
platform_driver_unregister(&hdmi_driver);
#endif
#ifdef CONFIG_DRM_EXYNOS_FIMD
platform_driver_unregister(&fimd_driver);
#endif
+
+ for (i = 0; i < EXYNOS_DRM_DISPLAY_NUM_DISPLAYS; i++) {
+ if (!displays[i])
+ continue;
+
+ exynos_display_remove(displays[i]);
+ kfree(displays[i]);
+ }
}
module_init(exynos_drm_init);
struct exynos_drm_overlay;
struct drm_connector;
struct exynos_drm_gem_object;
+struct exynos_drm_display;
extern unsigned int drm_vblank_offdelay;
-/* this enumerates display type. */
-enum exynos_drm_output_type {
- EXYNOS_DISPLAY_TYPE_NONE,
- /* RGB or CPU Interface. */
- EXYNOS_DISPLAY_TYPE_LCD,
- /* HDMI Interface. */
- EXYNOS_DISPLAY_TYPE_HDMI,
- /* Virtual Display Interface. */
- EXYNOS_DISPLAY_TYPE_VIDI,
-};
-
-/*
- * Exynos drm overlay ops structure.
- *
- * @mode_set: copy drm overlay info to hw specific overlay info.
- * @commit: apply hardware specific overlay data to registers.
- * @disable: disable hardware specific overlay.
- */
-struct exynos_drm_overlay_ops {
- void (*mode_set)(struct device *subdrv_dev,
- struct exynos_drm_overlay *overlay);
- void (*page_flip)(struct device *subdrv_dev,
- struct exynos_drm_overlay *overlay);
- void (*commit)(struct device *subdrv_dev, int zpos);
- void (*disable)(struct device *subdrv_dev, int zpos);
-};
-
/*
* Exynos drm common overlay structure.
*
bool activated;
};
-/*
- * 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.
- * @is_connected: check for that display is connected or not.
- * @get_edid: get edid modes from display driver.
- * @get_panel: get panel object from display driver.
- * @check_timing: check if timing is valid or not.
- * @power_on: display device on or off.
- */
-struct exynos_drm_display_ops {
- enum exynos_drm_output_type type;
- bool (*is_connected)(struct device *dev);
- int (*get_edid)(struct device *dev, struct drm_connector *connector,
- u8 *edid, int len);
- void *(*get_panel)(struct device *dev);
- int (*check_timing)(struct device *dev, void *timing);
- int (*power_on)(struct device *dev, int mode);
-};
-
-/*
- * Exynos drm manager ops
- *
- * @dpms: control device power.
- * @apply: set timing, vblank and overlay data to registers.
- * @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.
- */
-struct exynos_drm_manager_ops {
- void (*dpms)(struct device *subdrv_dev, int mode);
- void (*apply)(struct device *subdrv_dev);
- void (*mode_fixup)(struct device *subdrv_dev,
- struct drm_connector *connector,
- struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode);
- void (*mode_set)(struct device *subdrv_dev, void *mode);
- void (*get_max_resol)(struct device *subdrv_dev, unsigned int *width,
- unsigned int *height);
- void (*commit)(struct device *subdrv_dev);
- int (*enable_vblank)(struct device *subdrv_dev);
- void (*disable_vblank)(struct device *subdrv_dev);
-};
-
-/*
- * Exynos drm common manager structure.
- *
- * @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.
- * @overlay_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 overlay reigsters.
- * @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.
- */
-struct exynos_drm_manager {
- struct device *dev;
- int pipe;
- struct exynos_drm_manager_ops *ops;
- struct exynos_drm_overlay_ops *overlay_ops;
- struct exynos_drm_display_ops *display_ops;
-};
-
/*
* Exynos drm private structure.
*/
* @dev: pointer to device object for subdrv device driver.
* @drm_dev: pointer to drm_device and this pointer would be set
* when sub driver calls exynos_drm_subdrv_register().
- * @manager: subdrv has its own manager to control a hardware appropriately
- * and we can access a hardware drawing on this manager.
+ * @display: A pointer to the display structure for this sub driver
* @probe: this callback would be called by exynos drm driver after
* subdrv is registered to it.
* @remove: this callback is used to release resources created
struct list_head list;
struct device *dev;
struct drm_device *drm_dev;
- struct exynos_drm_manager *manager;
+ struct exynos_drm_display *display;
- int (*probe)(struct drm_device *drm_dev, struct device *dev);
+ int (*probe)(struct drm_device *drm_dev,
+ struct exynos_drm_subdrv *subdrv);
void (*remove)(struct drm_device *dev);
int (*open)(struct drm_device *drm_dev, struct device *dev,
struct drm_file *file);
#include "exynos_drm_drv.h"
#include "exynos_drm_crtc.h"
#include "exynos_drm_encoder.h"
+#include "exynos_drm_display.h"
#define to_exynos_encoder(x) container_of(x, struct exynos_drm_encoder,\
drm_encoder)
* exynos specific encoder structure.
*
* @drm_encoder: encoder object.
- * @manager: specific encoder has its own manager to control a hardware
- * appropriately and we can access a hardware drawing on this manager.
+ * @display: specific encoder has its own display to control a hardware
+ * appropriately and we can access a hardware drawing on this display.
* @dpms: store the encoder dpms value.
*/
struct exynos_drm_encoder {
- struct drm_encoder drm_encoder;
- struct exynos_drm_manager *manager;
+ struct drm_encoder drm_encoder;
+ struct exynos_drm_display *display;
int dpms;
};
{
struct drm_device *dev = encoder->dev;
struct drm_connector *connector;
- struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
+ struct exynos_drm_display *display = exynos_drm_get_display(encoder);
struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
- if (connector->encoder == encoder) {
- struct exynos_drm_display_ops *display_ops =
- manager->display_ops;
-
- DRM_DEBUG_KMS("connector[%d] dpms[%d]\n",
- connector->base.id, mode);
- if (display_ops && display_ops->power_on)
- display_ops->power_on(manager->dev, mode);
+ if (connector->encoder != encoder)
+ continue;
+
+ DRM_DEBUG_KMS("connector[%d] dpms[%d]\n", connector->base.id,
+ mode);
+
+ /* We want to make sure we order things correctly here. When
+ * turning on, start the controller, then the panel. When
+ * turning off, do the reverse.
+ */
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ if (display->controller_ops->power)
+ display->controller_ops->power(
+ display->controller_ctx, mode);
+
+ if (display->panel_ops->power)
+ display->panel_ops->power(display->panel_ctx,
+ mode);
+
exynos_encoder->dpms = mode;
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ case DRM_MODE_DPMS_OFF:
+ if (display->panel_ops->power)
+ display->panel_ops->power(display->panel_ctx,
+ mode);
+
+ if (display->controller_ops->power)
+ display->controller_ops->power(
+ display->controller_ctx, mode);
+
+ exynos_encoder->dpms = mode;
+ break;
+ default:
+ DRM_ERROR("Unknown dpms mode: %d\n", mode);
+ break;
}
}
}
static void exynos_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
{
struct drm_device *dev = encoder->dev;
- struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
- struct exynos_drm_manager_ops *manager_ops = manager->ops;
+ struct exynos_drm_display *display = exynos_drm_get_display(encoder);
struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
DRM_DEBUG_KMS("%s, encoder dpms: %d\n", __FILE__, mode);
switch (mode) {
case DRM_MODE_DPMS_ON:
- if (manager_ops && manager_ops->apply)
- manager_ops->apply(manager->dev);
+ if (display->controller_ops->apply)
+ display->controller_ops->apply(display->controller_ctx);
+
+ if (display->panel_ops->apply)
+ display->panel_ops->apply(display->panel_ctx);
+
exynos_drm_display_power(encoder, mode);
break;
case DRM_MODE_DPMS_STANDBY:
{
struct drm_device *dev = encoder->dev;
struct drm_connector *connector;
- struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
- struct exynos_drm_manager_ops *manager_ops = manager->ops;
+ struct exynos_drm_display *display = exynos_drm_get_display(encoder);
DRM_DEBUG_KMS("%s\n", __FILE__);
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
- if (connector->encoder == encoder)
- if (manager_ops && manager_ops->mode_fixup)
- manager_ops->mode_fixup(manager->dev, connector,
- mode, adjusted_mode);
+ if (connector->encoder != encoder)
+ continue;
+
+ if (display->panel_ops->mode_fixup)
+ display->panel_ops->mode_fixup(display->panel_ctx,
+ connector, mode, adjusted_mode);
}
return true;
{
struct drm_device *dev = encoder->dev;
struct drm_connector *connector;
- struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
- struct exynos_drm_manager_ops *manager_ops = manager->ops;
- struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
+ struct exynos_drm_display *display = exynos_drm_get_display(encoder);
struct exynos_drm_overlay *overlay = get_exynos_drm_overlay(dev,
encoder->crtc);
DRM_DEBUG_KMS("%s\n", __FILE__);
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
- if (connector->encoder == encoder) {
- if (manager_ops && manager_ops->mode_set)
- manager_ops->mode_set(manager->dev,
- adjusted_mode);
+ if (connector->encoder != encoder)
+ continue;
- if (overlay_ops && overlay_ops->mode_set)
- overlay_ops->mode_set(manager->dev, overlay);
- }
+ if (display->panel_ops->mode_set)
+ display->panel_ops->mode_set(display->panel_ctx,
+ adjusted_mode);
+
+ if (display->controller_ops->mode_set)
+ display->controller_ops->mode_set(
+ display->controller_ctx, overlay);
}
}
static void exynos_drm_encoder_commit(struct drm_encoder *encoder)
{
- struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
- struct exynos_drm_manager_ops *manager_ops = manager->ops;
+ struct exynos_drm_display *display = exynos_drm_get_display(encoder);
DRM_DEBUG_KMS("%s\n", __FILE__);
+ if (display->controller_ops->commit)
+ display->controller_ops->commit(display->controller_ctx);
- if (manager_ops && manager_ops->commit)
- manager_ops->commit(manager->dev);
+ if (display->panel_ops->commit)
+ display->panel_ops->commit(display->panel_ctx);
}
static struct drm_crtc *
static void exynos_drm_encoder_destroy(struct drm_encoder *encoder)
{
- struct exynos_drm_encoder *exynos_encoder =
- to_exynos_encoder(encoder);
+ struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
DRM_DEBUG_KMS("%s\n", __FILE__);
- exynos_encoder->manager->pipe = -1;
+ exynos_encoder->display->pipe = -1;
drm_encoder_cleanup(encoder);
kfree(exynos_encoder);
{
struct drm_encoder *clone;
struct drm_device *dev = encoder->dev;
- struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
- struct exynos_drm_display_ops *display_ops =
- exynos_encoder->manager->display_ops;
+ struct exynos_drm_display *display = exynos_drm_get_display(encoder);
unsigned int clone_mask = 0;
int cnt = 0;
list_for_each_entry(clone, &dev->mode_config.encoder_list, head) {
- switch (display_ops->type) {
- case EXYNOS_DISPLAY_TYPE_LCD:
- case EXYNOS_DISPLAY_TYPE_HDMI:
- case EXYNOS_DISPLAY_TYPE_VIDI:
+ switch (display->display_type) {
+ case EXYNOS_DRM_DISPLAY_TYPE_FIMD:
+ case EXYNOS_DRM_DISPLAY_TYPE_MIXER:
+ case EXYNOS_DRM_DISPLAY_TYPE_VIDI:
clone_mask |= (1 << (cnt++));
break;
default:
struct drm_encoder *
exynos_drm_encoder_create(struct drm_device *dev,
- struct exynos_drm_manager *manager,
+ struct exynos_drm_display *display,
unsigned int possible_crtcs)
{
struct drm_encoder *encoder;
DRM_DEBUG_KMS("%s\n", __FILE__);
- if (!manager || !possible_crtcs)
- return NULL;
-
- if (!manager->dev)
+ if (!display || !possible_crtcs)
return NULL;
exynos_encoder = kzalloc(sizeof(*exynos_encoder), GFP_KERNEL);
}
exynos_encoder->dpms = DRM_MODE_DPMS_OFF;
- exynos_encoder->manager = manager;
+ exynos_encoder->display = display;
encoder = &exynos_encoder->drm_encoder;
encoder->possible_crtcs = possible_crtcs;
return encoder;
}
-struct exynos_drm_manager *exynos_drm_get_manager(struct drm_encoder *encoder)
+struct exynos_drm_display *exynos_drm_get_display(struct drm_encoder *encoder)
{
- return to_exynos_encoder(encoder)->manager;
+ return to_exynos_encoder(encoder)->display;
}
void exynos_drm_fn_encoder(struct drm_crtc *crtc, void *data,
struct drm_device *dev = crtc->dev;
struct drm_encoder *encoder;
struct exynos_drm_private *private = dev->dev_private;
- struct exynos_drm_manager *manager;
+ struct exynos_drm_display *display;
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
/*
* otherwise check crtc attached to encoder
*/
if (!encoder->crtc) {
- manager = to_exynos_encoder(encoder)->manager;
- if (manager->pipe < 0 ||
- private->crtc[manager->pipe] != crtc)
- continue;
- } else {
- if (encoder->crtc != crtc)
+ display = exynos_drm_get_display(encoder);
+ if (display->pipe < 0 ||
+ private->crtc[display->pipe] != crtc)
continue;
+ } else if (encoder->crtc != crtc) {
+ continue;
}
fn(encoder, data);
void exynos_drm_enable_vblank(struct drm_encoder *encoder, void *data)
{
- struct exynos_drm_manager *manager =
- to_exynos_encoder(encoder)->manager;
- struct exynos_drm_manager_ops *manager_ops = manager->ops;
+ struct exynos_drm_display *display = exynos_drm_get_display(encoder);
int crtc = *(int *)data;
- if (manager->pipe == -1)
- manager->pipe = crtc;
+ if (display->pipe == -1)
+ display->pipe = crtc;
- if (manager_ops->enable_vblank)
- manager_ops->enable_vblank(manager->dev);
+ /* TODO(seanpaul): Fix the pipe interaction here */
+ if (display->controller_ops->enable_vblank)
+ display->controller_ops->enable_vblank(display->controller_ctx,
+ display->pipe);
}
void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data)
{
- struct exynos_drm_manager *manager =
- to_exynos_encoder(encoder)->manager;
- struct exynos_drm_manager_ops *manager_ops = manager->ops;
+ struct exynos_drm_display *display = exynos_drm_get_display(encoder);
int crtc = *(int *)data;
- if (manager->pipe == -1)
- manager->pipe = crtc;
+ /*
+ * TODO(seanpaul): This seems like a hack. I don't think it's actually
+ * needed for 2 reasons:
+ * (1) disable_vblank implies vblank has been enabled. If
+ * enable_vblank hasn't already been called, that's a bug.
+ * (2) Even if (1) isn't true, this function should just disable an
+ * interrupt, and shouldn't affect pipe.
+ */
+ if (display->pipe == -1)
+ display->pipe = crtc;
- if (manager_ops->disable_vblank)
- manager_ops->disable_vblank(manager->dev);
+ if (display->controller_ops->disable_vblank)
+ display->controller_ops->disable_vblank(
+ display->controller_ctx);
}
void exynos_drm_encoder_crtc_plane_commit(struct drm_encoder *encoder,
void *data)
{
- struct exynos_drm_manager *manager =
- to_exynos_encoder(encoder)->manager;
- struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
+ struct exynos_drm_display *display = exynos_drm_get_display(encoder);
int zpos = DEFAULT_ZPOS;
if (data)
zpos = *(int *)data;
- if (overlay_ops && overlay_ops->commit)
- overlay_ops->commit(manager->dev, zpos);
+ if (display->controller_ops->win_commit)
+ display->controller_ops->win_commit(display->controller_ctx,
+ zpos);
}
void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data)
{
- struct exynos_drm_manager *manager =
- to_exynos_encoder(encoder)->manager;
+ struct exynos_drm_display *display = exynos_drm_get_display(encoder);
int crtc = *(int *)data;
int zpos = DEFAULT_ZPOS;
/*
* when crtc is detached from encoder, this pipe is used
- * to select manager operation
+ * to select display operation
*/
- manager->pipe = crtc;
+ display->pipe = crtc;
exynos_drm_encoder_crtc_plane_commit(encoder, &zpos);
}
{
struct drm_device *dev = encoder->dev;
struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
- struct exynos_drm_manager *manager = exynos_encoder->manager;
- struct exynos_drm_manager_ops *manager_ops = manager->ops;
+ struct exynos_drm_display *display = exynos_encoder->display;
struct drm_connector *connector;
int mode = *(int *)data;
DRM_DEBUG_KMS("%s\n", __FILE__);
- if (manager_ops && manager_ops->dpms)
- manager_ops->dpms(manager->dev, mode);
+ /* We want to make sure we order things correctly here. When turning on,
+ * start the controller, then the panel. When turning off, do the
+ * reverse.
+ */
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ if (display->controller_ops->dpms)
+ display->controller_ops->dpms(display->controller_ctx,
+ mode);
+
+ if (display->panel_ops->dpms)
+ display->panel_ops->dpms(display->panel_ctx, mode);
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ case DRM_MODE_DPMS_OFF:
+ if (display->panel_ops->dpms)
+ display->panel_ops->dpms(display->panel_ctx, mode);
+
+ if (display->controller_ops->dpms)
+ display->controller_ops->dpms(display->controller_ctx,
+ mode);
+ break;
+ default:
+ DRM_ERROR("Unknown dpms mode: %d\n", mode);
+ break;
+ }
/*
* set current dpms mode to the connector connected to
/*
* if this condition is ok then it means that the crtc is already
* detached from encoder and last function for detaching is properly
- * done, so clear pipe from manager to prevent repeated call.
+ * done, so clear pipe from display to prevent repeated call.
*/
if (mode > DRM_MODE_DPMS_ON) {
if (!encoder->crtc)
- manager->pipe = -1;
+ display->pipe = -1;
}
}
void exynos_drm_encoder_crtc_mode_set(struct drm_encoder *encoder, void *data)
{
- struct exynos_drm_manager *manager =
- to_exynos_encoder(encoder)->manager;
- struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
+ struct exynos_drm_display *display = exynos_drm_get_display(encoder);
struct exynos_drm_overlay *overlay = data;
- if (overlay_ops && overlay_ops->mode_set)
- overlay_ops->mode_set(manager->dev, overlay);
+ if (display->controller_ops->mode_set)
+ display->controller_ops->mode_set(display->controller_ctx,
+ overlay);
}
void exynos_drm_encoder_crtc_disable(struct drm_encoder *encoder, void *data)
{
- struct exynos_drm_manager *manager =
- to_exynos_encoder(encoder)->manager;
- struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
+ struct exynos_drm_display *display = exynos_drm_get_display(encoder);
int zpos = DEFAULT_ZPOS;
DRM_DEBUG_KMS("\n");
if (data)
zpos = *(int *)data;
- if (overlay_ops && overlay_ops->disable)
- overlay_ops->disable(manager->dev, zpos);
+ if (display->controller_ops->win_disable)
+ display->controller_ops->win_disable(display->controller_ctx,
+ zpos);
}
void exynos_drm_encoder_crtc_page_flip(struct drm_encoder *encoder, void *data)
{
- struct exynos_drm_manager *manager =
- to_exynos_encoder(encoder)->manager;
- struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
+ struct exynos_drm_display *display = exynos_drm_get_display(encoder);
struct exynos_drm_overlay *overlay = data;
- if (overlay_ops && overlay_ops->page_flip)
- overlay_ops->page_flip(manager->dev, overlay);
+ if (display->controller_ops->page_flip)
+ display->controller_ops->page_flip(display->controller_ctx,
+ overlay);
}
#ifndef _EXYNOS_DRM_ENCODER_H_
#define _EXYNOS_DRM_ENCODER_H_
-struct exynos_drm_manager;
+struct exynos_drm_display;
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 int possible_crtcs);
-struct exynos_drm_manager *
-exynos_drm_get_manager(struct drm_encoder *encoder);
+ struct exynos_drm_display *display,
+ unsigned int possible_crtcs);
+struct exynos_drm_display *exynos_drm_get_display(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);
#include "exynos_drm_drv.h"
#include "exynos_drm_crtc.h"
#include "exynos_drm_fbdev.h"
+#include "exynos_drm_display.h"
/*
* FIMD is stand for Fully Interactive Mobile Display and
};
struct fimd_context {
- struct exynos_drm_subdrv subdrv;
+ struct drm_device *drm_dev;
+ enum disp_panel_type panel_type;
+ int pipe;
int irq;
struct drm_crtc *crtc;
struct clk *bus_clk;
dp_dev = dev;
}
-static bool fimd_display_is_connected(struct device *dev)
+static bool fimd_display_is_connected(void *ctx)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
return true;
}
-static void *fimd_get_panel(struct device *dev)
+static struct exynos_drm_panel_info *fimd_get_panel(void *ctx)
{
- struct fimd_context *ctx = get_fimd_context(dev);
+ struct fimd_context *fimd_ctx = ctx;
DRM_DEBUG_KMS("%s\n", __FILE__);
- return ctx->panel;
+ return fimd_ctx->panel;
}
-static int fimd_check_timing(struct device *dev, void *timing)
+static int fimd_check_timing(void *ctx, void *timing)
{
- struct fimd_context *ctx = get_fimd_context(dev);
+ struct fimd_context *fimd_ctx = ctx;
struct fb_videomode *check_timing = timing;
int i;
DRM_DEBUG_KMS("%s\n", __FILE__);
for (i = 0;i< MAX_NR_PANELS;i++) {
- if (ctx->panel[i].timing.xres == -1 &&
- ctx->panel[i].timing.yres == -1)
+ if (fimd_ctx->panel[i].timing.xres == -1 &&
+ fimd_ctx->panel[i].timing.yres == -1)
break;
- if (ctx->panel[i].timing.xres == check_timing->xres &&
- ctx->panel[i].timing.yres == check_timing->yres &&
- ctx->panel[i].timing.refresh == check_timing->refresh
- )
+ if (fimd_ctx->panel[i].timing.xres == check_timing->xres &&
+ fimd_ctx->panel[i].timing.yres == check_timing->yres &&
+ fimd_ctx->panel[i].timing.refresh == check_timing->refresh)
return 0;
}
static int fimd_power_on(struct fimd_context *ctx, bool enable);
-static int fimd_display_power_on(struct device *dev, int mode)
+static int fimd_power(void *ctx, int mode)
{
- struct fimd_context *ctx = get_fimd_context(dev);
+ struct fimd_context *fimd_ctx = ctx;
bool enable;
DRM_DEBUG_KMS("%s\n", __FILE__);
return -EINVAL;
}
- fimd_power_on(ctx, enable);
+ fimd_power_on(fimd_ctx, enable);
return 0;
}
-static struct exynos_drm_display_ops fimd_display_ops = {
- .type = EXYNOS_DISPLAY_TYPE_LCD,
- .is_connected = fimd_display_is_connected,
- .get_panel = fimd_get_panel,
- .check_timing = fimd_check_timing,
- .power_on = fimd_display_power_on,
-};
-
-static void fimd_apply(struct device *subdrv_dev)
-{
- struct fimd_context *ctx = get_fimd_context(subdrv_dev);
- struct exynos_drm_manager *mgr = ctx->subdrv.manager;
- struct exynos_drm_manager_ops *mgr_ops = mgr->ops;
- struct exynos_drm_overlay_ops *ovl_ops = mgr->overlay_ops;
- struct fimd_win_data *win_data;
- int i;
-
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- for (i = 0; i < WINDOWS_NR; i++) {
- win_data = &ctx->win_data[i];
- if (win_data->enabled && (ovl_ops && ovl_ops->commit))
- ovl_ops->commit(subdrv_dev, i);
- }
-
- if (mgr_ops && mgr_ops->commit)
- mgr_ops->commit(subdrv_dev);
-}
-
-static void fimd_commit(struct device *dev)
+static void fimd_commit(void *ctx)
{
- struct fimd_context *ctx = get_fimd_context(dev);
- struct exynos_drm_panel_info *panel = &ctx->panel[ctx->idx];
+ struct fimd_context *fimd_ctx = ctx;
+ struct exynos_drm_panel_info *panel = &fimd_ctx->panel[fimd_ctx->idx];
struct fb_videomode *timing = &panel->timing;
u32 val;
- if (ctx->suspended)
+ if (fimd_ctx->suspended)
return;
DRM_DEBUG_KMS("%s\n", __FILE__);
/* setup polarity values from machine code. */
- writel(ctx->vidcon1, ctx->regs + VIDCON1);
+ writel(fimd_ctx->vidcon1, fimd_ctx->regs + VIDCON1);
/* setup vertical timing values. */
val = VIDTCON0_VBPD(timing->upper_margin - 1) |
VIDTCON0_VFPD(timing->lower_margin - 1) |
VIDTCON0_VSPW(timing->vsync_len - 1);
- writel(val, ctx->regs + VIDTCON0);
+ writel(val, fimd_ctx->regs + VIDTCON0);
/* setup horizontal timing values. */
val = VIDTCON1_HBPD(timing->left_margin - 1) |
VIDTCON1_HFPD(timing->right_margin - 1) |
VIDTCON1_HSPW(timing->hsync_len - 1);
- writel(val, ctx->regs + VIDTCON1);
+ writel(val, fimd_ctx->regs + VIDTCON1);
/* setup horizontal and vertical display size. */
val = VIDTCON2_LINEVAL(timing->yres - 1) |
VIDTCON2_HOZVAL(timing->xres - 1);
- writel(val, ctx->regs + VIDTCON2);
+ writel(val, fimd_ctx->regs + VIDTCON2);
/* setup clock source, clock divider, enable dma. */
- val = ctx->vidcon0;
+ val = fimd_ctx->vidcon0;
val &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR);
- if (ctx->clkdiv[ctx->idx] > 1)
- val |= VIDCON0_CLKVAL_F(ctx->clkdiv[ctx->idx] - 1) | VIDCON0_CLKDIR;
+ if (fimd_ctx->clkdiv[fimd_ctx->idx] > 1)
+ val |= VIDCON0_CLKVAL_F(fimd_ctx->clkdiv[fimd_ctx->idx] - 1) |
+ VIDCON0_CLKDIR;
else
val &= ~VIDCON0_CLKDIR; /* 1:1 clock */
* at vsync(same as dma start)
*/
val |= VIDCON0_ENVID | VIDCON0_ENVID_F;
- writel(val, ctx->regs + VIDCON0);
+ writel(val, fimd_ctx->regs + VIDCON0);
}
-static int fimd_enable_vblank(struct device *dev)
+static struct exynos_panel_ops fimd_panel_ops = {
+ .is_connected = fimd_display_is_connected,
+ .check_timing = fimd_check_timing,
+};
+
+static int fimd_enable_vblank(void *ctx, int pipe)
{
- struct fimd_context *ctx = get_fimd_context(dev);
+ struct fimd_context *fimd_ctx = ctx;
u32 val;
DRM_DEBUG_KMS("%s\n", __FILE__);
- if (ctx->suspended)
+ fimd_ctx->pipe = pipe;
+
+ if (fimd_ctx->suspended)
return -EPERM;
- if (!test_and_set_bit(0, &ctx->irq_flags)) {
- val = readl(ctx->regs + VIDINTCON0);
+ if (!test_and_set_bit(0, &fimd_ctx->irq_flags)) {
+ val = readl(fimd_ctx->regs + VIDINTCON0);
val |= VIDINTCON0_INT_ENABLE;
val |= VIDINTCON0_INT_FRAME;
val &= ~VIDINTCON0_FRAMESEL1_MASK;
val |= VIDINTCON0_FRAMESEL1_NONE;
- writel(val, ctx->regs + VIDINTCON0);
+ writel(val, fimd_ctx->regs + VIDINTCON0);
}
return 0;
}
-static void fimd_disable_vblank(struct device *dev)
+static void fimd_disable_vblank(void *ctx)
{
- struct fimd_context *ctx = get_fimd_context(dev);
+ struct fimd_context *fimd_ctx = ctx;
u32 val;
DRM_DEBUG_KMS("%s\n", __FILE__);
- if (ctx->suspended)
+ if (fimd_ctx->suspended)
return;
- if (test_and_clear_bit(0, &ctx->irq_flags)) {
- val = readl(ctx->regs + VIDINTCON0);
+ if (test_and_clear_bit(0, &fimd_ctx->irq_flags)) {
+ val = readl(fimd_ctx->regs + VIDINTCON0);
val &= ~VIDINTCON0_INT_FRAME;
val &= ~VIDINTCON0_INT_ENABLE;
- writel(val, ctx->regs + VIDINTCON0);
+ writel(val, fimd_ctx->regs + VIDINTCON0);
}
}
-static struct exynos_drm_manager_ops fimd_manager_ops = {
- .apply = fimd_apply,
- .commit = fimd_commit,
- .enable_vblank = fimd_enable_vblank,
- .disable_vblank = fimd_disable_vblank,
-};
-
-static void fimd_win_mode_set(struct device *dev,
- struct exynos_drm_overlay *overlay)
+static void fimd_win_mode_set(void *ctx, struct exynos_drm_overlay *overlay)
{
- struct fimd_context *ctx = get_fimd_context(dev);
+ struct fimd_context *fimd_ctx = ctx;
struct fimd_win_data *win_data;
- int win;
+ int i, win;
unsigned long offset;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (!overlay) {
- dev_err(dev, "overlay is NULL\n");
+ DRM_ERROR("overlay is NULL\n");
return;
}
win = overlay->zpos;
if (win == DEFAULT_ZPOS)
- win = ctx->default_win;
+ win = fimd_ctx->default_win;
if (win < 0 || win > WINDOWS_NR)
return;
- if(win == ctx->default_win) {
- for(ctx->idx = 0;ctx->idx < MAX_NR_PANELS;ctx->idx++) {
- if (ctx->panel[ctx->idx].timing.xres == -1 &&
- ctx->panel[ctx->idx].timing.yres == -1) {
- DRM_ERROR("Invalid panel parameters");
- ctx->idx = 0; /* Reset to first panel index*/
- break;
- }
- if (ctx->panel[ctx->idx].timing.xres == overlay->fb_width &&
- ctx->panel[ctx->idx].timing.yres == overlay->fb_height)
- break;
+ if (win == fimd_ctx->default_win) {
+ for (i = 0; i < MAX_NR_PANELS; i++) {
+ struct fb_videomode *timing;
+
+ timing = &fimd_ctx->panel[i].timing;
+
+ if (timing->xres == -1 && timing->yres == -1) {
+ DRM_ERROR("Invalid panel parameters");
+ i = 0; /* Reset to first panel index*/
+ break;
+ }
+ if (timing->xres == overlay->fb_width &&
+ timing->yres == overlay->fb_height)
+ break;
}
+ fimd_ctx->idx = i;
}
offset = overlay->fb_x * (overlay->bpp >> 3);
DRM_DEBUG_KMS("offset = 0x%lx, pitch = %x\n",
offset, overlay->fb_pitch);
- win_data = &ctx->win_data[win];
+ win_data = &fimd_ctx->win_data[win];
win_data->offset_x = overlay->crtc_x;
win_data->offset_y = overlay->crtc_y;
overlay->fb_width, overlay->crtc_width);
}
-static void fimd_win_set_pixfmt(struct device *dev, unsigned int win)
+static void fimd_win_set_pixfmt(struct fimd_context *fimd_ctx, unsigned int win)
{
- struct fimd_context *ctx = get_fimd_context(dev);
- struct fimd_win_data *win_data = &ctx->win_data[win];
+ struct fimd_win_data *win_data = &fimd_ctx->win_data[win];
unsigned long val;
unsigned long bytes;
DRM_DEBUG_KMS("bpp = %d\n", win_data->bpp);
- writel(val, ctx->regs + WINCON(win));
+ writel(val, fimd_ctx->regs + WINCON(win));
}
-static void fimd_win_set_colkey(struct device *dev, unsigned int win)
+static void fimd_win_set_colkey(struct fimd_context *fimd_ctx, unsigned int win)
{
- struct fimd_context *ctx = get_fimd_context(dev);
unsigned int keycon0 = 0, keycon1 = 0;
DRM_DEBUG_KMS("%s\n", __FILE__);
keycon1 = WxKEYCON1_COLVAL(0xffffffff);
- writel(keycon0, ctx->regs + WKEYCON0_BASE(win));
- writel(keycon1, ctx->regs + WKEYCON1_BASE(win));
+ writel(keycon0, fimd_ctx->regs + WKEYCON0_BASE(win));
+ writel(keycon1, fimd_ctx->regs + WKEYCON1_BASE(win));
}
static void mie_set_6bit_dithering(struct fimd_context *ctx)
}
}
-static void fimd_win_page_flip(struct device *dev,
- struct exynos_drm_overlay *overlay)
+static void fimd_win_page_flip(void *ctx, struct exynos_drm_overlay *overlay)
{
- struct fimd_context *ctx = get_fimd_context(dev);
+ struct fimd_context *fimd_ctx = ctx;
int win = overlay->zpos;
struct fimd_win_data *win_data;
unsigned long offset;
if (win == DEFAULT_ZPOS)
- win = ctx->default_win;
+ win = fimd_ctx->default_win;
if (win < 0 || win > WINDOWS_NR)
return;
- win_data = &ctx->win_data[win];
+ win_data = &fimd_ctx->win_data[win];
offset = overlay->fb_x * (overlay->bpp >> 3);
offset += overlay->fb_y * overlay->fb_pitch;
win_data->vaddr = overlay->vaddr[0] + offset;
}
-static void fimd_win_commit(struct device *dev, int zpos)
+static void fimd_win_commit(void *ctx, int zpos)
{
- struct fimd_context *ctx = get_fimd_context(dev);
+ struct fimd_context *fimd_ctx = ctx;
struct fimd_win_data *win_data;
int win = zpos;
unsigned long val, alpha, size;
DRM_DEBUG_KMS("%s\n", __FILE__);
- if (ctx->suspended)
+ if (fimd_ctx->suspended)
return;
if (win == DEFAULT_ZPOS)
- win = ctx->default_win;
+ win = fimd_ctx->default_win;
if (win < 0 || win > WINDOWS_NR)
return;
- win_data = &ctx->win_data[win];
+ win_data = &fimd_ctx->win_data[win];
/*
* SHADOWCON register is used for enabling timing.
*/
/* protect windows */
- val = readl(ctx->regs + SHADOWCON);
+ val = readl(fimd_ctx->regs + SHADOWCON);
val |= SHADOWCON_WINx_PROTECT(win);
- writel(val, ctx->regs + SHADOWCON);
+ writel(val, fimd_ctx->regs + SHADOWCON);
/* buffer start address */
val = (unsigned long)win_data->dma_addr;
- writel(val, ctx->regs + VIDWx_BUF_START(win, 0));
+ writel(val, fimd_ctx->regs + VIDWx_BUF_START(win, 0));
/* buffer end address */
size = win_data->fb_height * win_data->fb_pitch;
val = (unsigned long)(win_data->dma_addr + size);
- writel(val, ctx->regs + VIDWx_BUF_END(win, 0));
+ writel(val, fimd_ctx->regs + VIDWx_BUF_END(win, 0));
DRM_DEBUG_KMS("start addr = 0x%lx, end addr = 0x%lx, size = 0x%lx\n",
(unsigned long)win_data->dma_addr, val, size);
/* buffer size */
val = VIDW_BUF_SIZE_OFFSET(win_data->buf_offsize) |
VIDW_BUF_SIZE_PAGEWIDTH(win_data->line_size);
- writel(val, ctx->regs + VIDWx_BUF_SIZE(win, 0));
+ writel(val, fimd_ctx->regs + VIDWx_BUF_SIZE(win, 0));
/* OSD position */
val = VIDOSDxA_TOPLEFT_X(win_data->offset_x) |
VIDOSDxA_TOPLEFT_Y(win_data->offset_y);
- writel(val, ctx->regs + VIDOSD_A(win));
+ writel(val, fimd_ctx->regs + VIDOSD_A(win));
val = VIDOSDxB_BOTRIGHT_X(win_data->offset_x +
win_data->ovl_width - 1) |
VIDOSDxB_BOTRIGHT_Y(win_data->offset_y +
win_data->ovl_height - 1);
- writel(val, ctx->regs + VIDOSD_B(win));
+ writel(val, fimd_ctx->regs + VIDOSD_B(win));
DRM_DEBUG_KMS("osd pos: tx = %d, ty = %d, bx = %d, by = %d\n",
win_data->offset_x, win_data->offset_y,
VIDISD14C_ALPHA1_G(0xf) |
VIDISD14C_ALPHA1_B(0xf);
- writel(alpha, ctx->regs + VIDOSD_C(win));
+ writel(alpha, fimd_ctx->regs + VIDOSD_C(win));
}
/* OSD size */
if (win == 0)
offset = VIDOSD_C_SIZE_W0;
val = win_data->ovl_width * win_data->ovl_height;
- writel(val, ctx->regs + offset);
+ writel(val, fimd_ctx->regs + offset);
DRM_DEBUG_KMS("osd size = 0x%x\n", (unsigned int)val);
}
- fimd_win_set_pixfmt(dev, win);
+ fimd_win_set_pixfmt(fimd_ctx, win);
/* hardware window 0 doesn't support color key. */
if (win != 0)
- fimd_win_set_colkey(dev, win);
+ fimd_win_set_colkey(fimd_ctx, win);
/* wincon */
- val = readl(ctx->regs + WINCON(win));
+ val = readl(fimd_ctx->regs + WINCON(win));
val |= WINCONx_ENWIN;
- writel(val, ctx->regs + WINCON(win));
+ writel(val, fimd_ctx->regs + WINCON(win));
- mie_set_6bit_dithering(ctx);
+ mie_set_6bit_dithering(fimd_ctx);
/* Enable DMA channel and unprotect windows */
- val = readl(ctx->regs + SHADOWCON);
+ val = readl(fimd_ctx->regs + SHADOWCON);
val |= SHADOWCON_CHx_ENABLE(win);
val &= ~SHADOWCON_WINx_PROTECT(win);
- writel(val, ctx->regs + SHADOWCON);
+ writel(val, fimd_ctx->regs + SHADOWCON);
win_data->enabled = true;
}
-static void fimd_win_disable(struct device *dev, int zpos)
+static void fimd_win_disable(void *ctx, int zpos)
{
- struct fimd_context *ctx = get_fimd_context(dev);
+ struct fimd_context *fimd_ctx = ctx;
struct fimd_win_data *win_data;
int win = zpos;
u32 val;
DRM_DEBUG_KMS("%s\n", __FILE__);
- if (ctx->suspended)
+ if (fimd_ctx->suspended)
return;
if (win == DEFAULT_ZPOS)
- win = ctx->default_win;
+ win = fimd_ctx->default_win;
if (win < 0 || win > WINDOWS_NR)
return;
- win_data = &ctx->win_data[win];
+ win_data = &fimd_ctx->win_data[win];
/* protect windows */
- val = readl(ctx->regs + SHADOWCON);
+ val = readl(fimd_ctx->regs + SHADOWCON);
val |= SHADOWCON_WINx_PROTECT(win);
- writel(val, ctx->regs + SHADOWCON);
+ writel(val, fimd_ctx->regs + SHADOWCON);
/* wincon */
- val = readl(ctx->regs + WINCON(win));
+ val = readl(fimd_ctx->regs + WINCON(win));
val &= ~WINCONx_ENWIN;
- writel(val, ctx->regs + WINCON(win));
+ writel(val, fimd_ctx->regs + WINCON(win));
/* unprotect windows */
- val = readl(ctx->regs + SHADOWCON);
+ val = readl(fimd_ctx->regs + SHADOWCON);
val &= ~SHADOWCON_CHx_ENABLE(win);
val &= ~SHADOWCON_WINx_PROTECT(win);
- writel(val, ctx->regs + SHADOWCON);
+ writel(val, fimd_ctx->regs + SHADOWCON);
win_data->enabled = false;
}
-static struct exynos_drm_overlay_ops fimd_overlay_ops = {
- .mode_set = fimd_win_mode_set,
- .page_flip = fimd_win_page_flip,
- .commit = fimd_win_commit,
- .disable = fimd_win_disable,
-};
+static void fimd_apply(void *ctx)
+{
+ struct fimd_context *fimd_ctx = ctx;
+ struct fimd_win_data *win_data;
+ int i;
-static struct exynos_drm_manager fimd_manager = {
- .pipe = -1,
- .ops = &fimd_manager_ops,
- .overlay_ops = &fimd_overlay_ops,
- .display_ops = &fimd_display_ops,
-};
+ for (i = 0; i < WINDOWS_NR; i++) {
+ win_data = &fimd_ctx->win_data[i];
+ if (win_data->enabled)
+ fimd_win_commit(ctx, i);
+ }
+}
-static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
+static irqreturn_t fimd_irq_handler(int irq, void *arg)
{
- struct fimd_context *ctx = (struct fimd_context *)dev_id;
- struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
- struct drm_device *drm_dev = subdrv->drm_dev;
- struct exynos_drm_manager *manager = subdrv->manager;
+ struct fimd_context *fimd_ctx = (struct fimd_context *)arg;
u32 val;
- val = readl(ctx->regs + VIDINTCON1);
+ val = readl(fimd_ctx->regs + VIDINTCON1);
if (val & VIDINTCON1_INT_FRAME)
/* VSYNC interrupt */
- writel(VIDINTCON1_INT_FRAME, ctx->regs + VIDINTCON1);
+ writel(VIDINTCON1_INT_FRAME, fimd_ctx->regs + VIDINTCON1);
/* check the crtc is detached already from encoder */
- if (manager->pipe < 0)
+ if (fimd_ctx->pipe < 0)
goto out;
- drm_handle_vblank(drm_dev, manager->pipe);
- exynos_drm_crtc_finish_pageflip(drm_dev, manager->pipe);
+ drm_handle_vblank(fimd_ctx->drm_dev, fimd_ctx->pipe);
+ exynos_drm_crtc_finish_pageflip(fimd_ctx->drm_dev, fimd_ctx->pipe);
out:
return IRQ_HANDLED;
}
-static int fimd_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
+static int fimd_subdrv_probe(void *ctx, struct drm_device *drm_dev)
{
+ struct fimd_context *fimd_ctx = ctx;
DRM_DEBUG_KMS("%s\n", __FILE__);
/*
*/
drm_dev->vblank_disable_allowed = 1;
+ fimd_ctx->drm_dev = drm_dev;
+
return 0;
}
-static void fimd_subdrv_remove(struct drm_device *drm_dev)
-{
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- /* TODO. */
-}
+static struct exynos_controller_ops fimd_controller_ops = {
+ .subdrv_probe = fimd_subdrv_probe,
+ .get_panel = fimd_get_panel,
+ .enable_vblank = fimd_enable_vblank,
+ .disable_vblank = fimd_disable_vblank,
+ .power = fimd_power,
+ .mode_set = fimd_win_mode_set,
+ .page_flip = fimd_win_page_flip,
+ .commit = fimd_commit,
+ .apply = fimd_apply,
+ .win_commit = fimd_win_commit,
+ .win_disable = fimd_win_disable,
+};
static int fimd_calc_clkdiv(struct fimd_context *ctx,
struct fb_videomode *timing)
/*
* Disables all windows for suspend, keeps track of which ones were enabled.
*/
-static void fimd_window_suspend(struct device *dev)
+static void fimd_window_suspend(struct fimd_context *fimd_ctx)
{
- struct fimd_context *ctx = get_fimd_context(dev);
struct fimd_win_data *win_data;
int i;
for(i = 0; i < WINDOWS_NR; i++)
{
- win_data = &ctx->win_data[i];
+ win_data = &fimd_ctx->win_data[i];
win_data->win_suspended = win_data->enabled;
- fimd_win_disable(dev, i);
+ fimd_win_disable(fimd_ctx, i);
}
}
/*
* Resumes the suspended windows.
*/
-static void fimd_window_resume(struct device *dev)
+static void fimd_window_resume(struct fimd_context *fimd_ctx)
{
- struct fimd_context *ctx = get_fimd_context(dev);
struct fimd_win_data *win_data;
int i;
for(i = 0; i < WINDOWS_NR; i++)
{
- win_data = &ctx->win_data[i];
+ win_data = &fimd_ctx->win_data[i];
if (win_data->win_suspended) {
- fimd_win_commit(dev, i);
+ fimd_win_commit(fimd_ctx, i);
win_data->win_suspended = false;
}
}
}
-static int fimd_power_on(struct fimd_context *ctx, bool enable)
+static int fimd_power_on(struct fimd_context *fimd_ctx, bool enable)
{
- struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
- struct device *dev = subdrv->dev;
- struct exynos_drm_fimd_pdata *pdata = dev->platform_data;
-
DRM_DEBUG_KMS("%s\n", __FILE__);
if (enable) {
int ret;
- ret = clk_enable(ctx->bus_clk);
+ ret = clk_enable(fimd_ctx->bus_clk);
if (ret < 0)
return ret;
- ret = clk_enable(ctx->lcd_clk);
+ ret = clk_enable(fimd_ctx->lcd_clk);
if (ret < 0) {
- clk_disable(ctx->bus_clk);
+ clk_disable(fimd_ctx->bus_clk);
return ret;
}
- ctx->suspended = false;
+ fimd_ctx->suspended = false;
/* if vblank was enabled status, enable it again. */
- if (test_and_clear_bit(0, &ctx->irq_flags))
- fimd_enable_vblank(dev);
+ if (test_and_clear_bit(0, &fimd_ctx->irq_flags))
+ fimd_enable_vblank(fimd_ctx, fimd_ctx->pipe);
- fimd_apply(dev);
+ fimd_apply(fimd_ctx);
+ fimd_commit(fimd_ctx);
- if (pdata->panel_type == DP_LCD)
- writel(MIE_CLK_ENABLE, ctx->regs + DPCLKCON);
+ if (fimd_ctx->panel_type == DP_LCD)
+ writel(MIE_CLK_ENABLE, fimd_ctx->regs + DPCLKCON);
- fimd_window_resume(dev);
+ fimd_window_resume(fimd_ctx);
if (dp_dev)
exynos_dp_resume(dp_dev);
* suspend that connector. Otherwise we might try to scan from
* a destroyed buffer later.
*/
- fimd_window_suspend(dev);
+ fimd_window_suspend(fimd_ctx);
- if (pdata->panel_type == DP_LCD)
- writel(0, ctx->regs + DPCLKCON);
+ if (fimd_ctx->panel_type == DP_LCD)
+ writel(0, fimd_ctx->regs + DPCLKCON);
- clk_disable(ctx->lcd_clk);
- clk_disable(ctx->bus_clk);
+ clk_disable(fimd_ctx->lcd_clk);
+ clk_disable(fimd_ctx->bus_clk);
- ctx->suspended = true;
+ fimd_ctx->suspended = true;
}
return 0;
static int __devinit fimd_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct fimd_context *ctx;
- struct exynos_drm_subdrv *subdrv;
+ struct fimd_context *fimd_ctx;
struct exynos_drm_fimd_pdata *pdata;
struct exynos_drm_panel_info *panel;
struct resource *res;
return -EINVAL;
}
- ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
- if (!ctx)
+ fimd_ctx = kzalloc(sizeof(*fimd_ctx), GFP_KERNEL);
+ if (!fimd_ctx)
return -ENOMEM;
- ctx->bus_clk = clk_get(dev, "fimd");
- if (IS_ERR(ctx->bus_clk)) {
+ fimd_ctx->panel_type = pdata->panel_type;
+ fimd_ctx->pipe = -1;
+
+ fimd_ctx->bus_clk = clk_get(dev, "fimd");
+ if (IS_ERR(fimd_ctx->bus_clk)) {
dev_err(dev, "failed to get bus clock\n");
- ret = PTR_ERR(ctx->bus_clk);
+ ret = PTR_ERR(fimd_ctx->bus_clk);
goto err_clk_get;
}
- ctx->lcd_clk = clk_get(dev, "sclk_fimd");
- if (IS_ERR(ctx->lcd_clk)) {
+ fimd_ctx->lcd_clk = clk_get(dev, "sclk_fimd");
+ if (IS_ERR(fimd_ctx->lcd_clk)) {
dev_err(dev, "failed to get lcd clock\n");
- ret = PTR_ERR(ctx->lcd_clk);
+ ret = PTR_ERR(fimd_ctx->lcd_clk);
goto err_bus_clk;
}
goto err_clk;
}
- if (clk_set_parent(ctx->lcd_clk, clk_parent)) {
- ret = PTR_ERR(ctx->lcd_clk);
+ if (clk_set_parent(fimd_ctx->lcd_clk, clk_parent)) {
+ ret = PTR_ERR(fimd_ctx->lcd_clk);
goto err_clk;
}
- if (clk_set_rate(ctx->lcd_clk, pdata->clock_rate)) {
- ret = PTR_ERR(ctx->lcd_clk);
+ if (clk_set_rate(fimd_ctx->lcd_clk, pdata->clock_rate)) {
+ ret = PTR_ERR(fimd_ctx->lcd_clk);
goto err_clk;
}
goto err_clk;
}
- ctx->regs_res = request_mem_region(res->start, resource_size(res),
+ fimd_ctx->regs_res = request_mem_region(res->start, resource_size(res),
dev_name(dev));
- if (!ctx->regs_res) {
+ if (!fimd_ctx->regs_res) {
dev_err(dev, "failed to claim register region\n");
ret = -ENOENT;
goto err_clk;
}
- ctx->regs = ioremap(res->start, resource_size(res));
- if (!ctx->regs) {
+ fimd_ctx->regs = ioremap(res->start, resource_size(res));
+ if (!fimd_ctx->regs) {
dev_err(dev, "failed to map registers\n");
ret = -ENXIO;
goto err_req_region_io;
}
- ctx->regs_mie = ioremap(MIE_BASE_ADDRESS, 0x400);
- if (!ctx->regs_mie) {
+ fimd_ctx->regs_mie = ioremap(MIE_BASE_ADDRESS, 0x400);
+ if (!fimd_ctx->regs_mie) {
dev_err(dev, "failed to map registers\n");
ret = -ENXIO;
goto err_req_region_io_mie;
goto err_req_region_irq;
}
- ctx->irq = res->start;
+ fimd_ctx->irq = res->start;
- ret = request_irq(ctx->irq, fimd_irq_handler, 0, "drm_fimd", ctx);
+ ret = request_irq(fimd_ctx->irq, fimd_irq_handler, 0, "drm_fimd",
+ fimd_ctx);
if (ret < 0) {
dev_err(dev, "irq request failed.\n");
goto err_req_irq;
}
- ctx->vidcon0 = pdata->vidcon0;
- ctx->vidcon1 = pdata->vidcon1;
- ctx->default_win = pdata->default_win;
- ctx->panel = panel;
-
- subdrv = &ctx->subdrv;
+ fimd_ctx->vidcon0 = pdata->vidcon0;
+ fimd_ctx->vidcon1 = pdata->vidcon1;
+ fimd_ctx->default_win = pdata->default_win;
+ fimd_ctx->panel = panel;
- subdrv->dev = dev;
- subdrv->manager = &fimd_manager;
- subdrv->probe = fimd_subdrv_probe;
- subdrv->remove = fimd_subdrv_remove;
+ mutex_init(&fimd_ctx->lock);
- mutex_init(&ctx->lock);
-
- platform_set_drvdata(pdev, ctx);
+ platform_set_drvdata(pdev, fimd_ctx);
pm_runtime_enable(dev);
pm_runtime_get_sync(dev);
if(panel[i].timing.xres == -1 && panel[i].timing.yres == -1)
break;
- ctx->clkdiv[i] = fimd_calc_clkdiv(ctx, &panel[i].timing);
- panel[i].timing.pixclock = clk_get_rate(ctx->lcd_clk) / ctx->clkdiv[i];
+ fimd_ctx->clkdiv[i] = fimd_calc_clkdiv(fimd_ctx,
+ &panel[i].timing);
+ panel[i].timing.pixclock = clk_get_rate(fimd_ctx->lcd_clk) /
+ fimd_ctx->clkdiv[i];
DRM_DEBUG_KMS("pixel clock = %d, clkdiv = %d\n for panel[%d]",
- panel[i].timing.pixclock, ctx->clkdiv[i],i);
+ panel[i].timing.pixclock,
+ fimd_ctx->clkdiv[i], i);
}
for (win = 0; win < WINDOWS_NR; win++)
- fimd_clear_win(ctx, win);
+ fimd_clear_win(fimd_ctx, win);
- if (pdata->panel_type == DP_LCD)
- writel(MIE_CLK_ENABLE, ctx->regs + DPCLKCON);
+ if (fimd_ctx->panel_type == DP_LCD)
+ writel(MIE_CLK_ENABLE, fimd_ctx->regs + DPCLKCON);
- exynos_drm_subdrv_register(subdrv);
+ exynos_display_attach_controller(EXYNOS_DRM_DISPLAY_TYPE_FIMD,
+ &fimd_controller_ops, fimd_ctx);
+ exynos_display_attach_panel(EXYNOS_DRM_DISPLAY_TYPE_FIMD,
+ &fimd_panel_ops, fimd_ctx);
return 0;
err_req_irq:
err_req_region_irq:
- iounmap(ctx->regs_mie);
+ iounmap(fimd_ctx->regs_mie);
err_req_region_io_mie:
- iounmap(ctx->regs);
+ iounmap(fimd_ctx->regs);
err_req_region_io:
- release_resource(ctx->regs_res);
- kfree(ctx->regs_res);
+ release_resource(fimd_ctx->regs_res);
+ kfree(fimd_ctx->regs_res);
err_clk:
- clk_disable(ctx->lcd_clk);
- clk_put(ctx->lcd_clk);
+ clk_disable(fimd_ctx->lcd_clk);
+ clk_put(fimd_ctx->lcd_clk);
err_bus_clk:
- clk_disable(ctx->bus_clk);
- clk_put(ctx->bus_clk);
+ clk_disable(fimd_ctx->bus_clk);
+ clk_put(fimd_ctx->bus_clk);
err_clk_get:
#ifdef CONFIG_EXYNOS_IOMMU
iommu_deinit(pdev);
#endif
- kfree(ctx);
+ kfree(fimd_ctx);
return ret;
}
DRM_DEBUG_KMS("%s\n", __FILE__);
- exynos_drm_subdrv_unregister(&ctx->subdrv);
-
if (ctx->suspended)
goto out;
+++ /dev/null
-/*
- * 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 "drmP.h"
-
-#include <linux/kernel.h>
-#include <linux/wait.h>
-#include <linux/module.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"
-
-#define to_context(dev) platform_get_drvdata(to_platform_device(dev))
-#define to_subdrv(dev) to_context(dev)
-#define get_ctx_from_subdrv(subdrv) container_of(subdrv,\
- struct drm_hdmi_context, subdrv);
-
-/* 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_subdrv subdrv;
- struct exynos_drm_hdmi_context *hdmi_ctx;
- struct exynos_drm_hdmi_context *mixer_ctx;
- bool enabled[MIXER_WIN_NR];
-};
-
-void exynos_hdmi_drv_attach(struct exynos_drm_hdmi_context *ctx)
-{
- DRM_DEBUG_KMS("%s. %s.\n", __FILE__, __func__);
- hdmi_ctx = ctx;
-}
-
-void exynos_mixer_drv_attach(struct exynos_drm_hdmi_context *ctx)
-{
- DRM_DEBUG_KMS("%s. %s.\n", __FILE__, __func__);
- mixer_ctx = ctx;
-}
-
-void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops)
-{
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- if (ops)
- hdmi_ops = ops;
-}
-
-void exynos_mixer_ops_register(struct exynos_mixer_ops *ops)
-{
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- if (ops)
- mixer_ops = ops;
-}
-
-enum exynos_mixer_mode_type exynos_mixer_get_mode_type(int width, int height)
-{
- if (width >= 464 && width <= 720 && height <= 480)
- return EXYNOS_MIXER_MODE_SD_NTSC;
- else if (width >= 464 && width <= 720 && height <= 576)
- return EXYNOS_MIXER_MODE_SD_PAL;
- else if (width >= 1024 && width <= 1280 && height <= 720)
- return EXYNOS_MIXER_MODE_HD_720;
- else if ((width == 1440 && height == 900) ||
- (width == 800 && height == 600) ||
- (width >= 1664 && width <= 1920 && height <= 1080))
- return EXYNOS_MIXER_MODE_HD_1080;
- else
- return EXYNOS_MIXER_MODE_INVALID;
-}
-
-static bool drm_hdmi_is_connected(struct device *dev)
-{
- struct drm_hdmi_context *ctx = to_context(dev);
-
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- if (hdmi_ops && hdmi_ops->is_connected)
- return hdmi_ops->is_connected(ctx->hdmi_ctx->ctx);
-
- return false;
-}
-
-static int drm_hdmi_get_edid(struct device *dev,
- struct drm_connector *connector, u8 *edid, int len)
-{
- struct drm_hdmi_context *ctx = to_context(dev);
-
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- if (hdmi_ops && hdmi_ops->get_edid)
- return hdmi_ops->get_edid(ctx->hdmi_ctx->ctx, connector, edid,
- len);
-
- return 0;
-}
-
-static int drm_hdmi_check_timing(struct device *dev, void *timing)
-{
- struct drm_hdmi_context *ctx = to_context(dev);
-
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- if (hdmi_ops && hdmi_ops->check_timing)
- return hdmi_ops->check_timing(ctx->hdmi_ctx->ctx, timing);
-
- return 0;
-}
-
-static int drm_hdmi_power_on(struct device *dev, int mode)
-{
- struct drm_hdmi_context *ctx = to_context(dev);
-
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- if (mixer_ops && mixer_ops->power_on) {
- if (mixer_ops->power_on(ctx->mixer_ctx->ctx, mode))
- return -EINVAL;
- }
-
- if (hdmi_ops && hdmi_ops->power_on)
- return hdmi_ops->power_on(ctx->hdmi_ctx->ctx, mode);
-
- return 0;
-}
-
-static struct exynos_drm_display_ops drm_hdmi_display_ops = {
- .type = EXYNOS_DISPLAY_TYPE_HDMI,
- .is_connected = drm_hdmi_is_connected,
- .get_edid = drm_hdmi_get_edid,
- .check_timing = drm_hdmi_check_timing,
- .power_on = drm_hdmi_power_on,
-};
-
-static int drm_hdmi_enable_vblank(struct device *subdrv_dev)
-{
- struct drm_hdmi_context *ctx = to_context(subdrv_dev);
- struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
- struct exynos_drm_manager *manager = subdrv->manager;
-
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- if (mixer_ops && mixer_ops->enable_vblank)
- return mixer_ops->enable_vblank(ctx->mixer_ctx->ctx,
- manager->pipe);
-
- return 0;
-}
-
-static void drm_hdmi_disable_vblank(struct device *subdrv_dev)
-{
- struct drm_hdmi_context *ctx = to_context(subdrv_dev);
-
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- if (mixer_ops && mixer_ops->disable_vblank)
- return mixer_ops->disable_vblank(ctx->mixer_ctx->ctx);
-}
-
-static void drm_hdmi_mode_fixup(struct device *subdrv_dev,
- struct drm_connector *connector,
- struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
-{
- struct drm_hdmi_context *ctx = to_context(subdrv_dev);
-
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- if (hdmi_ops && hdmi_ops->mode_fixup)
- hdmi_ops->mode_fixup(ctx->hdmi_ctx->ctx, connector, mode,
- adjusted_mode);
-}
-
-static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode)
-{
- struct drm_hdmi_context *ctx = to_context(subdrv_dev);
-
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- if (hdmi_ops && hdmi_ops->mode_set)
- hdmi_ops->mode_set(ctx->hdmi_ctx->ctx, mode);
-}
-
-static void drm_hdmi_get_max_resol(struct device *subdrv_dev,
- unsigned int *width, unsigned int *height)
-{
- struct drm_hdmi_context *ctx = to_context(subdrv_dev);
-
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- if (hdmi_ops && hdmi_ops->get_max_resol)
- hdmi_ops->get_max_resol(ctx->hdmi_ctx->ctx, width, height);
-}
-
-static void drm_hdmi_commit(struct device *subdrv_dev)
-{
- struct drm_hdmi_context *ctx = to_context(subdrv_dev);
-
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- if (hdmi_ops && hdmi_ops->commit)
- hdmi_ops->commit(ctx->hdmi_ctx->ctx);
-}
-
-static void drm_hdmi_dpms(struct device *subdrv_dev, int mode)
-{
- struct drm_hdmi_context *ctx = to_context(subdrv_dev);
-
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- 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_hdmi_apply(struct device *subdrv_dev)
-{
- struct drm_hdmi_context *ctx = to_context(subdrv_dev);
- int i;
-
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- for (i = 0; i < MIXER_WIN_NR; i++) {
- if (!ctx->enabled[i])
- continue;
- if (mixer_ops && mixer_ops->win_commit)
- mixer_ops->win_commit(ctx->mixer_ctx->ctx, i);
- }
-
- if (hdmi_ops && hdmi_ops->commit)
- hdmi_ops->commit(ctx->hdmi_ctx->ctx);
-}
-
-static struct exynos_drm_manager_ops drm_hdmi_manager_ops = {
- .dpms = drm_hdmi_dpms,
- .apply = drm_hdmi_apply,
- .enable_vblank = drm_hdmi_enable_vblank,
- .disable_vblank = drm_hdmi_disable_vblank,
- .mode_fixup = drm_hdmi_mode_fixup,
- .mode_set = drm_hdmi_mode_set,
- .get_max_resol = drm_hdmi_get_max_resol,
- .commit = drm_hdmi_commit,
-};
-
-static void drm_mixer_mode_set(struct device *subdrv_dev,
- struct exynos_drm_overlay *overlay)
-{
- struct drm_hdmi_context *ctx = to_context(subdrv_dev);
-
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- if (mixer_ops && mixer_ops->win_mode_set)
- mixer_ops->win_mode_set(ctx->mixer_ctx->ctx, overlay);
-}
-
-static void drm_mixer_page_flip(struct device *subdrv_dev,
- struct exynos_drm_overlay *overlay)
-{
- struct drm_hdmi_context *ctx = to_context(subdrv_dev);
-
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- if (mixer_ops && mixer_ops->win_page_flip)
- mixer_ops->win_page_flip(ctx->mixer_ctx->ctx, overlay);
-}
-
-static void drm_mixer_commit(struct device *subdrv_dev, int zpos)
-{
- struct drm_hdmi_context *ctx = to_context(subdrv_dev);
- int win = (zpos == DEFAULT_ZPOS) ? MIXER_DEFAULT_WIN : zpos;
-
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- 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_disable(struct device *subdrv_dev, int zpos)
-{
- struct drm_hdmi_context *ctx = to_context(subdrv_dev);
- int win = (zpos == DEFAULT_ZPOS) ? MIXER_DEFAULT_WIN : zpos;
-
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- 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_overlay_ops drm_hdmi_overlay_ops = {
- .mode_set = drm_mixer_mode_set,
- .page_flip = drm_mixer_page_flip,
- .commit = drm_mixer_commit,
- .disable = drm_mixer_disable,
-};
-
-static struct exynos_drm_manager hdmi_manager = {
- .pipe = -1,
- .ops = &drm_hdmi_manager_ops,
- .overlay_ops = &drm_hdmi_overlay_ops,
- .display_ops = &drm_hdmi_display_ops,
-};
-
-static int hdmi_subdrv_probe(struct drm_device *drm_dev,
- struct device *dev)
-{
- struct exynos_drm_subdrv *subdrv = to_subdrv(dev);
- struct drm_hdmi_context *ctx;
-
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- if (!hdmi_ctx) {
- DRM_DEBUG_KMS("hdmi context is null.\n");
- return -EFAULT;
- }
-
- if (!mixer_ctx) {
- DRM_DEBUG_KMS("mixer context is null.\n");
- return -EFAULT;
- }
-
- ctx = get_ctx_from_subdrv(subdrv);
-
- ctx->hdmi_ctx = hdmi_ctx;
- ctx->mixer_ctx = mixer_ctx;
-
- ctx->hdmi_ctx->drm_dev = drm_dev;
-
- ctx->mixer_ctx->drm_dev = drm_dev;
-
- return 0;
-}
-
-static int __devinit exynos_drm_hdmi_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct exynos_drm_subdrv *subdrv;
- struct drm_hdmi_context *ctx;
-
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
- if (!ctx) {
- DRM_LOG_KMS("failed to alloc common hdmi context.\n");
- return -ENOMEM;
- }
-
- subdrv = &ctx->subdrv;
-
- subdrv->dev = dev;
- subdrv->manager = &hdmi_manager;
- subdrv->probe = hdmi_subdrv_probe;
-
- platform_set_drvdata(pdev, subdrv);
-
- exynos_drm_subdrv_register(subdrv);
-
- return 0;
-}
-
-static int hdmi_runtime_suspend(struct device *dev)
-{
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- return 0;
-}
-
-static int hdmi_runtime_resume(struct device *dev)
-{
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- return 0;
-}
-
-static const struct dev_pm_ops hdmi_pm_ops = {
- .runtime_suspend = hdmi_runtime_suspend,
- .runtime_resume = hdmi_runtime_resume,
-};
-
-static int __devexit exynos_drm_hdmi_remove(struct platform_device *pdev)
-{
- struct drm_hdmi_context *ctx = platform_get_drvdata(pdev);
-
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- exynos_drm_subdrv_unregister(&ctx->subdrv);
- kfree(ctx);
-
- return 0;
-}
-
-struct platform_driver exynos_drm_common_hdmi_driver = {
- .probe = exynos_drm_hdmi_probe,
- .remove = __devexit_p(exynos_drm_hdmi_remove),
- .driver = {
- .name = "exynos-drm-hdmi",
- .owner = THIS_MODULE,
- .pm = &hdmi_pm_ops,
- },
-};
+++ /dev/null
-/* exynos_drm_hdmi.h
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd.
- * Authoer: Inki Dae <inki.dae@samsung.com>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice (including the next
- * paragraph) shall be included in all copies or substantial portions of the
- * Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#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.
- * @ctx: pointer to the context of specific device driver.
- * this context should be hdmi_context or mixer_context.
- */
-struct exynos_drm_hdmi_context {
- struct drm_device *drm_dev;
- void *ctx;
-};
-
-struct exynos_hdmi_ops {
- /* display */
- bool (*is_connected)(void *ctx);
- int (*get_edid)(void *ctx, struct drm_connector *connector,
- u8 *edid, int len);
- int (*check_timing)(void *ctx, void *timing);
- int (*power_on)(void *ctx, int mode);
-
- /* manager */
- void (*mode_fixup)(void *ctx, struct drm_connector *connector,
- struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode);
- void (*mode_set)(void *ctx, void *mode);
- void (*get_max_resol)(void *ctx, unsigned int *width,
- unsigned int *height);
- void (*commit)(void *ctx);
- void (*dpms)(void *ctx, int mode);
-};
-
-struct exynos_mixer_ops {
- /* manager */
- int (*enable_vblank)(void *ctx, int pipe);
- void (*disable_vblank)(void *ctx);
- void (*dpms)(void *ctx, int mode);
- int (*power_on)(void *ctx, int mode);
-
- /* overlay */
- void (*win_mode_set)(void *ctx, struct exynos_drm_overlay *overlay);
- void (*win_page_flip)(void *ctx, struct exynos_drm_overlay *overlay);
- void (*win_commit)(void *ctx, int zpos);
- void (*win_disable)(void *ctx, int zpos);
-};
-
-enum exynos_mixer_mode_type {
- EXYNOS_MIXER_MODE_INVALID,
- EXYNOS_MIXER_MODE_SD_NTSC,
- EXYNOS_MIXER_MODE_SD_PAL,
- EXYNOS_MIXER_MODE_HD_720,
- EXYNOS_MIXER_MODE_HD_1080,
-};
-
-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);
-
-enum exynos_mixer_mode_type exynos_mixer_get_mode_type(int width, int height);
-
-#endif
#include "exynos_drm_drv.h"
#include "exynos_drm_crtc.h"
#include "exynos_drm_encoder.h"
+#include "exynos_drm_display.h"
/* vidi has totally three virtual windows. */
#define WINDOWS_NR 3
};
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;
bool suspended;
struct work_struct work;
struct mutex lock;
+ int pipe;
};
static const char fake_edid_info[] = {
static void vidi_fake_vblank_handler(struct work_struct *work);
-static bool vidi_display_is_connected(struct device *dev)
+static bool vidi_display_is_connected(void *ctx)
{
- struct vidi_context *ctx = get_vidi_context(dev);
+ struct vidi_context *vidi_ctx = ctx;
DRM_DEBUG_KMS("%s\n", __FILE__);
* connection request would come from user side
* to do hotplug through specific ioctl.
*/
- return ctx->connected ? true : false;
+ return vidi_ctx->connected ? true : false;
}
-static int vidi_get_edid(struct device *dev, struct drm_connector *connector,
+static int vidi_get_edid(void *ctx, struct drm_connector *connector,
u8 *edid, int len)
{
- struct vidi_context *ctx = get_vidi_context(dev);
+ struct vidi_context *vidi_ctx = ctx;
struct edid *raw_edid;
DRM_DEBUG_KMS("%s\n", __FILE__);
/*
* the edid data comes from user side and it would be set
- * to ctx->raw_edid through specific ioctl.
+ * to vidi_ctx->raw_edid through specific ioctl.
*/
- if (!ctx->raw_edid) {
+ if (!vidi_ctx->raw_edid) {
DRM_DEBUG_KMS("raw_edid is null.\n");
return -EFAULT;
}
return -ENOMEM;
}
- memcpy(raw_edid, ctx->raw_edid, min((1 + ctx->raw_edid->extensions)
- * EDID_LENGTH, len));
+ memcpy(raw_edid, vidi_ctx->raw_edid,
+ min((1 + vidi_ctx->raw_edid->extensions) * EDID_LENGTH, len));
/* attach the edid data to connector. */
connector->display_info.raw_edid = (char *)raw_edid;
- memcpy(edid, ctx->raw_edid, min((1 + ctx->raw_edid->extensions)
- * EDID_LENGTH, len));
+ memcpy(edid, vidi_ctx->raw_edid,
+ min((1 + vidi_ctx->raw_edid->extensions) * EDID_LENGTH, len));
return 0;
}
-static void *vidi_get_panel(struct device *dev)
+static struct exynos_drm_panel_info *vidi_get_panel(void *ctx)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
return NULL;
}
-static int vidi_check_timing(struct device *dev, void *timing)
+static int vidi_check_timing(void *ctx, void *timing)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
return 0;
}
-static int vidi_display_power_on(struct device *dev, int mode)
+static int vidi_display_power(void *ctx, int mode)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
return 0;
}
-static struct exynos_drm_display_ops vidi_display_ops = {
- .type = EXYNOS_DISPLAY_TYPE_VIDI,
+static void vidi_commit(void *ctx)
+{
+ struct vidi_context *vidi_ctx = ctx;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (vidi_ctx->suspended)
+ return;
+}
+
+static struct exynos_panel_ops vidi_panel_ops = {
.is_connected = vidi_display_is_connected,
.get_edid = vidi_get_edid,
- .get_panel = vidi_get_panel,
.check_timing = vidi_check_timing,
- .power_on = vidi_display_power_on,
+ .power = vidi_display_power,
+ .commit = vidi_commit,
};
-static void vidi_dpms(struct device *subdrv_dev, int mode)
+static int vidi_dpms(void *ctx, int mode)
{
- struct vidi_context *ctx = get_vidi_context(subdrv_dev);
+ struct vidi_context *vidi_ctx = ctx;
DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode);
- mutex_lock(&ctx->lock);
+ mutex_lock(&vidi_ctx->lock);
switch (mode) {
case DRM_MODE_DPMS_ON:
break;
}
- mutex_unlock(&ctx->lock);
-}
-
-static void vidi_apply(struct device *subdrv_dev)
-{
- struct vidi_context *ctx = get_vidi_context(subdrv_dev);
- struct exynos_drm_manager *mgr = ctx->subdrv.manager;
- struct exynos_drm_manager_ops *mgr_ops = mgr->ops;
- struct exynos_drm_overlay_ops *ovl_ops = mgr->overlay_ops;
- struct vidi_win_data *win_data;
- int i;
-
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- for (i = 0; i < WINDOWS_NR; i++) {
- win_data = &ctx->win_data[i];
- if (win_data->enabled && (ovl_ops && ovl_ops->commit))
- ovl_ops->commit(subdrv_dev, i);
- }
-
- if (mgr_ops && mgr_ops->commit)
- mgr_ops->commit(subdrv_dev);
-}
-
-static void vidi_commit(struct device *dev)
-{
- struct vidi_context *ctx = get_vidi_context(dev);
-
- DRM_DEBUG_KMS("%s\n", __FILE__);
+ mutex_unlock(&vidi_ctx->lock);
- if (ctx->suspended)
- return;
+ return 0;
}
-static int vidi_enable_vblank(struct device *dev)
+static int vidi_enable_vblank(void *ctx, int pipe)
{
- struct vidi_context *ctx = get_vidi_context(dev);
+ struct vidi_context *vidi_ctx = ctx;
DRM_DEBUG_KMS("%s\n", __FILE__);
- if (ctx->suspended)
+ if (vidi_ctx->suspended)
return -EPERM;
- if (!test_and_set_bit(0, &ctx->irq_flags))
- ctx->vblank_on = true;
+ if (!test_and_set_bit(0, &vidi_ctx->irq_flags))
+ vidi_ctx->vblank_on = true;
+
+ vidi_ctx->pipe = pipe;
return 0;
}
-static void vidi_disable_vblank(struct device *dev)
+static void vidi_disable_vblank(void *ctx)
{
- struct vidi_context *ctx = get_vidi_context(dev);
+ struct vidi_context *vidi_ctx = ctx;
DRM_DEBUG_KMS("%s\n", __FILE__);
- if (ctx->suspended)
+ if (vidi_ctx->suspended)
return;
- if (test_and_clear_bit(0, &ctx->irq_flags))
- ctx->vblank_on = false;
+ if (test_and_clear_bit(0, &vidi_ctx->irq_flags))
+ vidi_ctx->vblank_on = false;
}
-static struct exynos_drm_manager_ops vidi_manager_ops = {
- .dpms = vidi_dpms,
- .apply = vidi_apply,
- .commit = vidi_commit,
- .enable_vblank = vidi_enable_vblank,
- .disable_vblank = vidi_disable_vblank,
-};
-
-static void vidi_win_mode_set(struct device *dev,
- struct exynos_drm_overlay *overlay)
+static void vidi_win_mode_set(void *ctx, struct exynos_drm_overlay *overlay)
{
- struct vidi_context *ctx = get_vidi_context(dev);
+ struct vidi_context *vidi_ctx = ctx;
struct vidi_win_data *win_data;
int win;
unsigned long offset;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (!overlay) {
- dev_err(dev, "overlay is NULL\n");
+ DRM_ERROR("overlay is NULL\n");
return;
}
win = overlay->zpos;
if (win == DEFAULT_ZPOS)
- win = ctx->default_win;
+ win = vidi_ctx->default_win;
if (win < 0 || win > WINDOWS_NR)
return;
DRM_DEBUG_KMS("offset = 0x%lx, fb_pitch = %x\n", offset,
overlay->fb_pitch);
- win_data = &ctx->win_data[win];
+ win_data = &vidi_ctx->win_data[win];
win_data->offset_x = overlay->crtc_x;
win_data->offset_y = overlay->crtc_y;
overlay->fb_width, overlay->crtc_width);
}
-static void vidi_win_commit(struct device *dev, int zpos)
+static void vidi_win_commit(void *ctx, int zpos)
{
- struct vidi_context *ctx = get_vidi_context(dev);
+ struct vidi_context *vidi_ctx = ctx;
struct vidi_win_data *win_data;
int win = zpos;
DRM_DEBUG_KMS("%s\n", __FILE__);
- if (ctx->suspended)
+ if (vidi_ctx->suspended)
return;
if (win == DEFAULT_ZPOS)
- win = ctx->default_win;
+ win = vidi_ctx->default_win;
if (win < 0 || win > WINDOWS_NR)
return;
- win_data = &ctx->win_data[win];
+ win_data = &vidi_ctx->win_data[win];
win_data->enabled = true;
DRM_DEBUG_KMS("dma_addr = 0x%x\n", win_data->dma_addr);
- if (ctx->vblank_on)
- schedule_work(&ctx->work);
+ if (vidi_ctx->vblank_on)
+ schedule_work(&vidi_ctx->work);
}
-static void vidi_win_disable(struct device *dev, int zpos)
+static void vidi_apply(void *ctx)
{
- struct vidi_context *ctx = get_vidi_context(dev);
+ struct vidi_context *vidi_ctx = ctx;
+ struct vidi_win_data *win_data;
+ int i;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ for (i = 0; i < WINDOWS_NR; i++) {
+ win_data = &vidi_ctx->win_data[i];
+ if (win_data->enabled)
+ vidi_win_commit(ctx, i);
+ }
+
+ vidi_commit(ctx);
+}
+
+static void vidi_win_disable(void *ctx, int zpos)
+{
+ struct vidi_context *vidi_ctx = ctx;
struct vidi_win_data *win_data;
int win = zpos;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (win == DEFAULT_ZPOS)
- win = ctx->default_win;
+ win = vidi_ctx->default_win;
if (win < 0 || win > WINDOWS_NR)
return;
- win_data = &ctx->win_data[win];
+ win_data = &vidi_ctx->win_data[win];
win_data->enabled = false;
/* TODO. */
}
-static struct exynos_drm_overlay_ops vidi_overlay_ops = {
- .mode_set = vidi_win_mode_set,
- .commit = vidi_win_commit,
- .disable = vidi_win_disable,
-};
-
-static struct exynos_drm_manager vidi_manager = {
- .pipe = -1,
- .ops = &vidi_manager_ops,
- .overlay_ops = &vidi_overlay_ops,
- .display_ops = &vidi_display_ops,
-};
-
static void vidi_fake_vblank_handler(struct work_struct *work)
{
- struct vidi_context *ctx = container_of(work, struct vidi_context,
+ struct vidi_context *vidi_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 (vidi_ctx->pipe < 0)
return;
/* refresh rate is about 50Hz. */
usleep_range(16000, 20000);
- drm_handle_vblank(subdrv->drm_dev, manager->pipe);
- exynos_drm_crtc_finish_pageflip(subdrv->drm_dev, manager->pipe);
+ drm_handle_vblank(vidi_ctx->drm_dev, vidi_ctx->pipe);
+ exynos_drm_crtc_finish_pageflip(vidi_ctx->drm_dev, vidi_ctx->pipe);
}
-static int vidi_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
+static int vidi_subdrv_probe(void *ctx, struct drm_device *drm_dev)
{
+ struct vidi_context *vidi_ctx = ctx;
+
DRM_DEBUG_KMS("%s\n", __FILE__);
/*
*/
drm_dev->vblank_disable_allowed = 1;
+ vidi_ctx->drm_dev = drm_dev;
+
return 0;
}
-static void vidi_subdrv_remove(struct drm_device *drm_dev)
-{
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- /* TODO. */
-}
+static struct exynos_controller_ops vidi_controller_ops = {
+ .subdrv_probe = vidi_subdrv_probe,
+ .get_panel = vidi_get_panel,
+ .dpms = vidi_dpms,
+ .apply = vidi_apply,
+ .win_commit = vidi_win_commit,
+ .enable_vblank = vidi_enable_vblank,
+ .disable_vblank = vidi_disable_vblank,
+ .mode_set = vidi_win_mode_set,
+ .win_disable = vidi_win_disable,
+};
-static int vidi_power_on(struct vidi_context *ctx, bool enable)
+static int vidi_power_on(struct vidi_context *vidi_ctx, bool enable)
{
- struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
- struct device *dev = subdrv->dev;
-
DRM_DEBUG_KMS("%s\n", __FILE__);
if (enable != false && enable != true)
return -EINVAL;
if (enable) {
- ctx->suspended = false;
+ vidi_ctx->suspended = false;
/* if vblank was enabled status, enable it again. */
- if (test_and_clear_bit(0, &ctx->irq_flags))
- vidi_enable_vblank(dev);
+ if (test_and_clear_bit(0, &vidi_ctx->irq_flags))
+ vidi_enable_vblank(vidi_ctx, vidi_ctx->pipe);
- vidi_apply(dev);
+ vidi_apply(vidi_ctx);
} else {
- ctx->suspended = true;
+ vidi_ctx->suspended = true;
}
return 0;
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;
}
{
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;
DRM_DEBUG_KMS("%s\n", __FILE__);
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 = get_vidi_context(manager->dev);
+ if (display->display_type == EXYNOS_DRM_DISPLAY_TYPE_VIDI) {
+ ctx = display->controller_ctx;
break;
}
}
ctx->raw_edid = (struct edid *)vidi->edid;
ctx->connected = vidi->connection;
- drm_helper_hpd_irq_event(ctx->subdrv.drm_dev);
+ drm_helper_hpd_irq_event(ctx->drm_dev);
return 0;
}
static int __devinit vidi_probe(struct platform_device *pdev)
{
- struct device *dev = &pdev->dev;
struct vidi_context *ctx;
- struct exynos_drm_subdrv *subdrv;
int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
/* for test */
ctx->raw_edid = (struct edid *)fake_edid_info;
- subdrv = &ctx->subdrv;
- subdrv->dev = dev;
- subdrv->manager = &vidi_manager;
- subdrv->probe = vidi_subdrv_probe;
- subdrv->remove = vidi_subdrv_remove;
-
mutex_init(&ctx->lock);
platform_set_drvdata(pdev, ctx);
if (ret < 0)
DRM_INFO("failed to create connection sysfs.\n");
- exynos_drm_subdrv_register(subdrv);
+ exynos_display_attach_controller(EXYNOS_DRM_DISPLAY_TYPE_VIDI,
+ &vidi_controller_ops, ctx);
+ exynos_display_attach_panel(EXYNOS_DRM_DISPLAY_TYPE_VIDI,
+ &vidi_panel_ops, ctx);
+
return 0;
}
DRM_DEBUG_KMS("%s\n", __FILE__);
- exynos_drm_subdrv_unregister(&ctx->subdrv);
-
kfree(ctx);
return 0;
#include <drm/exynos_drm.h>
#include "exynos_drm_drv.h"
-#include "exynos_drm_hdmi.h"
+#include "exynos_drm_display.h"
#include "exynos_hdmi.h"
int cur_conf;
struct hdmi_resources res;
- void *parent_ctx;
};
/* HDMI Version 1.3 */
hdata->enabled = true;
}
+static void hdmi_apply(void *ctx)
+{
+ hdmi_commit(ctx);
+}
+
static int hdmi_power_on(void *ctx, int mode)
{
struct hdmi_context *hdata = ctx;
return 0;
}
-static struct exynos_hdmi_ops hdmi_ops = {
+static int hdmi_subdrv_probe(void *ctx, struct drm_device *drm_dev)
+{
+ struct hdmi_context *hdata = ctx;
+
+ hdata->drm_dev = drm_dev;
+
+ return 0;
+}
+
+static struct exynos_panel_ops hdmi_ops = {
/* display */
+ .subdrv_probe = hdmi_subdrv_probe,
.is_connected = hdmi_is_connected,
.get_edid = hdmi_get_edid,
.check_timing = hdmi_check_timing,
- .power_on = hdmi_power_on,
+ .power = hdmi_power_on,
/* manager */
.mode_fixup = hdmi_mode_fixup,
.mode_set = hdmi_mode_set,
.commit = hdmi_commit,
+ .apply = hdmi_apply,
};
/*
{
struct hdmi_context *hdata =
container_of(work, struct hdmi_context, hotplug_work);
- struct exynos_drm_hdmi_context *ctx =
- (struct exynos_drm_hdmi_context *)hdata->parent_ctx;
- drm_helper_hpd_irq_event(ctx->drm_dev);
+ drm_helper_hpd_irq_event(hdata->drm_dev);
}
static irqreturn_t hdmi_irq_handler(int irq, void *arg)
{
- struct exynos_drm_hdmi_context *ctx = arg;
- struct hdmi_context *hdata = ctx->ctx;
+ struct hdmi_context *hdata = arg;
u32 intc_flag;
if (hdata->is_hdmi_powered_on) {
intc_flag = hdmi_reg_read(hdata, HDMI_INTC_FLAG);
}
}
- if (ctx->drm_dev && hdata->hpd_handle)
+ if (hdata->drm_dev && hdata->hpd_handle)
queue_work(hdata->wq, &hdata->hotplug_work);
return IRQ_HANDLED;
#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 hdmi_context *hdata = get_hdmi_context(dev);
DRM_DEBUG_KMS("[hdmi] sleep suspend - start\n");
if (pm_runtime_suspended(dev)) {
}
static int hdmi_resume(struct device *dev)
{
- struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
- struct hdmi_context *hdata = ctx->ctx;
+ struct hdmi_context *hdata = get_hdmi_context(dev);
DRM_DEBUG_KMS("[hdmi] sleep resume - start\n");
#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 hdmi_context *hdata = get_hdmi_context(dev);
DRM_DEBUG_KMS("[hdmi] runtime suspend - start\n");
hdmi_resource_poweroff(hdata);
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 hdmi_context *hdata = get_hdmi_context(dev);
DRM_DEBUG_KMS("[hdmi] runtime resume - start\n");
int hdmi_register_audio_device(struct platform_device *pdev)
{
- struct exynos_drm_hdmi_context *ctx = platform_get_drvdata(pdev);
- struct hdmi_context *hdata = ctx->ctx;
+ struct hdmi_context *hdata = platform_get_drvdata(pdev);
struct platform_device *audio_dev;
int ret;
static int __devinit hdmi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct exynos_drm_hdmi_context *drm_hdmi_ctx;
struct hdmi_context *hdata;
struct exynos_drm_hdmi_pdata *pdata;
struct resource *res;
return -EINVAL;
}
- drm_hdmi_ctx = kzalloc(sizeof(*drm_hdmi_ctx), GFP_KERNEL);
- if (!drm_hdmi_ctx) {
- DRM_ERROR("failed to allocate common hdmi context.\n");
- return -ENOMEM;
- }
-
hdata = kzalloc(sizeof(struct hdmi_context), GFP_KERNEL);
if (!hdata) {
DRM_ERROR("out of memory\n");
- kfree(drm_hdmi_ctx);
return -ENOMEM;
}
- drm_hdmi_ctx->ctx = (void *)hdata;
- hdata->parent_ctx = (void *)drm_hdmi_ctx;
-
- platform_set_drvdata(pdev, drm_hdmi_ctx);
+ platform_set_drvdata(pdev, hdata);
hdata->is_v13 = pdata->is_v13;
hdata->default_win = pdata->default_win;
INIT_WORK(&hdata->hotplug_work, hdmi_hotplug_func);
ret = request_irq(hdata->internal_irq, hdmi_irq_handler,
- IRQF_SHARED, "int_hdmi", hdata->parent_ctx);
+ IRQF_SHARED, "int_hdmi", hdata);
if (ret) {
DRM_ERROR("request int interrupt failed.\n");
goto err_workqueue;
ret = request_irq(hdata->external_irq, hdmi_irq_handler,
IRQ_TYPE_EDGE_BOTH | IRQF_SHARED, "ext_hdmi",
- hdata->parent_ctx);
+ hdata);
if (ret) {
DRM_ERROR("request ext interrupt failed.\n");
goto err_int_irq;
}
}
- /* 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_resource_poweron(hdata);
pm_runtime_enable(dev);
} else {
pm_runtime_get_sync(dev);
}
+
+ exynos_display_attach_panel(EXYNOS_DRM_DISPLAY_TYPE_MIXER, &hdmi_ops,
+ hdata);
+
return 0;
err_ext_irq:
hdmi_resources_cleanup(hdata);
err_data:
kfree(hdata);
- kfree(drm_hdmi_ctx);
return ret;
}
static int __devexit hdmi_remove(struct platform_device *pdev)
{
- struct exynos_drm_hdmi_context *ctx = platform_get_drvdata(pdev);
- struct hdmi_context *hdata = ctx->ctx;
+ struct hdmi_context *hdata = platform_get_drvdata(pdev);
struct hdmi_resources *res = &hdata->res;
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
extern struct i2c_driver hdmiphy_driver;
extern struct i2c_driver ddc_driver;
+enum exynos_mixer_mode_type {
+ EXYNOS_MIXER_MODE_INVALID,
+ EXYNOS_MIXER_MODE_SD_NTSC,
+ EXYNOS_MIXER_MODE_SD_PAL,
+ EXYNOS_MIXER_MODE_HD_720,
+ EXYNOS_MIXER_MODE_HD_1080,
+};
+
+enum exynos_mixer_mode_type exynos_mixer_get_mode_type(int width, int height);
+
#endif
#include "exynos_drm_drv.h"
#include "exynos_drm_crtc.h"
-#include "exynos_drm_hdmi.h"
+#include "exynos_drm_display.h"
+
+#include "exynos_hdmi.h"
#include <plat/map-base.h>
#ifdef CONFIG_EXYNOS_IOMMU
#define get_mixer_context(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;
void __iomem *vaddr;
struct mixer_context {
struct device *dev;
+ struct drm_device *drm_dev;
unsigned int irq;
int pipe;
bool interlace;
bool is_mixer_powered_on;
+ bool enabled[MIXER_WIN_NR];
struct mixer_resources mixer_res;
struct hdmi_win_data win_data[MIXER_WIN_NR];
writel(val, res->mixer_regs + reg_id);
}
+enum exynos_mixer_mode_type exynos_mixer_get_mode_type(int width, int height)
+{
+ if (width >= 464 && width <= 720 && height <= 480)
+ return EXYNOS_MIXER_MODE_SD_NTSC;
+ else if (width >= 464 && width <= 720 && height <= 576)
+ return EXYNOS_MIXER_MODE_SD_PAL;
+ else if (width >= 1024 && width <= 1280 && height <= 720)
+ return EXYNOS_MIXER_MODE_HD_720;
+ else if ((width == 1440 && height == 900) ||
+ (width == 800 && height == 600) ||
+ (width >= 1664 && width <= 1920 && height <= 1080))
+ return EXYNOS_MIXER_MODE_HD_1080;
+ else
+ return EXYNOS_MIXER_MODE_INVALID;
+}
+
static void mixer_regs_dump(struct mixer_context *mctx)
{
#define DUMPREG(reg_id) \
spin_lock_irqsave(&res->reg_slock, flags);
mixer_vsync_set_update(mctx, false);
+ mctx->enabled[win] = true;
+
/* interlace or progressive scan mode */
val = (mctx->interlace ? ~0 : 0);
vp_reg_writemask(res, VP_MODE, val, VP_MODE_LINE_SKIP);
spin_lock_irqsave(&res->reg_slock, flags);
mixer_vsync_set_update(mctx, false);
+ mctx->enabled[win] = true;
+
/* setup format */
mixer_reg_writemask(res, MXR_GRAPHIC_CFG(win),
MXR_GRP_CFG_FORMAT_VAL(fmt), MXR_GRP_CFG_FORMAT_MASK);
win_data->scan_flags = overlay->scan_flag;
}
-static void mixer_win_page_flip(void *ctx,
- struct exynos_drm_overlay *overlay)
+static void mixer_win_page_flip(void *ctx, struct exynos_drm_overlay *overlay)
{
struct mixer_context *mixer_ctx = ctx;
struct hdmi_win_data *win_data;
mixer_graph_buffer(mctx, win);
}
+static void mixer_apply(void *ctx)
+{
+ struct mixer_context *mctx = ctx;
+ int i;
+
+ for (i = 0; i < MIXER_WIN_NR; i++) {
+ if (!mctx->enabled[i])
+ continue;
+
+ mixer_win_commit(ctx, i);
+ }
+}
+
static void mixer_win_disable(void *ctx, int zpos)
{
struct mixer_context *mctx = ctx;
spin_lock_irqsave(&res->reg_slock, flags);
mixer_vsync_set_update(mctx, false);
+ mctx->enabled[win] = false;
mixer_cfg_layer(mctx, win, false);
mixer_vsync_set_update(mctx, true);
/* for pageflip event */
static irqreturn_t mixer_irq_handler(int irq, void *arg)
{
- struct exynos_drm_hdmi_context *drm_hdmi_ctx = arg;
- struct mixer_context *mctx = drm_hdmi_ctx->ctx;
+ struct mixer_context *mctx = arg;
struct mixer_resources *res = &mctx->mixer_res;
u32 val, base, shadow;
int i;
goto out;
}
- drm_handle_vblank(drm_hdmi_ctx->drm_dev, mctx->pipe);
+ drm_handle_vblank(mctx->drm_dev, mctx->pipe);
/* Bail out if a layer update is pending */
if (mixer_get_layer_update_count(mctx))
for (i = 0; i < MIXER_WIN_NR; i++)
mctx->win_data[i].updated = false;
- exynos_drm_crtc_finish_pageflip(drm_hdmi_ctx->drm_dev,
- mctx->pipe);
+ exynos_drm_crtc_finish_pageflip(mctx->drm_dev, mctx->pipe);
if (mctx->event_flags & MXR_EVENT_VSYNC) {
DRM_DEBUG_KMS("mctx->event_flags & MXR_EVENT_VSYNC");
#ifdef CONFIG_PM_SLEEP
static int mixer_resume(struct device *dev)
{
- struct exynos_drm_hdmi_context *ctx = get_mixer_context(dev);
- struct mixer_context *mctx = ctx->ctx;
+ struct mixer_context *mctx = get_mixer_context(dev);
DRM_DEBUG_KMS("[mixer] sleep resume - start\n");
}
static int mixer_suspend(struct device *dev)
{
- struct exynos_drm_hdmi_context *ctx = get_mixer_context(dev);
- struct mixer_context *mctx = ctx->ctx;
+ struct mixer_context *mctx = get_mixer_context(dev);
DRM_DEBUG_KMS("[mixer] suspend - start\n");
if (pm_runtime_suspended(dev)) {
#ifdef CONFIG_PM_RUNTIME
static int mixer_runtime_resume(struct device *dev)
{
- struct exynos_drm_hdmi_context *ctx = get_mixer_context(dev);
- struct mixer_context *mctx = ctx->ctx;
+ struct mixer_context *mctx = get_mixer_context(dev);
DRM_DEBUG_KMS("[mixer] runtime resume - start\n");
static int mixer_runtime_suspend(struct device *dev)
{
- struct exynos_drm_hdmi_context *ctx = get_mixer_context(dev);
- struct mixer_context *mctx = ctx->ctx;
+ struct mixer_context *mctx = get_mixer_context(dev);
DRM_DEBUG_KMS("[mixer] runtime suspend - start\n");
#endif
-static int mixer_power_on(void *ctx, int mode)
+static int mixer_power(void *ctx, int mode)
{
struct mixer_context *mctx = ctx;
return 0;
}
-static struct exynos_mixer_ops mixer_ops = {
+static int mixer_subdrv_probe(void *ctx, struct drm_device *drm_dev)
+{
+ struct mixer_context *mctx = ctx;
+
+ mctx->drm_dev = drm_dev;
+
+ return 0;
+}
+
+static struct exynos_controller_ops mixer_ops = {
/* manager */
+ .subdrv_probe = mixer_subdrv_probe,
.enable_vblank = mixer_enable_vblank,
.disable_vblank = mixer_disable_vblank,
- .power_on = mixer_power_on,
+ .power = mixer_power,
/* overlay */
- .win_mode_set = mixer_win_mode_set,
- .win_page_flip = mixer_win_page_flip,
+ .mode_set = mixer_win_mode_set,
+ .page_flip = mixer_win_page_flip,
+ .apply = mixer_apply,
.win_commit = mixer_win_commit,
.win_disable = mixer_win_disable,
};
#endif
static int __devinit mixer_resources_init_exynos(
- struct exynos_drm_hdmi_context *ctx,
+ struct mixer_context *mctx,
struct platform_device *pdev,
int is_exynos5)
{
- struct mixer_context *mctx = ctx->ctx;
struct device *dev = &pdev->dev;
struct mixer_resources *mixer_res = &mctx->mixer_res;
struct resource *res;
}
}
- ret = request_irq(res->start, mixer_irq_handler, 0, "drm_mixer", ctx);
+ ret = request_irq(res->start, mixer_irq_handler, 0, "drm_mixer", mctx);
if (ret) {
dev_err(dev, "request interrupt failed.\n");
goto fail_mixer_regs;
return ret;
}
-static void mixer_resources_cleanup(struct mixer_context *mctx)
+static void mixer_resources_cleanup(struct device *dev,
+ struct mixer_context *mctx)
{
struct mixer_resources *res = &mctx->mixer_res;
disable_irq(res->irq);
- free_irq(res->irq, mctx);
+ free_irq(res->irq, dev);
iounmap(res->vp_regs);
iounmap(res->mixer_regs);
static int __devinit mixer_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct exynos_drm_hdmi_context *drm_hdmi_ctx;
struct exynos_drm_hdmi_pdata *pdata;
struct mixer_context *mctx;
int ret;
dev_info(dev, "probe start\n");
- drm_hdmi_ctx = kzalloc(sizeof(*drm_hdmi_ctx), GFP_KERNEL);
- if (!drm_hdmi_ctx) {
- DRM_ERROR("failed to allocate common hdmi context.\n");
- return -ENOMEM;
- }
-
mctx = kzalloc(sizeof(*mctx), GFP_KERNEL);
if (!mctx) {
DRM_ERROR("failed to alloc mixer context.\n");
- kfree(drm_hdmi_ctx);
return -ENOMEM;
}
-
mctx->dev = &pdev->dev;
mctx->pipe = -1;
- drm_hdmi_ctx->ctx = (void *)mctx;
- platform_set_drvdata(pdev, drm_hdmi_ctx);
+ platform_set_drvdata(pdev, mctx);
/* Get from Platform soc deatils */
pdata = pdev->dev.platform_data;
/* acquire resources: regs, irqs, clocks */
- ret = mixer_resources_init_exynos(drm_hdmi_ctx, pdev,pdata->is_soc_exynos5);
+ ret = mixer_resources_init_exynos(mctx, pdev, pdata->is_soc_exynos5);
if (ret)
goto fail;
- /* 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);
mctx->is_mixer_powered_on = false;
pm_runtime_enable(dev);
+ exynos_display_attach_controller(EXYNOS_DRM_DISPLAY_TYPE_MIXER,
+ &mixer_ops, mctx);
+
return 0;
static int mixer_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct exynos_drm_hdmi_context *drm_hdmi_ctx =
- platform_get_drvdata(pdev);
- struct mixer_context *mctx = drm_hdmi_ctx->ctx;
+ struct mixer_context *mctx = platform_get_drvdata(pdev);
dev_info(dev, "remove successful\n");
mixer_resource_poweroff(mctx);
- mixer_resources_cleanup(mctx);
+ mixer_resources_cleanup(dev, mctx);
kfree(mctx);
- kfree(drm_hdmi_ctx);
return 0;
}