Commit b33a51e4 authored by Dave Airlie's avatar Dave Airlie

Merge branch 'exynos-drm-next' of...

Merge branch 'exynos-drm-next' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos into drm-next

Summary:
- Resolve probe order and deferred probe issue with component framework
  support.
- Resolve hdmi dt broken issue.
  . HDMI DT support, which was broken since CCF (common clock framework)
    support, and considring legacy dt binding.
- Consolidate HDMI part.
  . APB based phy support for Exynos5420 and later, and fixups related
    to power on/off sequence.
- Consolidate IPP part.
  . Mostly bug fixups and code cleanups.
- Trivial fixups and code cleanups.

* 'exynos-drm-next' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos: (64 commits)
  drm/exynos: consider deferred probe case
  drm/exynos: remove unnecessary exynos_hdmi.h file
  drm/exynos/fimd: allow multiplatform configuration
  drm/exynos: add hdmiphy power on/off sequence
  drm/exynos: ipp: remove description of non-existing field
  drm/exynos: ipp: update comment for struct drm_ipp_buf_info
  drm/exynos: ipp: rearrange c_node->event_lock using routine
  drm/exynos: ipp: rearrange c_node->mem_lock using routines
  drm/exynos: ipp: add ipp_remove_id()
  drm/exynos: ipp: add cmd_lock for cmd_list
  drm/exynos: ipp: rename cmd_lock to lock
  drm/exynos: ipp: remove duplicated setting
  drm/exynos: ipp: remove usless list_empty() functions
  drm/exynos: Use PTR_ERR_OR_ZERO in exynos_dp_core.c
  drm/exynos: remove hardware overlays disable from fimd probe
  drm/exynos: Fix checkpatch warning in exynos_dp_reg.c
  drm/exynos: add fimd dependency to fimd related encoders
  drm/exynos: remove redundant mutex_unlock
  drm/exynos/fimc: simplify and rename fimc_dst_get_buf_seq
  drm/exynos/fimc: replace mutex by spinlock
  ...
parents 1c404d88 df5225bc
......@@ -62,6 +62,10 @@ Optional properties for dp-controller:
-hsync-active-high:
HSYNC polarity configuration.
High if defined, Low if not defined
-samsung,hpd-gpio:
Hotplug detect GPIO.
Indicates which GPIO should be used for hotplug
detection
Example:
......
......@@ -5,6 +5,7 @@ Required properties:
1) "samsung,exynos5-hdmi" <DEPRECATED>
2) "samsung,exynos4210-hdmi"
3) "samsung,exynos4212-hdmi"
4) "samsung,exynos5420-hdmi"
- reg: physical base address of the hdmi and length of memory mapped
region.
- interrupts: interrupt number to the cpu.
......@@ -27,6 +28,7 @@ Required properties:
"hdmi", "sclk_hdmi", "sclk_pixel", "sclk_hdmiphy" and "mout_hdmi".
- ddc: phandle to the hdmi ddc node
- phy: phandle to the hdmi phy node
- samsung,syscon-phandle: phandle for system controller node for PMU.
Example:
......@@ -37,4 +39,5 @@ Example:
hpd-gpio = <&gpx3 7 1>;
ddc = <&hdmi_ddc_node>;
phy = <&hdmi_phy_node>;
samsung,syscon-phandle = <&pmu_system_controller>;
};
......@@ -83,6 +83,8 @@ config DRM_KMS_CMA_HELPER
source "drivers/gpu/drm/i2c/Kconfig"
source "drivers/gpu/drm/bridge/Kconfig"
config DRM_TDFX
tristate "3dfx Banshee/Voodoo3+"
depends on DRM && PCI
......@@ -199,5 +201,3 @@ source "drivers/gpu/drm/msm/Kconfig"
source "drivers/gpu/drm/tegra/Kconfig"
source "drivers/gpu/drm/panel/Kconfig"
source "drivers/gpu/drm/bridge/Kconfig"
......@@ -26,14 +26,14 @@ config DRM_EXYNOS_DMABUF
config DRM_EXYNOS_FIMD
bool "Exynos DRM FIMD"
depends on DRM_EXYNOS && !FB_S3C && !ARCH_MULTIPLATFORM
depends on DRM_EXYNOS && !FB_S3C
select FB_MODE_HELPERS
help
Choose this option if you want to use Exynos FIMD for DRM.
config DRM_EXYNOS_DPI
bool "EXYNOS DRM parallel output support"
depends on DRM_EXYNOS
depends on DRM_EXYNOS_FIMD
select DRM_PANEL
default n
help
......@@ -41,7 +41,7 @@ config DRM_EXYNOS_DPI
config DRM_EXYNOS_DSI
bool "EXYNOS DRM MIPI-DSI driver support"
depends on DRM_EXYNOS
depends on DRM_EXYNOS_FIMD
select DRM_MIPI_DSI
select DRM_PANEL
default n
......@@ -50,7 +50,7 @@ config DRM_EXYNOS_DSI
config DRM_EXYNOS_DP
bool "EXYNOS DRM DP driver support"
depends on DRM_EXYNOS && ARCH_EXYNOS
depends on DRM_EXYNOS_FIMD && ARCH_EXYNOS && (DRM_PTN3460=n || DRM_PTN3460=y || DRM_PTN3460=DRM_EXYNOS)
default DRM_EXYNOS
help
This enables support for DP device.
......
/*
* Copyright (C) 2011 Samsung Electronics Co.Ltd
* Authors:
* Seung-Woo Kim <sw0312.kim@samsung.com>
* Inki Dae <inki.dae@samsung.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <drm/drmP.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/of.h>
#include "exynos_drm_drv.h"
#include "exynos_hdmi.h"
static int s5p_ddc_probe(struct i2c_client *client,
const struct i2c_device_id *dev_id)
{
hdmi_attach_ddc_client(client);
dev_info(&client->adapter->dev,
"attached %s into i2c adapter successfully\n",
client->name);
return 0;
}
static int s5p_ddc_remove(struct i2c_client *client)
{
dev_info(&client->adapter->dev,
"detached %s from i2c adapter successfully\n",
client->name);
return 0;
}
static struct of_device_id hdmiddc_match_types[] = {
{
.compatible = "samsung,exynos5-hdmiddc",
}, {
.compatible = "samsung,exynos4210-hdmiddc",
}, {
/* end node */
}
};
struct i2c_driver ddc_driver = {
.driver = {
.name = "exynos-hdmiddc",
.owner = THIS_MODULE,
.of_match_table = hdmiddc_match_types,
},
.probe = s5p_ddc_probe,
.remove = s5p_ddc_remove,
.command = NULL,
};
This diff is collapsed.
......@@ -14,6 +14,7 @@
#define _EXYNOS_DP_CORE_H
#include <drm/drm_crtc.h>
#include <drm/drm_dp_helper.h>
#include <drm/exynos_drm.h>
#define DP_TIMEOUT_LOOP_COUNT 100
......@@ -159,6 +160,7 @@ struct exynos_dp_device {
struct work_struct hotplug_work;
struct phy *phy;
int dpms_mode;
int hpd_gpio;
struct exynos_drm_panel_info panel;
};
......@@ -261,69 +263,17 @@ void exynos_dp_disable_scrambling(struct exynos_dp_device *dp);
#define EDID_EXTENSION_FLAG 0x7e
#define EDID_CHECKSUM 0x7f
/* Definition for DPCD Register */
#define DPCD_ADDR_DPCD_REV 0x0000
#define DPCD_ADDR_MAX_LINK_RATE 0x0001
#define DPCD_ADDR_MAX_LANE_COUNT 0x0002
#define DPCD_ADDR_LINK_BW_SET 0x0100
#define DPCD_ADDR_LANE_COUNT_SET 0x0101
#define DPCD_ADDR_TRAINING_PATTERN_SET 0x0102
#define DPCD_ADDR_TRAINING_LANE0_SET 0x0103
#define DPCD_ADDR_LANE0_1_STATUS 0x0202
#define DPCD_ADDR_LANE_ALIGN_STATUS_UPDATED 0x0204
#define DPCD_ADDR_ADJUST_REQUEST_LANE0_1 0x0206
#define DPCD_ADDR_ADJUST_REQUEST_LANE2_3 0x0207
#define DPCD_ADDR_TEST_REQUEST 0x0218
#define DPCD_ADDR_TEST_RESPONSE 0x0260
#define DPCD_ADDR_TEST_EDID_CHECKSUM 0x0261
#define DPCD_ADDR_SINK_POWER_STATE 0x0600
/* DPCD_ADDR_MAX_LANE_COUNT */
/* DP_MAX_LANE_COUNT */
#define DPCD_ENHANCED_FRAME_CAP(x) (((x) >> 7) & 0x1)
#define DPCD_MAX_LANE_COUNT(x) ((x) & 0x1f)
/* DPCD_ADDR_LANE_COUNT_SET */
#define DPCD_ENHANCED_FRAME_EN (0x1 << 7)
/* DP_LANE_COUNT_SET */
#define DPCD_LANE_COUNT_SET(x) ((x) & 0x1f)
/* DPCD_ADDR_TRAINING_PATTERN_SET */
#define DPCD_SCRAMBLING_DISABLED (0x1 << 5)
#define DPCD_SCRAMBLING_ENABLED (0x0 << 5)
#define DPCD_TRAINING_PATTERN_2 (0x2 << 0)
#define DPCD_TRAINING_PATTERN_1 (0x1 << 0)
#define DPCD_TRAINING_PATTERN_DISABLED (0x0 << 0)
/* DPCD_ADDR_TRAINING_LANE0_SET */
#define DPCD_MAX_PRE_EMPHASIS_REACHED (0x1 << 5)
/* DP_TRAINING_LANE0_SET */
#define DPCD_PRE_EMPHASIS_SET(x) (((x) & 0x3) << 3)
#define DPCD_PRE_EMPHASIS_GET(x) (((x) >> 3) & 0x3)
#define DPCD_PRE_EMPHASIS_PATTERN2_LEVEL0 (0x0 << 3)
#define DPCD_MAX_SWING_REACHED (0x1 << 2)
#define DPCD_VOLTAGE_SWING_SET(x) (((x) & 0x3) << 0)
#define DPCD_VOLTAGE_SWING_GET(x) (((x) >> 0) & 0x3)
#define DPCD_VOLTAGE_SWING_PATTERN1_LEVEL0 (0x0 << 0)
/* DPCD_ADDR_LANE0_1_STATUS */
#define DPCD_LANE_SYMBOL_LOCKED (0x1 << 2)
#define DPCD_LANE_CHANNEL_EQ_DONE (0x1 << 1)
#define DPCD_LANE_CR_DONE (0x1 << 0)
#define DPCD_CHANNEL_EQ_BITS (DPCD_LANE_CR_DONE| \
DPCD_LANE_CHANNEL_EQ_DONE|\
DPCD_LANE_SYMBOL_LOCKED)
/* DPCD_ADDR_LANE_ALIGN__STATUS_UPDATED */
#define DPCD_LINK_STATUS_UPDATED (0x1 << 7)
#define DPCD_DOWNSTREAM_PORT_STATUS_CHANGED (0x1 << 6)
#define DPCD_INTERLANE_ALIGN_DONE (0x1 << 0)
/* DPCD_ADDR_TEST_REQUEST */
#define DPCD_TEST_EDID_READ (0x1 << 2)
/* DPCD_ADDR_TEST_RESPONSE */
#define DPCD_TEST_EDID_CHECKSUM_WRITE (0x1 << 2)
/* DPCD_ADDR_SINK_POWER_STATE */
#define DPCD_SET_POWER_STATE_D0 (0x1 << 0)
#define DPCD_SET_POWER_STATE_D4 (0x2 << 0)
#endif /* _EXYNOS_DP_CORE_H */
......@@ -13,6 +13,7 @@
#include <linux/device.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include "exynos_dp_core.h"
#include "exynos_dp_reg.h"
......@@ -326,6 +327,9 @@ void exynos_dp_clear_hotplug_interrupts(struct exynos_dp_device *dp)
{
u32 reg;
if (gpio_is_valid(dp->hpd_gpio))
return;
reg = HOTPLUG_CHG | HPD_LOST | PLUG;
writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4);
......@@ -337,6 +341,9 @@ void exynos_dp_init_hpd(struct exynos_dp_device *dp)
{
u32 reg;
if (gpio_is_valid(dp->hpd_gpio))
return;
exynos_dp_clear_hotplug_interrupts(dp);
reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3);
......@@ -348,6 +355,13 @@ enum dp_irq_type exynos_dp_get_irq_type(struct exynos_dp_device *dp)
{
u32 reg;
if (gpio_is_valid(dp->hpd_gpio)) {
reg = gpio_get_value(dp->hpd_gpio);
if (reg)
return DP_IRQ_TYPE_HP_CABLE_IN;
else
return DP_IRQ_TYPE_HP_CABLE_OUT;
} else {
/* Parse hotplug interrupt status register */
reg = readl(dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4);
......@@ -361,6 +375,7 @@ enum dp_irq_type exynos_dp_get_irq_type(struct exynos_dp_device *dp)
return DP_IRQ_TYPE_HP_CHANGE;
return DP_IRQ_TYPE_UNKNOWN;
}
}
void exynos_dp_reset_aux(struct exynos_dp_device *dp)
......@@ -386,7 +401,7 @@ void exynos_dp_init_aux(struct exynos_dp_device *dp)
/* Disable AUX transaction H/W retry */
reg = AUX_BIT_PERIOD_EXPECTED_DELAY(3) | AUX_HW_RETRY_COUNT_SEL(0)|
AUX_HW_RETRY_INTERVAL_600_MICROSECONDS;
writel(reg, dp->reg_base + EXYNOS_DP_AUX_HW_RETRY_CTL) ;
writel(reg, dp->reg_base + EXYNOS_DP_AUX_HW_RETRY_CTL);
/* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */
reg = DEFER_CTRL_EN | DEFER_COUNT(1);
......@@ -402,9 +417,14 @@ int exynos_dp_get_plug_in_status(struct exynos_dp_device *dp)
{
u32 reg;
if (gpio_is_valid(dp->hpd_gpio)) {
if (gpio_get_value(dp->hpd_gpio))
return 0;
} else {
reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3);
if (reg & HPD_STATUS)
return 0;
}
return -EINVAL;
}
......
......@@ -19,21 +19,19 @@
#include "exynos_drm_fbdev.h"
static LIST_HEAD(exynos_drm_subdrv_list);
static LIST_HEAD(exynos_drm_manager_list);
static LIST_HEAD(exynos_drm_display_list);
static int exynos_drm_create_enc_conn(struct drm_device *dev,
int exynos_drm_create_enc_conn(struct drm_device *dev,
struct exynos_drm_display *display)
{
struct drm_encoder *encoder;
struct exynos_drm_manager *manager;
int ret;
unsigned long possible_crtcs = 0;
/* Find possible crtcs for this display */
list_for_each_entry(manager, &exynos_drm_manager_list, list)
if (manager->type == display->type)
possible_crtcs |= 1 << manager->pipe;
ret = exynos_drm_crtc_get_pipe_from_type(dev, display->type);
if (ret < 0)
return ret;
possible_crtcs |= 1 << ret;
/* create and initialize a encoder for this sub driver. */
encoder = exynos_drm_encoder_create(dev, display, possible_crtcs);
......@@ -57,127 +55,29 @@ static int exynos_drm_create_enc_conn(struct drm_device *dev,
return ret;
}
static int exynos_drm_subdrv_probe(struct drm_device *dev,
struct exynos_drm_subdrv *subdrv)
int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv)
{
if (subdrv->probe) {
int ret;
subdrv->drm_dev = dev;
if (!subdrv)
return -EINVAL;
/*
* this probe callback would be called by sub driver
* after setting of all resources to this sub driver,
* such as clock, irq and register map are done or by load()
* of exynos drm driver.
*
* P.S. note that this driver is considered for modularization.
*/
ret = subdrv->probe(dev, subdrv->dev);
if (ret)
return ret;
}
list_add_tail(&subdrv->list, &exynos_drm_subdrv_list);
return 0;
}
EXPORT_SYMBOL_GPL(exynos_drm_subdrv_register);
static void exynos_drm_subdrv_remove(struct drm_device *dev,
struct exynos_drm_subdrv *subdrv)
{
if (subdrv->remove)
subdrv->remove(dev, subdrv->dev);
}
int exynos_drm_initialize_managers(struct drm_device *dev)
int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *subdrv)
{
struct exynos_drm_manager *manager, *n;
int ret, pipe = 0;
list_for_each_entry(manager, &exynos_drm_manager_list, list) {
if (manager->ops->initialize) {
ret = manager->ops->initialize(manager, dev, pipe);
if (ret) {
DRM_ERROR("Mgr init [%d] failed with %d\n",
manager->type, ret);
goto err;
}
}
if (!subdrv)
return -EINVAL;
manager->drm_dev = dev;
manager->pipe = pipe++;
list_del(&subdrv->list);
ret = exynos_drm_crtc_create(manager);
if (ret) {
DRM_ERROR("CRTC create [%d] failed with %d\n",
manager->type, ret);
goto err;
}
}
return 0;
err:
list_for_each_entry_safe(manager, n, &exynos_drm_manager_list, list) {
if (pipe-- > 0)
exynos_drm_manager_unregister(manager);
else
list_del(&manager->list);
}
return ret;
}
void exynos_drm_remove_managers(struct drm_device *dev)
{
struct exynos_drm_manager *manager, *n;
list_for_each_entry_safe(manager, n, &exynos_drm_manager_list, list)
exynos_drm_manager_unregister(manager);
}
int exynos_drm_initialize_displays(struct drm_device *dev)
{
struct exynos_drm_display *display, *n;
int ret, initialized = 0;
list_for_each_entry(display, &exynos_drm_display_list, list) {
if (display->ops->initialize) {
ret = display->ops->initialize(display, dev);
if (ret) {
DRM_ERROR("Display init [%d] failed with %d\n",
display->type, ret);
goto err;
}
}
initialized++;
ret = exynos_drm_create_enc_conn(dev, display);
if (ret) {
DRM_ERROR("Encoder create [%d] failed with %d\n",
display->type, ret);
goto err;
}
}
return 0;
err:
list_for_each_entry_safe(display, n, &exynos_drm_display_list, list) {
if (initialized-- > 0)
exynos_drm_display_unregister(display);
else
list_del(&display->list);
}
return ret;
}
void exynos_drm_remove_displays(struct drm_device *dev)
{
struct exynos_drm_display *display, *n;
list_for_each_entry_safe(display, n, &exynos_drm_display_list, list)
exynos_drm_display_unregister(display);
}
EXPORT_SYMBOL_GPL(exynos_drm_subdrv_unregister);
int exynos_drm_device_register(struct drm_device *dev)
int exynos_drm_device_subdrv_probe(struct drm_device *dev)
{
struct exynos_drm_subdrv *subdrv, *n;
int err;
......@@ -186,19 +86,28 @@ int exynos_drm_device_register(struct drm_device *dev)
return -EINVAL;
list_for_each_entry_safe(subdrv, n, &exynos_drm_subdrv_list, list) {
err = exynos_drm_subdrv_probe(dev, subdrv);
if (subdrv->probe) {
subdrv->drm_dev = dev;
/*
* this probe callback would be called by sub driver
* after setting of all resources to this sub driver,
* such as clock, irq and register map are done.
*/
err = subdrv->probe(dev, subdrv->dev);
if (err) {
DRM_DEBUG("exynos drm subdrv probe failed.\n");
list_del(&subdrv->list);
continue;
}
}
}
return 0;
}
EXPORT_SYMBOL_GPL(exynos_drm_device_register);
EXPORT_SYMBOL_GPL(exynos_drm_device_subdrv_probe);
int exynos_drm_device_unregister(struct drm_device *dev)
int exynos_drm_device_subdrv_remove(struct drm_device *dev)
{
struct exynos_drm_subdrv *subdrv;
......@@ -208,66 +117,13 @@ int exynos_drm_device_unregister(struct drm_device *dev)
}
list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) {
exynos_drm_subdrv_remove(dev, subdrv);
if (subdrv->remove)
subdrv->remove(dev, subdrv->dev);
}
return 0;
}
EXPORT_SYMBOL_GPL(exynos_drm_device_unregister);
int exynos_drm_manager_register(struct exynos_drm_manager *manager)
{
BUG_ON(!manager->ops);
list_add_tail(&manager->list, &exynos_drm_manager_list);
return 0;
}
int exynos_drm_manager_unregister(struct exynos_drm_manager *manager)
{
if (manager->ops->remove)
manager->ops->remove(manager);
list_del(&manager->list);
return 0;
}
int exynos_drm_display_register(struct exynos_drm_display *display)
{
BUG_ON(!display->ops);
list_add_tail(&display->list, &exynos_drm_display_list);
return 0;
}
int exynos_drm_display_unregister(struct exynos_drm_display *display)
{
if (display->ops->remove)
display->ops->remove(display);
list_del(&display->list);
return 0;
}
int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv)
{
if (!subdrv)
return -EINVAL;
list_add_tail(&subdrv->list, &exynos_drm_subdrv_list);
return 0;
}
EXPORT_SYMBOL_GPL(exynos_drm_subdrv_register);
int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *subdrv)
{
if (!subdrv)
return -EINVAL;
list_del(&subdrv->list);
return 0;
}
EXPORT_SYMBOL_GPL(exynos_drm_subdrv_unregister);
EXPORT_SYMBOL_GPL(exynos_drm_device_subdrv_remove);
int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file)
{
......
......@@ -368,6 +368,7 @@ int exynos_drm_crtc_create(struct exynos_drm_manager *manager)
return -ENOMEM;
}
manager->crtc = &exynos_crtc->drm_crtc;
crtc = &exynos_crtc->drm_crtc;
private->crtc[manager->pipe] = crtc;
......@@ -491,3 +492,19 @@ void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb)
manager->ops->wait_for_vblank(manager);
}
}
int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev,
unsigned int out_type)
{
struct drm_crtc *crtc;
list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) {
struct exynos_drm_crtc *exynos_crtc;
exynos_crtc = to_exynos_crtc(crtc);
if (exynos_crtc->manager->type == out_type)
return exynos_crtc->manager->pipe;
}
return -EPERM;
}
......@@ -32,4 +32,8 @@ void exynos_drm_crtc_plane_commit(struct drm_crtc *crtc, int zpos);
void exynos_drm_crtc_plane_enable(struct drm_crtc *crtc, int zpos);
void exynos_drm_crtc_plane_disable(struct drm_crtc *crtc, int zpos);
/* This function gets pipe value to crtc device matched with out_type. */
int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev,
unsigned int out_type);
#endif
......@@ -40,20 +40,10 @@ exynos_dpi_detect(struct drm_connector *connector, bool force)
{
struct exynos_dpi *ctx = connector_to_dpi(connector);
/* panels supported only by boot-loader are always connected */
if (!ctx->panel_node)
return connector_status_connected;
if (!ctx->panel) {
ctx->panel = of_drm_find_panel(ctx->panel_node);
if (ctx->panel)
if (!ctx->panel->connector)
drm_panel_attach(ctx->panel, &ctx->connector);
}
if (ctx->panel)
return connector_status_connected;
return connector_status_disconnected;
}
static void exynos_dpi_connector_destroy(struct drm_connector *connector)
......@@ -116,9 +106,6 @@ static int exynos_dpi_create_connector(struct exynos_drm_display *display,
ctx->encoder = encoder;
if (ctx->panel_node)
connector->polled = DRM_CONNECTOR_POLL_CONNECT;
else
connector->polled = DRM_CONNECTOR_POLL_HPD;
ret = drm_connector_init(encoder->dev, connector,
......@@ -287,8 +274,10 @@ static int exynos_dpi_parse_dt(struct exynos_dpi *ctx)
return -ENOMEM;
ret = of_get_videomode(dn, vm, 0);
if (ret < 0)
if (ret < 0) {
devm_kfree(dev, vm);
return ret;
}
ctx->vm = vm;
......@@ -301,32 +290,58 @@ static int exynos_dpi_parse_dt(struct exynos_dpi *ctx)
return 0;
}
int exynos_dpi_probe(struct device *dev)
struct exynos_drm_display *exynos_dpi_probe(struct device *dev)
{
struct exynos_dpi *ctx;
int ret;
ret = exynos_drm_component_add(dev,
EXYNOS_DEVICE_TYPE_CONNECTOR,
exynos_dpi_display.type);
if (ret)
return ERR_PTR(ret);
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
goto err_del_component;
ctx->dev = dev;
exynos_dpi_display.ctx = ctx;
ctx->dpms_mode = DRM_MODE_DPMS_OFF;
ret = exynos_dpi_parse_dt(ctx);
if (ret < 0)
return ret;
if (ret < 0) {
devm_kfree(dev, ctx);
goto err_del_component;
}
if (ctx->panel_node) {
ctx->panel = of_drm_find_panel(ctx->panel_node);
if (!ctx->panel) {
exynos_drm_component_del(dev,
EXYNOS_DEVICE_TYPE_CONNECTOR);
return ERR_PTR(-EPROBE_DEFER);
}
}
exynos_drm_display_register(&exynos_dpi_display);
return &exynos_dpi_display;
return 0;
err_del_component:
exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
return NULL;
}
int exynos_dpi_remove(struct device *dev)
{
struct drm_encoder *encoder = exynos_dpi_display.encoder;
struct exynos_dpi *ctx = exynos_dpi_display.ctx;
exynos_dpi_dpms(&exynos_dpi_display, DRM_MODE_DPMS_OFF);
exynos_drm_display_unregister(&exynos_dpi_display);
encoder->funcs->destroy(encoder);
drm_connector_cleanup(&ctx->connector);
exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
return 0;
}
This diff is collapsed.
......@@ -42,6 +42,13 @@ struct drm_connector;
extern unsigned int drm_vblank_offdelay;
/* This enumerates device type. */
enum exynos_drm_device_type {
EXYNOS_DEVICE_TYPE_NONE,
EXYNOS_DEVICE_TYPE_CRTC,
EXYNOS_DEVICE_TYPE_CONNECTOR,
};
/* this enumerates display type. */
enum exynos_drm_output_type {
EXYNOS_DISPLAY_TYPE_NONE,
......@@ -122,7 +129,6 @@ struct exynos_drm_overlay {
* Exynos DRM Display Structure.
* - this structure is common to analog tv, digital tv and lcd panel.
*
* @initialize: initializes the display with drm_dev
* @remove: cleans up the display for removal
* @mode_fixup: fix mode data comparing to hw specific display mode.
* @mode_set: convert drm_display_mode to hw specific display mode and
......@@ -133,8 +139,6 @@ struct exynos_drm_overlay {
*/
struct exynos_drm_display;
struct exynos_drm_display_ops {
int (*initialize)(struct exynos_drm_display *display,
struct drm_device *drm_dev);
int (*create_connector)(struct exynos_drm_display *display,
struct drm_encoder *encoder);
void (*remove)(struct exynos_drm_display *display);
......@@ -172,8 +176,6 @@ struct exynos_drm_display {
/*
* Exynos drm manager ops
*
* @initialize: initializes the manager with drm_dev
* @remove: cleans up the manager for removal
* @dpms: control device power.
* @mode_fixup: fix mode data before applying it
* @mode_set: set the given mode to the manager
......@@ -189,9 +191,6 @@ struct exynos_drm_display {
*/
struct exynos_drm_manager;
struct exynos_drm_manager_ops {
int (*initialize)(struct exynos_drm_manager *mgr,
struct drm_device *drm_dev, int pipe);
void (*remove)(struct exynos_drm_manager *mgr);
void (*dpms)(struct exynos_drm_manager *mgr, int mode);
bool (*mode_fixup)(struct exynos_drm_manager *mgr,
const struct drm_display_mode *mode,
......@@ -215,6 +214,7 @@ struct exynos_drm_manager_ops {
* @list: the list entry for this manager
* @type: one of EXYNOS_DISPLAY_TYPE_LCD and HDMI.
* @drm_dev: pointer to the drm device
* @crtc: crtc object.
* @pipe: the pipe number for this crtc/manager
* @ops: pointer to callbacks for exynos drm specific functionality
* @ctx: A pointer to the manager's implementation specific context
......@@ -223,6 +223,7 @@ struct exynos_drm_manager {
struct list_head list;
enum exynos_drm_output_type type;
struct drm_device *drm_dev;
struct drm_crtc *crtc;
int pipe;
struct exynos_drm_manager_ops *ops;
void *ctx;
......@@ -254,6 +255,7 @@ struct drm_exynos_file_private {
* otherwise default one.
* @da_space_size: size of device address space.
* if 0 then default value is used for it.
* @pipe: the pipe number for this crtc/manager.
*/
struct exynos_drm_private {
struct drm_fb_helper *fb_helper;
......@@ -271,6 +273,8 @@ struct exynos_drm_private {
unsigned long da_start;
unsigned long da_space_size;
unsigned int pipe;
};
/*
......@@ -302,39 +306,14 @@ struct exynos_drm_subdrv {
struct drm_file *file);
};
/*
* this function calls a probe callback registered to sub driver list and
* create its own encoder and connector and then set drm_device object
* to global one.
*/
int exynos_drm_device_register(struct drm_device *dev);
/*
* this function calls a remove callback registered to sub driver list and
* destroy its own encoder and connetor.
*/
int exynos_drm_device_unregister(struct drm_device *dev);
int exynos_drm_initialize_managers(struct drm_device *dev);
void exynos_drm_remove_managers(struct drm_device *dev);
int exynos_drm_initialize_displays(struct drm_device *dev);
void exynos_drm_remove_displays(struct drm_device *dev);
int exynos_drm_manager_register(struct exynos_drm_manager *manager);
int exynos_drm_manager_unregister(struct exynos_drm_manager *manager);
int exynos_drm_display_register(struct exynos_drm_display *display);
int exynos_drm_display_unregister(struct exynos_drm_display *display);
/*
* this function would be called by sub drivers such as display controller
* or hdmi driver to register this sub driver object to exynos drm driver
* and when a sub driver is registered to exynos drm driver a probe callback
* of the sub driver is called and creates its own encoder and connector.
*/
/* This function would be called by non kms drivers such as g2d and ipp. */
int exynos_drm_subdrv_register(struct exynos_drm_subdrv *drm_subdrv);
/* this function removes subdrv list from exynos drm driver */
int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *drm_subdrv);
int exynos_drm_device_subdrv_probe(struct drm_device *dev);
int exynos_drm_device_subdrv_remove(struct drm_device *dev);
int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file);
void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file);
......@@ -360,18 +339,40 @@ int exynos_platform_device_ipp_register(void);
void exynos_platform_device_ipp_unregister(void);
#ifdef CONFIG_DRM_EXYNOS_DPI
int exynos_dpi_probe(struct device *dev);
struct exynos_drm_display * exynos_dpi_probe(struct device *dev);
int exynos_dpi_remove(struct device *dev);
#else
static inline int exynos_dpi_probe(struct device *dev) { return 0; }
static inline struct exynos_drm_display *
exynos_dpi_probe(struct device *dev) { return 0; }
static inline int exynos_dpi_remove(struct device *dev) { return 0; }
#endif
/*
* this function registers exynos drm vidi platform device/driver.
*/
int exynos_drm_probe_vidi(void);
/*
* this function unregister exynos drm vidi platform device/driver.
*/
void exynos_drm_remove_vidi(void);
/* This function creates a encoder and a connector, and initializes them. */
int exynos_drm_create_enc_conn(struct drm_device *dev,
struct exynos_drm_display *display);
int exynos_drm_component_add(struct device *dev,
enum exynos_drm_device_type dev_type,
enum exynos_drm_output_type out_type);
void exynos_drm_component_del(struct device *dev,
enum exynos_drm_device_type dev_type);
extern struct platform_driver fimd_driver;
extern struct platform_driver dp_driver;
extern struct platform_driver dsi_driver;
extern struct platform_driver fimd_driver;
extern struct platform_driver hdmi_driver;
extern struct platform_driver mixer_driver;
extern struct platform_driver hdmi_driver;
extern struct platform_driver exynos_drm_common_hdmi_driver;
extern struct platform_driver vidi_driver;
extern struct platform_driver g2d_driver;
......
......@@ -19,6 +19,7 @@
#include <linux/irq.h>
#include <linux/phy/phy.h>
#include <linux/regulator/consumer.h>
#include <linux/component.h>
#include <video/mipi_display.h>
#include <video/videomode.h>
......@@ -1378,16 +1379,60 @@ static int exynos_dsi_parse_dt(struct exynos_dsi *dsi)
return ret;
}
static int exynos_dsi_bind(struct device *dev, struct device *master,
void *data)
{
struct drm_device *drm_dev = data;
struct exynos_dsi *dsi;
int ret;
ret = exynos_drm_create_enc_conn(drm_dev, &exynos_dsi_display);
if (ret) {
DRM_ERROR("Encoder create [%d] failed with %d\n",
exynos_dsi_display.type, ret);
return ret;
}
dsi = exynos_dsi_display.ctx;
return mipi_dsi_host_register(&dsi->dsi_host);
}
static void exynos_dsi_unbind(struct device *dev, struct device *master,
void *data)
{
struct exynos_dsi *dsi = exynos_dsi_display.ctx;
struct drm_encoder *encoder = dsi->encoder;
exynos_dsi_dpms(&exynos_dsi_display, DRM_MODE_DPMS_OFF);
mipi_dsi_host_unregister(&dsi->dsi_host);
encoder->funcs->destroy(encoder);
drm_connector_cleanup(&dsi->connector);
}
static const struct component_ops exynos_dsi_component_ops = {
.bind = exynos_dsi_bind,
.unbind = exynos_dsi_unbind,
};
static int exynos_dsi_probe(struct platform_device *pdev)
{
struct resource *res;
struct exynos_dsi *dsi;
int ret;
ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
exynos_dsi_display.type);
if (ret)
return ret;
dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL);
if (!dsi) {
dev_err(&pdev->dev, "failed to allocate dsi object.\n");
return -ENOMEM;
ret = -ENOMEM;
goto err_del_component;
}
init_completion(&dsi->completed);
......@@ -1401,7 +1446,7 @@ static int exynos_dsi_probe(struct platform_device *pdev)
ret = exynos_dsi_parse_dt(dsi);
if (ret)
return ret;
goto err_del_component;
dsi->supplies[0].supply = "vddcore";
dsi->supplies[1].supply = "vddio";
......@@ -1415,32 +1460,37 @@ static int exynos_dsi_probe(struct platform_device *pdev)
dsi->pll_clk = devm_clk_get(&pdev->dev, "pll_clk");
if (IS_ERR(dsi->pll_clk)) {
dev_info(&pdev->dev, "failed to get dsi pll input clock\n");
return -EPROBE_DEFER;
ret = PTR_ERR(dsi->pll_clk);
goto err_del_component;
}
dsi->bus_clk = devm_clk_get(&pdev->dev, "bus_clk");
if (IS_ERR(dsi->bus_clk)) {
dev_info(&pdev->dev, "failed to get dsi bus clock\n");
return -EPROBE_DEFER;
ret = PTR_ERR(dsi->bus_clk);
goto err_del_component;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dsi->reg_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(dsi->reg_base)) {
dev_err(&pdev->dev, "failed to remap io region\n");
return PTR_ERR(dsi->reg_base);
ret = PTR_ERR(dsi->reg_base);
goto err_del_component;
}
dsi->phy = devm_phy_get(&pdev->dev, "dsim");
if (IS_ERR(dsi->phy)) {
dev_info(&pdev->dev, "failed to get dsim phy\n");
return -EPROBE_DEFER;
ret = PTR_ERR(dsi->phy);
goto err_del_component;
}
dsi->irq = platform_get_irq(pdev, 0);
if (dsi->irq < 0) {
dev_err(&pdev->dev, "failed to request dsi irq resource\n");
return dsi->irq;
ret = dsi->irq;
goto err_del_component;
}
irq_set_status_flags(dsi->irq, IRQ_NOAUTOEN);
......@@ -1449,58 +1499,31 @@ static int exynos_dsi_probe(struct platform_device *pdev)
dev_name(&pdev->dev), dsi);
if (ret) {
dev_err(&pdev->dev, "failed to request dsi irq\n");
return ret;
goto err_del_component;
}
exynos_dsi_display.ctx = dsi;
platform_set_drvdata(pdev, &exynos_dsi_display);
exynos_drm_display_register(&exynos_dsi_display);
return mipi_dsi_host_register(&dsi->dsi_host);
}
static int exynos_dsi_remove(struct platform_device *pdev)
{
struct exynos_dsi *dsi = exynos_dsi_display.ctx;
exynos_dsi_dpms(&exynos_dsi_display, DRM_MODE_DPMS_OFF);
exynos_drm_display_unregister(&exynos_dsi_display);
mipi_dsi_host_unregister(&dsi->dsi_host);
return 0;
}
#if CONFIG_PM_SLEEP
static int exynos_dsi_resume(struct device *dev)
{
struct exynos_dsi *dsi = exynos_dsi_display.ctx;
ret = component_add(&pdev->dev, &exynos_dsi_component_ops);
if (ret)
goto err_del_component;
if (dsi->state & DSIM_STATE_ENABLED) {
dsi->state &= ~DSIM_STATE_ENABLED;
exynos_dsi_enable(dsi);
}
return ret;
return 0;
err_del_component:
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
return ret;
}
static int exynos_dsi_suspend(struct device *dev)
static int exynos_dsi_remove(struct platform_device *pdev)
{
struct exynos_dsi *dsi = exynos_dsi_display.ctx;
if (dsi->state & DSIM_STATE_ENABLED) {
exynos_dsi_disable(dsi);
dsi->state |= DSIM_STATE_ENABLED;
}
component_del(&pdev->dev, &exynos_dsi_component_ops);
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
return 0;
}
#endif
static const struct dev_pm_ops exynos_dsi_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(exynos_dsi_suspend, exynos_dsi_resume)
};
static struct of_device_id exynos_dsi_of_match[] = {
{ .compatible = "samsung,exynos4210-mipi-dsi" },
......@@ -1513,7 +1536,6 @@ struct platform_driver dsi_driver = {
.driver = {
.name = "exynos-dsi",
.owner = THIS_MODULE,
.pm = &exynos_dsi_pm_ops,
.of_match_table = exynos_dsi_of_match,
},
};
......
......@@ -121,16 +121,8 @@ static int exynos_drm_fbdev_update(struct drm_fb_helper *helper,
offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3);
offset += fbi->var.yoffset * fb->pitches[0];
dev->mode_config.fb_base = (resource_size_t)buffer->dma_addr;
fbi->screen_base = buffer->kvaddr + offset;
if (is_drm_iommu_supported(dev))
fbi->fix.smem_start = (unsigned long)
(page_to_phys(sg_page(buffer->sgt->sgl)) + offset);
else
fbi->fix.smem_start = (unsigned long)buffer->dma_addr;
fbi->screen_size = size;
fbi->fix.smem_len = size;
return 0;
}
......@@ -237,7 +229,7 @@ static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
.fb_probe = exynos_drm_fbdev_create,
};
bool exynos_drm_fbdev_is_anything_connected(struct drm_device *dev)
static bool exynos_drm_fbdev_is_anything_connected(struct drm_device *dev)
{
struct drm_connector *connector;
bool ret = false;
......
This diff is collapsed.
......@@ -19,6 +19,7 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/pm_runtime.h>
#include <linux/component.h>
#include <video/of_display_timing.h>
#include <video/of_videomode.h>
......@@ -38,6 +39,7 @@
*/
#define FIMD_DEFAULT_FRAMERATE 60
#define MIN_FB_WIDTH_FOR_16WORD_BURST 128
/* position control register for hardware window 0, 2 ~ 4.*/
#define VIDOSD_A(win) (VIDOSD_BASE + 0x00 + (win) * 16)
......@@ -122,6 +124,7 @@ struct fimd_context {
struct exynos_drm_panel_info panel;
struct fimd_driver_data *driver_data;
struct exynos_drm_display *display;
};
static const struct of_device_id fimd_driver_dt_match[] = {
......@@ -143,13 +146,57 @@ static inline struct fimd_driver_data *drm_fimd_get_driver_data(
return (struct fimd_driver_data *)of_id->data;
}
static void fimd_wait_for_vblank(struct exynos_drm_manager *mgr)
{
struct fimd_context *ctx = mgr->ctx;
if (ctx->suspended)
return;
atomic_set(&ctx->wait_vsync_event, 1);
/*
* wait for FIMD to signal VSYNC interrupt or return after
* timeout which is set to 50ms (refresh rate of 20).
*/
if (!wait_event_timeout(ctx->wait_vsync_queue,
!atomic_read(&ctx->wait_vsync_event),
HZ/20))
DRM_DEBUG_KMS("vblank wait timed out.\n");
}
static void fimd_clear_channel(struct exynos_drm_manager *mgr)
{
struct fimd_context *ctx = mgr->ctx;
int win, ch_enabled = 0;
DRM_DEBUG_KMS("%s\n", __FILE__);
/* Check if any channel is enabled. */
for (win = 0; win < WINDOWS_NR; win++) {
u32 val = readl(ctx->regs + SHADOWCON);
if (val & SHADOWCON_CHx_ENABLE(win)) {
val &= ~SHADOWCON_CHx_ENABLE(win);
writel(val, ctx->regs + SHADOWCON);
ch_enabled = 1;
}
}
/* Wait for vsync, as disable channel takes effect at next vsync */
if (ch_enabled)
fimd_wait_for_vblank(mgr);
}
static int fimd_mgr_initialize(struct exynos_drm_manager *mgr,
struct drm_device *drm_dev, int pipe)
struct drm_device *drm_dev)
{
struct fimd_context *ctx = mgr->ctx;
struct exynos_drm_private *priv;
priv = drm_dev->dev_private;
ctx->drm_dev = drm_dev;
ctx->pipe = pipe;
mgr->drm_dev = ctx->drm_dev = drm_dev;
mgr->pipe = ctx->pipe = priv->pipe++;
/*
* enable drm irq mode.
......@@ -169,8 +216,14 @@ static int fimd_mgr_initialize(struct exynos_drm_manager *mgr,
drm_dev->vblank_disable_allowed = true;
/* attach this sub driver to iommu mapping if supported. */
if (is_drm_iommu_supported(ctx->drm_dev))
if (is_drm_iommu_supported(ctx->drm_dev)) {
/*
* If any channel is already active, iommu will throw
* a PAGE FAULT when enabled. So clear any channel if enabled.
*/
fimd_clear_channel(mgr);
drm_iommu_attach_device(ctx->drm_dev, ctx->dev);
}
return 0;
}
......@@ -324,25 +377,6 @@ static void fimd_disable_vblank(struct exynos_drm_manager *mgr)
}
}
static void fimd_wait_for_vblank(struct exynos_drm_manager *mgr)
{
struct fimd_context *ctx = mgr->ctx;
if (ctx->suspended)
return;
atomic_set(&ctx->wait_vsync_event, 1);
/*
* wait for FIMD to signal VSYNC interrupt or return after
* timeout which is set to 50ms (refresh rate of 20).
*/
if (!wait_event_timeout(ctx->wait_vsync_queue,
!atomic_read(&ctx->wait_vsync_event),
HZ/20))
DRM_DEBUG_KMS("vblank wait timed out.\n");
}
static void fimd_win_mode_set(struct exynos_drm_manager *mgr,
struct exynos_drm_overlay *overlay)
{
......@@ -446,6 +480,19 @@ static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win)
DRM_DEBUG_KMS("bpp = %d\n", win_data->bpp);
/*
* In case of exynos, setting dma-burst to 16Word causes permanent
* tearing for very small buffers, e.g. cursor buffer. Burst Mode
* switching which is based on overlay size is not recommended as
* overlay size varies alot towards the end of the screen and rapid
* movement causes unstable DMA which results into iommu crash/tear.
*/
if (win_data->fb_width < MIN_FB_WIDTH_FOR_16WORD_BURST) {
val &= ~WINCONx_BURSTLEN_MASK;
val |= WINCONx_BURSTLEN_4WORD;
}
writel(val, ctx->regs + WINCON(win));
}
......@@ -656,19 +703,6 @@ static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos)
win_data->enabled = false;
}
static void fimd_clear_win(struct fimd_context *ctx, int win)
{
writel(0, ctx->regs + WINCON(win));
writel(0, ctx->regs + VIDOSD_A(win));
writel(0, ctx->regs + VIDOSD_B(win));
writel(0, ctx->regs + VIDOSD_C(win));
if (win == 1 || win == 2)
writel(0, ctx->regs + VIDOSD_D(win));
fimd_shadow_protect_win(ctx, win, false);
}
static void fimd_window_suspend(struct exynos_drm_manager *mgr)
{
struct fimd_context *ctx = mgr->ctx;
......@@ -803,8 +837,6 @@ static void fimd_dpms(struct exynos_drm_manager *mgr, int mode)
}
static struct exynos_drm_manager_ops fimd_manager_ops = {
.initialize = fimd_mgr_initialize,
.remove = fimd_mgr_remove,
.dpms = fimd_dpms,
.mode_fixup = fimd_mode_fixup,
.mode_set = fimd_mode_set,
......@@ -849,20 +881,64 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
return IRQ_HANDLED;
}
static int fimd_bind(struct device *dev, struct device *master, void *data)
{
struct fimd_context *ctx = fimd_manager.ctx;
struct drm_device *drm_dev = data;
fimd_mgr_initialize(&fimd_manager, drm_dev);
exynos_drm_crtc_create(&fimd_manager);
if (ctx->display)
exynos_drm_create_enc_conn(drm_dev, ctx->display);
return 0;
}
static void fimd_unbind(struct device *dev, struct device *master,
void *data)
{
struct exynos_drm_manager *mgr = dev_get_drvdata(dev);
struct fimd_context *ctx = fimd_manager.ctx;
struct drm_crtc *crtc = mgr->crtc;
fimd_dpms(mgr, DRM_MODE_DPMS_OFF);
if (ctx->display)
exynos_dpi_remove(dev);
fimd_mgr_remove(mgr);
crtc->funcs->destroy(crtc);
}
static const struct component_ops fimd_component_ops = {
.bind = fimd_bind,
.unbind = fimd_unbind,
};
static int fimd_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct fimd_context *ctx;
struct resource *res;
int win;
int ret = -EINVAL;
if (!dev->of_node)
return -ENODEV;
ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC,
fimd_manager.type);
if (ret)
return ret;
if (!dev->of_node) {
ret = -ENODEV;
goto err_del_component;
}
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
if (!ctx) {
ret = -ENOMEM;
goto err_del_component;
}
ctx->dev = dev;
ctx->suspended = true;
......@@ -875,32 +951,37 @@ static int fimd_probe(struct platform_device *pdev)
ctx->bus_clk = devm_clk_get(dev, "fimd");
if (IS_ERR(ctx->bus_clk)) {
dev_err(dev, "failed to get bus clock\n");
return PTR_ERR(ctx->bus_clk);
ret = PTR_ERR(ctx->bus_clk);
goto err_del_component;
}
ctx->lcd_clk = devm_clk_get(dev, "sclk_fimd");
if (IS_ERR(ctx->lcd_clk)) {
dev_err(dev, "failed to get lcd clock\n");
return PTR_ERR(ctx->lcd_clk);
ret = PTR_ERR(ctx->lcd_clk);
goto err_del_component;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ctx->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(ctx->regs))
return PTR_ERR(ctx->regs);
if (IS_ERR(ctx->regs)) {
ret = PTR_ERR(ctx->regs);
goto err_del_component;
}
res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "vsync");
if (!res) {
dev_err(dev, "irq request failed.\n");
return -ENXIO;
ret = -ENXIO;
goto err_del_component;
}
ret = devm_request_irq(dev, res->start, fimd_irq_handler,
0, "drm_fimd", ctx);
if (ret) {
dev_err(dev, "irq request failed.\n");
return ret;
goto err_del_component;
}
ctx->driver_data = drm_fimd_get_driver_data(pdev);
......@@ -910,30 +991,34 @@ static int fimd_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, &fimd_manager);
fimd_manager.ctx = ctx;
exynos_drm_manager_register(&fimd_manager);
exynos_dpi_probe(ctx->dev);
ctx->display = exynos_dpi_probe(dev);
if (IS_ERR(ctx->display))
return PTR_ERR(ctx->display);
pm_runtime_enable(dev);
pm_runtime_enable(&pdev->dev);
for (win = 0; win < WINDOWS_NR; win++)
fimd_clear_win(ctx, win);
ret = component_add(&pdev->dev, &fimd_component_ops);
if (ret)
goto err_disable_pm_runtime;
return 0;
return ret;
err_disable_pm_runtime:
pm_runtime_disable(&pdev->dev);
err_del_component:
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
return ret;
}
static int fimd_remove(struct platform_device *pdev)
{
struct exynos_drm_manager *mgr = platform_get_drvdata(pdev);
exynos_dpi_remove(&pdev->dev);
exynos_drm_manager_unregister(&fimd_manager);
fimd_dpms(mgr, DRM_MODE_DPMS_OFF);
pm_runtime_disable(&pdev->dev);
component_del(&pdev->dev, &fimd_component_ops);
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
return 0;
}
......
......@@ -612,22 +612,20 @@ int exynos_drm_gem_dumb_create(struct drm_file *file_priv,
args->pitch = args->width * ((args->bpp + 7) / 8);
args->size = args->pitch * args->height;
exynos_gem_obj = exynos_drm_gem_create(dev, EXYNOS_BO_CONTIG |
EXYNOS_BO_WC, args->size);
/*
* If physically contiguous memory allocation fails and if IOMMU is
* supported then try to get buffer from non physically contiguous
* memory area.
*/
if (IS_ERR(exynos_gem_obj) && is_drm_iommu_supported(dev)) {
dev_warn(dev->dev, "contiguous FB allocation failed, falling back to non-contiguous\n");
if (is_drm_iommu_supported(dev)) {
exynos_gem_obj = exynos_drm_gem_create(dev,
EXYNOS_BO_NONCONTIG | EXYNOS_BO_WC,
args->size);
} else {
exynos_gem_obj = exynos_drm_gem_create(dev,
EXYNOS_BO_CONTIG | EXYNOS_BO_WC,
args->size);
}
if (IS_ERR(exynos_gem_obj))
if (IS_ERR(exynos_gem_obj)) {
dev_warn(dev->dev, "FB allocation failed.\n");
return PTR_ERR(exynos_gem_obj);
}
ret = exynos_drm_gem_handle_create(&exynos_gem_obj->base, file_priv,
&args->handle);
......
......@@ -1335,11 +1335,7 @@ static irqreturn_t gsc_irq_handler(int irq, void *dev_id)
static int gsc_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
{
struct drm_exynos_ipp_prop_list *prop_list;
prop_list = devm_kzalloc(ippdrv->dev, sizeof(*prop_list), GFP_KERNEL);
if (!prop_list)
return -ENOMEM;
struct drm_exynos_ipp_prop_list *prop_list = &ippdrv->prop_list;
prop_list->version = 1;
prop_list->writeback = 1;
......@@ -1363,8 +1359,6 @@ static int gsc_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
prop_list->scale_min.hsize = GSC_SCALE_MIN;
prop_list->scale_min.vsize = GSC_SCALE_MIN;
ippdrv->prop_list = prop_list;
return 0;
}
......@@ -1387,7 +1381,7 @@ static int gsc_ippdrv_check_property(struct device *dev,
{
struct gsc_context *ctx = get_gsc_context(dev);
struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
struct drm_exynos_ipp_prop_list *pp = ippdrv->prop_list;
struct drm_exynos_ipp_prop_list *pp = &ippdrv->prop_list;
struct drm_exynos_ipp_config *config;
struct drm_exynos_pos *pos;
struct drm_exynos_sz *sz;
......
This diff is collapsed.
......@@ -52,7 +52,7 @@ struct drm_exynos_ipp_cmd_work {
* @list: list head to command queue information.
* @event_list: list head of event.
* @mem_list: list head to source,destination memory queue information.
* @cmd_lock: lock for synchronization of access to ioctl.
* @lock: lock for synchronization of access to ioctl.
* @mem_lock: lock for synchronization of access to memory nodes.
* @event_lock: lock for synchronization of access to scheduled event.
* @start_complete: completion of start of command.
......@@ -68,7 +68,7 @@ struct drm_exynos_ipp_cmd_node {
struct list_head list;
struct list_head event_list;
struct list_head mem_list[EXYNOS_DRM_OPS_MAX];
struct mutex cmd_lock;
struct mutex lock;
struct mutex mem_lock;
struct mutex event_lock;
struct completion start_complete;
......@@ -83,7 +83,7 @@ struct drm_exynos_ipp_cmd_node {
/*
* A structure of buffer information.
*
* @gem_objs: Y, Cb, Cr each gem object.
* @handles: Y, Cb, Cr each gem object handle.
* @base: Y, Cb, Cr each planar address.
*/
struct drm_exynos_ipp_buf_info {
......@@ -142,12 +142,12 @@ struct exynos_drm_ipp_ops {
* @parent_dev: parent device information.
* @dev: platform device.
* @drm_dev: drm device.
* @ipp_id: id of ipp driver.
* @dedicated: dedicated ipp device.
* @ops: source, destination operations.
* @event_workq: event work queue.
* @c_node: current command information.
* @cmd_list: list head for command information.
* @cmd_lock: lock for synchronization of access to cmd_list.
* @prop_list: property informations of current ipp driver.
* @check_property: check property about format, size, buffer.
* @reset: reset ipp block.
......@@ -160,13 +160,13 @@ struct exynos_drm_ippdrv {
struct device *parent_dev;
struct device *dev;
struct drm_device *drm_dev;
u32 ipp_id;
bool dedicated;
struct exynos_drm_ipp_ops *ops[EXYNOS_DRM_OPS_MAX];
struct workqueue_struct *event_workq;
struct drm_exynos_ipp_cmd_node *c_node;
struct list_head cmd_list;
struct drm_exynos_ipp_prop_list *prop_list;
struct mutex cmd_lock;
struct drm_exynos_ipp_prop_list prop_list;
int (*check_property)(struct device *dev,
struct drm_exynos_ipp_property *property);
......
......@@ -158,8 +158,9 @@ static irqreturn_t rotator_irq_handler(int irq, void *arg)
rot->cur_buf_id[EXYNOS_DRM_OPS_DST];
queue_work(ippdrv->event_workq,
(struct work_struct *)event_work);
} else
} else {
DRM_ERROR("the SFR is set illegally\n");
}
return IRQ_HANDLED;
}
......@@ -469,11 +470,7 @@ static struct exynos_drm_ipp_ops rot_dst_ops = {
static int rotator_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
{
struct drm_exynos_ipp_prop_list *prop_list;
prop_list = devm_kzalloc(ippdrv->dev, sizeof(*prop_list), GFP_KERNEL);
if (!prop_list)
return -ENOMEM;
struct drm_exynos_ipp_prop_list *prop_list = &ippdrv->prop_list;
prop_list->version = 1;
prop_list->flip = (1 << EXYNOS_DRM_FLIP_VERTICAL) |
......@@ -486,8 +483,6 @@ static int rotator_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
prop_list->crop = 0;
prop_list->scale = 0;
ippdrv->prop_list = prop_list;
return 0;
}
......
......@@ -51,6 +51,7 @@ struct vidi_context {
struct drm_crtc *crtc;
struct drm_encoder *encoder;
struct drm_connector connector;
struct exynos_drm_subdrv subdrv;
struct vidi_win_data win_data[WINDOWS_NR];
struct edid *raw_edid;
unsigned int clkdiv;
......@@ -294,14 +295,13 @@ static void vidi_dpms(struct exynos_drm_manager *mgr, int mode)
}
static int vidi_mgr_initialize(struct exynos_drm_manager *mgr,
struct drm_device *drm_dev, int pipe)
struct drm_device *drm_dev)
{
struct vidi_context *ctx = mgr->ctx;
struct exynos_drm_private *priv = drm_dev->dev_private;
DRM_ERROR("vidi initialize ct=%p dev=%p pipe=%d\n", ctx, drm_dev, pipe);
ctx->drm_dev = drm_dev;
ctx->pipe = pipe;
mgr->drm_dev = ctx->drm_dev = drm_dev;
mgr->pipe = ctx->pipe = priv->pipe++;
/*
* enable drm irq mode.
......@@ -324,7 +324,6 @@ static int vidi_mgr_initialize(struct exynos_drm_manager *mgr,
}
static struct exynos_drm_manager_ops vidi_manager_ops = {
.initialize = vidi_mgr_initialize,
.dpms = vidi_dpms,
.commit = vidi_commit,
.enable_vblank = vidi_enable_vblank,
......@@ -579,13 +578,38 @@ static struct exynos_drm_display vidi_display = {
.ops = &vidi_display_ops,
};
static int vidi_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
{
struct exynos_drm_manager *mgr = get_vidi_mgr(dev);
struct vidi_context *ctx = mgr->ctx;
struct drm_crtc *crtc = ctx->crtc;
int ret;
vidi_mgr_initialize(mgr, drm_dev);
ret = exynos_drm_crtc_create(&vidi_manager);
if (ret) {
DRM_ERROR("failed to create crtc.\n");
return ret;
}
ret = exynos_drm_create_enc_conn(drm_dev, &vidi_display);
if (ret) {
crtc->funcs->destroy(crtc);
DRM_ERROR("failed to create encoder and connector.\n");
return ret;
}
return 0;
}
static int vidi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct exynos_drm_subdrv *subdrv;
struct vidi_context *ctx;
int ret;
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
......@@ -600,28 +624,43 @@ static int vidi_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, &vidi_manager);
ret = device_create_file(dev, &dev_attr_connection);
if (ret < 0)
DRM_INFO("failed to create connection sysfs.\n");
subdrv = &ctx->subdrv;
subdrv->dev = &pdev->dev;
subdrv->probe = vidi_subdrv_probe;
ret = exynos_drm_subdrv_register(subdrv);
if (ret < 0) {
dev_err(&pdev->dev, "failed to register drm vidi device\n");
return ret;
}
exynos_drm_manager_register(&vidi_manager);
exynos_drm_display_register(&vidi_display);
ret = device_create_file(&pdev->dev, &dev_attr_connection);
if (ret < 0) {
exynos_drm_subdrv_unregister(subdrv);
DRM_INFO("failed to create connection sysfs.\n");
}
return 0;
}
static int vidi_remove(struct platform_device *pdev)
{
struct vidi_context *ctx = platform_get_drvdata(pdev);
exynos_drm_display_unregister(&vidi_display);
exynos_drm_manager_unregister(&vidi_manager);
struct exynos_drm_manager *mgr = platform_get_drvdata(pdev);
struct vidi_context *ctx = mgr->ctx;
struct drm_encoder *encoder = ctx->encoder;
struct drm_crtc *crtc = mgr->crtc;
if (ctx->raw_edid != (struct edid *)fake_edid_info) {
kfree(ctx->raw_edid);
ctx->raw_edid = NULL;
return -EINVAL;
}
crtc->funcs->destroy(crtc);
encoder->funcs->destroy(encoder);
drm_connector_cleanup(&ctx->connector);
return 0;
}
......@@ -633,3 +672,31 @@ struct platform_driver vidi_driver = {
.owner = THIS_MODULE,
},
};
int exynos_drm_probe_vidi(void)
{
struct platform_device *pdev;
int ret;
pdev = platform_device_register_simple("exynos-drm-vidi", -1, NULL, 0);
if (IS_ERR(pdev))
return PTR_ERR(pdev);
ret = platform_driver_register(&vidi_driver);
if (ret) {
platform_device_unregister(pdev);
return ret;
}
return ret;
}
void exynos_drm_remove_vidi(void)
{
struct vidi_context *ctx = vidi_manager.ctx;
struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
struct platform_device *pdev = to_platform_device(subdrv->dev);
platform_driver_unregister(&vidi_driver);
platform_device_unregister(pdev);
}
This diff is collapsed.
/*
*
* 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.
*/
#ifndef _EXYNOS_HDMI_H_
#define _EXYNOS_HDMI_H_
void hdmi_attach_ddc_client(struct i2c_client *ddc);
void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy);
extern struct i2c_driver hdmiphy_driver;
extern struct i2c_driver ddc_driver;
#endif
/*
* Copyright (C) 2011 Samsung Electronics Co.Ltd
* Authors:
* Seung-Woo Kim <sw0312.kim@samsung.com>
* Inki Dae <inki.dae@samsung.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <drm/drmP.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/of.h>
#include "exynos_drm_drv.h"
#include "exynos_hdmi.h"
static int hdmiphy_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
hdmi_attach_hdmiphy_client(client);
dev_info(&client->adapter->dev, "attached s5p_hdmiphy "
"into i2c adapter successfully\n");
return 0;
}
static int hdmiphy_remove(struct i2c_client *client)
{
dev_info(&client->adapter->dev, "detached s5p_hdmiphy "
"from i2c adapter successfully\n");
return 0;
}
static struct of_device_id hdmiphy_match_types[] = {
{
.compatible = "samsung,exynos5-hdmiphy",
}, {
.compatible = "samsung,exynos4210-hdmiphy",
}, {
.compatible = "samsung,exynos4212-hdmiphy",
}, {
/* end node */
}
};
struct i2c_driver hdmiphy_driver = {
.driver = {
.name = "exynos-hdmiphy",
.owner = THIS_MODULE,
.of_match_table = hdmiphy_match_types,
},
.probe = hdmiphy_probe,
.remove = hdmiphy_remove,
.command = NULL,
};
EXPORT_SYMBOL(hdmiphy_driver);
......@@ -31,6 +31,7 @@
#include <linux/clk.h>
#include <linux/regulator/consumer.h>
#include <linux/of.h>
#include <linux/component.h>
#include <drm/exynos_drm.h>
......@@ -830,13 +831,15 @@ static int vp_resources_init(struct mixer_context *mixer_ctx)
}
static int mixer_initialize(struct exynos_drm_manager *mgr,
struct drm_device *drm_dev, int pipe)
struct drm_device *drm_dev)
{
int ret;
struct mixer_context *mixer_ctx = mgr->ctx;
struct exynos_drm_private *priv;
priv = drm_dev->dev_private;
mixer_ctx->drm_dev = drm_dev;
mixer_ctx->pipe = pipe;
mgr->drm_dev = mixer_ctx->drm_dev = drm_dev;
mgr->pipe = mixer_ctx->pipe = priv->pipe++;
/* acquire resources: regs, irqs, clocks */
ret = mixer_resources_init(mixer_ctx);
......@@ -1142,8 +1145,6 @@ int mixer_check_mode(struct drm_display_mode *mode)
}
static struct exynos_drm_manager_ops mixer_manager_ops = {
.initialize = mixer_initialize,
.remove = mixer_mgr_remove,
.dpms = mixer_dpms,
.enable_vblank = mixer_enable_vblank,
.disable_vblank = mixer_disable_vblank,
......@@ -1200,11 +1201,13 @@ static struct of_device_id mixer_match_types[] = {
}
};
static int mixer_probe(struct platform_device *pdev)
static int mixer_bind(struct device *dev, struct device *manager, void *data)
{
struct device *dev = &pdev->dev;
struct platform_device *pdev = to_platform_device(dev);
struct drm_device *drm_dev = data;
struct mixer_context *ctx;
struct mixer_drv_data *drv;
int ret;
dev_info(dev, "probe start\n");
......@@ -1233,19 +1236,61 @@ static int mixer_probe(struct platform_device *pdev)
atomic_set(&ctx->wait_vsync_event, 0);
mixer_manager.ctx = ctx;
ret = mixer_initialize(&mixer_manager, drm_dev);
if (ret)
return ret;
platform_set_drvdata(pdev, &mixer_manager);
exynos_drm_manager_register(&mixer_manager);
ret = exynos_drm_crtc_create(&mixer_manager);
if (ret) {
mixer_mgr_remove(&mixer_manager);
return ret;
}
pm_runtime_enable(dev);
return 0;
}
static int mixer_remove(struct platform_device *pdev)
static void mixer_unbind(struct device *dev, struct device *master, void *data)
{
dev_info(&pdev->dev, "remove successful\n");
struct exynos_drm_manager *mgr = dev_get_drvdata(dev);
struct drm_crtc *crtc = mgr->crtc;
dev_info(dev, "remove successful\n");
mixer_mgr_remove(mgr);
pm_runtime_disable(dev);
pm_runtime_disable(&pdev->dev);
crtc->funcs->destroy(crtc);
}
static const struct component_ops mixer_component_ops = {
.bind = mixer_bind,
.unbind = mixer_unbind,
};
static int mixer_probe(struct platform_device *pdev)
{
int ret;
ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC,
mixer_manager.type);
if (ret)
return ret;
ret = component_add(&pdev->dev, &mixer_component_ops);
if (ret)
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
return ret;
}
static int mixer_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &mixer_component_ops);
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
return 0;
}
......
......@@ -578,4 +578,20 @@
#define HDMI_TG_VACT_ST4_H HDMI_TG_BASE(0x0074)
#define HDMI_TG_3D HDMI_TG_BASE(0x00F0)
/* HDMI PHY Registers Offsets*/
#define HDMIPHY_POWER (0x74 >> 2)
#define HDMIPHY_MODE_SET_DONE (0x7c >> 2)
/* HDMI PHY Values */
#define HDMI_PHY_POWER_ON 0x80
#define HDMI_PHY_POWER_OFF 0xff
/* HDMI PHY Values */
#define HDMI_PHY_DISABLE_MODE_SET 0x80
#define HDMI_PHY_ENABLE_MODE_SET 0x00
/* PMU Registers for PHY */
#define PMU_HDMI_PHY_CONTROL 0x700
#define PMU_HDMI_PHY_ENABLE_BIT BIT(0)
#endif /* SAMSUNG_REGS_HDMI_H */
......@@ -310,6 +310,8 @@
# define DP_TEST_NAK (1 << 1)
# define DP_TEST_EDID_CHECKSUM_WRITE (1 << 2)
#define DP_TEST_EDID_CHECKSUM 0x261
#define DP_TEST_SINK 0x270
#define DP_TEST_SINK_START (1 << 0)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment