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: ...@@ -62,6 +62,10 @@ Optional properties for dp-controller:
-hsync-active-high: -hsync-active-high:
HSYNC polarity configuration. HSYNC polarity configuration.
High if defined, Low if not defined High if defined, Low if not defined
-samsung,hpd-gpio:
Hotplug detect GPIO.
Indicates which GPIO should be used for hotplug
detection
Example: Example:
......
...@@ -5,6 +5,7 @@ Required properties: ...@@ -5,6 +5,7 @@ Required properties:
1) "samsung,exynos5-hdmi" <DEPRECATED> 1) "samsung,exynos5-hdmi" <DEPRECATED>
2) "samsung,exynos4210-hdmi" 2) "samsung,exynos4210-hdmi"
3) "samsung,exynos4212-hdmi" 3) "samsung,exynos4212-hdmi"
4) "samsung,exynos5420-hdmi"
- reg: physical base address of the hdmi and length of memory mapped - reg: physical base address of the hdmi and length of memory mapped
region. region.
- interrupts: interrupt number to the cpu. - interrupts: interrupt number to the cpu.
...@@ -27,6 +28,7 @@ Required properties: ...@@ -27,6 +28,7 @@ Required properties:
"hdmi", "sclk_hdmi", "sclk_pixel", "sclk_hdmiphy" and "mout_hdmi". "hdmi", "sclk_hdmi", "sclk_pixel", "sclk_hdmiphy" and "mout_hdmi".
- ddc: phandle to the hdmi ddc node - ddc: phandle to the hdmi ddc node
- phy: phandle to the hdmi phy node - phy: phandle to the hdmi phy node
- samsung,syscon-phandle: phandle for system controller node for PMU.
Example: Example:
...@@ -37,4 +39,5 @@ Example: ...@@ -37,4 +39,5 @@ Example:
hpd-gpio = <&gpx3 7 1>; hpd-gpio = <&gpx3 7 1>;
ddc = <&hdmi_ddc_node>; ddc = <&hdmi_ddc_node>;
phy = <&hdmi_phy_node>; phy = <&hdmi_phy_node>;
samsung,syscon-phandle = <&pmu_system_controller>;
}; };
...@@ -83,6 +83,8 @@ config DRM_KMS_CMA_HELPER ...@@ -83,6 +83,8 @@ config DRM_KMS_CMA_HELPER
source "drivers/gpu/drm/i2c/Kconfig" source "drivers/gpu/drm/i2c/Kconfig"
source "drivers/gpu/drm/bridge/Kconfig"
config DRM_TDFX config DRM_TDFX
tristate "3dfx Banshee/Voodoo3+" tristate "3dfx Banshee/Voodoo3+"
depends on DRM && PCI depends on DRM && PCI
...@@ -199,5 +201,3 @@ source "drivers/gpu/drm/msm/Kconfig" ...@@ -199,5 +201,3 @@ source "drivers/gpu/drm/msm/Kconfig"
source "drivers/gpu/drm/tegra/Kconfig" source "drivers/gpu/drm/tegra/Kconfig"
source "drivers/gpu/drm/panel/Kconfig" source "drivers/gpu/drm/panel/Kconfig"
source "drivers/gpu/drm/bridge/Kconfig"
...@@ -26,14 +26,14 @@ config DRM_EXYNOS_DMABUF ...@@ -26,14 +26,14 @@ config DRM_EXYNOS_DMABUF
config DRM_EXYNOS_FIMD config DRM_EXYNOS_FIMD
bool "Exynos DRM FIMD" bool "Exynos DRM FIMD"
depends on DRM_EXYNOS && !FB_S3C && !ARCH_MULTIPLATFORM depends on DRM_EXYNOS && !FB_S3C
select FB_MODE_HELPERS select FB_MODE_HELPERS
help help
Choose this option if you want to use Exynos FIMD for DRM. Choose this option if you want to use Exynos FIMD for DRM.
config DRM_EXYNOS_DPI config DRM_EXYNOS_DPI
bool "EXYNOS DRM parallel output support" bool "EXYNOS DRM parallel output support"
depends on DRM_EXYNOS depends on DRM_EXYNOS_FIMD
select DRM_PANEL select DRM_PANEL
default n default n
help help
...@@ -41,7 +41,7 @@ config DRM_EXYNOS_DPI ...@@ -41,7 +41,7 @@ config DRM_EXYNOS_DPI
config DRM_EXYNOS_DSI config DRM_EXYNOS_DSI
bool "EXYNOS DRM MIPI-DSI driver support" bool "EXYNOS DRM MIPI-DSI driver support"
depends on DRM_EXYNOS depends on DRM_EXYNOS_FIMD
select DRM_MIPI_DSI select DRM_MIPI_DSI
select DRM_PANEL select DRM_PANEL
default n default n
...@@ -50,7 +50,7 @@ config DRM_EXYNOS_DSI ...@@ -50,7 +50,7 @@ config DRM_EXYNOS_DSI
config DRM_EXYNOS_DP config DRM_EXYNOS_DP
bool "EXYNOS DRM DP driver support" 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 default DRM_EXYNOS
help help
This enables support for DP device. 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 @@ ...@@ -14,6 +14,7 @@
#define _EXYNOS_DP_CORE_H #define _EXYNOS_DP_CORE_H
#include <drm/drm_crtc.h> #include <drm/drm_crtc.h>
#include <drm/drm_dp_helper.h>
#include <drm/exynos_drm.h> #include <drm/exynos_drm.h>
#define DP_TIMEOUT_LOOP_COUNT 100 #define DP_TIMEOUT_LOOP_COUNT 100
...@@ -159,6 +160,7 @@ struct exynos_dp_device { ...@@ -159,6 +160,7 @@ struct exynos_dp_device {
struct work_struct hotplug_work; struct work_struct hotplug_work;
struct phy *phy; struct phy *phy;
int dpms_mode; int dpms_mode;
int hpd_gpio;
struct exynos_drm_panel_info panel; struct exynos_drm_panel_info panel;
}; };
...@@ -261,69 +263,17 @@ void exynos_dp_disable_scrambling(struct exynos_dp_device *dp); ...@@ -261,69 +263,17 @@ void exynos_dp_disable_scrambling(struct exynos_dp_device *dp);
#define EDID_EXTENSION_FLAG 0x7e #define EDID_EXTENSION_FLAG 0x7e
#define EDID_CHECKSUM 0x7f #define EDID_CHECKSUM 0x7f
/* Definition for DPCD Register */ /* DP_MAX_LANE_COUNT */
#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 */
#define DPCD_ENHANCED_FRAME_CAP(x) (((x) >> 7) & 0x1) #define DPCD_ENHANCED_FRAME_CAP(x) (((x) >> 7) & 0x1)
#define DPCD_MAX_LANE_COUNT(x) ((x) & 0x1f) #define DPCD_MAX_LANE_COUNT(x) ((x) & 0x1f)
/* DPCD_ADDR_LANE_COUNT_SET */ /* DP_LANE_COUNT_SET */
#define DPCD_ENHANCED_FRAME_EN (0x1 << 7)
#define DPCD_LANE_COUNT_SET(x) ((x) & 0x1f) #define DPCD_LANE_COUNT_SET(x) ((x) & 0x1f)
/* DPCD_ADDR_TRAINING_PATTERN_SET */ /* DP_TRAINING_LANE0_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)
#define DPCD_PRE_EMPHASIS_SET(x) (((x) & 0x3) << 3) #define DPCD_PRE_EMPHASIS_SET(x) (((x) & 0x3) << 3)
#define DPCD_PRE_EMPHASIS_GET(x) (((x) >> 3) & 0x3) #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_SET(x) (((x) & 0x3) << 0)
#define DPCD_VOLTAGE_SWING_GET(x) (((x) >> 0) & 0x3) #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 */ #endif /* _EXYNOS_DP_CORE_H */
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/gpio.h>
#include "exynos_dp_core.h" #include "exynos_dp_core.h"
#include "exynos_dp_reg.h" #include "exynos_dp_reg.h"
...@@ -326,6 +327,9 @@ void exynos_dp_clear_hotplug_interrupts(struct exynos_dp_device *dp) ...@@ -326,6 +327,9 @@ void exynos_dp_clear_hotplug_interrupts(struct exynos_dp_device *dp)
{ {
u32 reg; u32 reg;
if (gpio_is_valid(dp->hpd_gpio))
return;
reg = HOTPLUG_CHG | HPD_LOST | PLUG; reg = HOTPLUG_CHG | HPD_LOST | PLUG;
writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4); 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) ...@@ -337,6 +341,9 @@ void exynos_dp_init_hpd(struct exynos_dp_device *dp)
{ {
u32 reg; u32 reg;
if (gpio_is_valid(dp->hpd_gpio))
return;
exynos_dp_clear_hotplug_interrupts(dp); exynos_dp_clear_hotplug_interrupts(dp);
reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3); 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) ...@@ -348,6 +355,13 @@ enum dp_irq_type exynos_dp_get_irq_type(struct exynos_dp_device *dp)
{ {
u32 reg; 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 */ /* Parse hotplug interrupt status register */
reg = readl(dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4); 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) ...@@ -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_HP_CHANGE;
return DP_IRQ_TYPE_UNKNOWN; return DP_IRQ_TYPE_UNKNOWN;
}
} }
void exynos_dp_reset_aux(struct exynos_dp_device *dp) void exynos_dp_reset_aux(struct exynos_dp_device *dp)
...@@ -386,7 +401,7 @@ void exynos_dp_init_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 */ /* Disable AUX transaction H/W retry */
reg = AUX_BIT_PERIOD_EXPECTED_DELAY(3) | AUX_HW_RETRY_COUNT_SEL(0)| reg = AUX_BIT_PERIOD_EXPECTED_DELAY(3) | AUX_HW_RETRY_COUNT_SEL(0)|
AUX_HW_RETRY_INTERVAL_600_MICROSECONDS; 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 */ /* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */
reg = DEFER_CTRL_EN | DEFER_COUNT(1); reg = DEFER_CTRL_EN | DEFER_COUNT(1);
...@@ -402,9 +417,14 @@ int exynos_dp_get_plug_in_status(struct exynos_dp_device *dp) ...@@ -402,9 +417,14 @@ int exynos_dp_get_plug_in_status(struct exynos_dp_device *dp)
{ {
u32 reg; 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); reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3);
if (reg & HPD_STATUS) if (reg & HPD_STATUS)
return 0; return 0;
}
return -EINVAL; return -EINVAL;
} }
......
...@@ -19,21 +19,19 @@ ...@@ -19,21 +19,19 @@
#include "exynos_drm_fbdev.h" #include "exynos_drm_fbdev.h"
static LIST_HEAD(exynos_drm_subdrv_list); 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 exynos_drm_display *display)
{ {
struct drm_encoder *encoder; struct drm_encoder *encoder;
struct exynos_drm_manager *manager;
int ret; int ret;
unsigned long possible_crtcs = 0; unsigned long possible_crtcs = 0;
/* Find possible crtcs for this display */ ret = exynos_drm_crtc_get_pipe_from_type(dev, display->type);
list_for_each_entry(manager, &exynos_drm_manager_list, list) if (ret < 0)
if (manager->type == display->type) return ret;
possible_crtcs |= 1 << manager->pipe;
possible_crtcs |= 1 << ret;
/* create and initialize a encoder for this sub driver. */ /* create and initialize a encoder for this sub driver. */
encoder = exynos_drm_encoder_create(dev, display, possible_crtcs); encoder = exynos_drm_encoder_create(dev, display, possible_crtcs);
...@@ -57,127 +55,29 @@ static int exynos_drm_create_enc_conn(struct drm_device *dev, ...@@ -57,127 +55,29 @@ static int exynos_drm_create_enc_conn(struct drm_device *dev,
return ret; return ret;
} }
static int exynos_drm_subdrv_probe(struct drm_device *dev, int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv)
struct exynos_drm_subdrv *subdrv)
{ {
if (subdrv->probe) { if (!subdrv)
int ret; return -EINVAL;
subdrv->drm_dev = dev;
/* list_add_tail(&subdrv->list, &exynos_drm_subdrv_list);
* 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;
}
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(exynos_drm_subdrv_register);
static void exynos_drm_subdrv_remove(struct drm_device *dev, int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *subdrv)
struct exynos_drm_subdrv *subdrv)
{
if (subdrv->remove)
subdrv->remove(dev, subdrv->dev);
}
int exynos_drm_initialize_managers(struct drm_device *dev)
{ {
struct exynos_drm_manager *manager, *n; if (!subdrv)
int ret, pipe = 0; return -EINVAL;
list_for_each_entry(manager, &exynos_drm_manager_list, list) {
if (manager->ops->initialize) {
ret = manager->ops->initialize(manager, dev, pipe);
if (ret) {
DRM_ERROR("Mgr init [%d] failed with %d\n",
manager->type, ret);
goto err;
}
}
manager->drm_dev = dev; list_del(&subdrv->list);
manager->pipe = pipe++;
ret = exynos_drm_crtc_create(manager);
if (ret) {
DRM_ERROR("CRTC create [%d] failed with %d\n",
manager->type, ret);
goto err;
}
}
return 0; 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; struct exynos_drm_subdrv *subdrv, *n;
int err; int err;
...@@ -186,19 +86,28 @@ int exynos_drm_device_register(struct drm_device *dev) ...@@ -186,19 +86,28 @@ int exynos_drm_device_register(struct drm_device *dev)
return -EINVAL; return -EINVAL;
list_for_each_entry_safe(subdrv, n, &exynos_drm_subdrv_list, list) { 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) { if (err) {
DRM_DEBUG("exynos drm subdrv probe failed.\n"); DRM_DEBUG("exynos drm subdrv probe failed.\n");
list_del(&subdrv->list); list_del(&subdrv->list);
continue; continue;
} }
} }
}
return 0; 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; struct exynos_drm_subdrv *subdrv;
...@@ -208,66 +117,13 @@ int exynos_drm_device_unregister(struct drm_device *dev) ...@@ -208,66 +117,13 @@ int exynos_drm_device_unregister(struct drm_device *dev)
} }
list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) { 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; return 0;
} }
EXPORT_SYMBOL_GPL(exynos_drm_device_unregister); EXPORT_SYMBOL_GPL(exynos_drm_device_subdrv_remove);
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);
int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file) 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) ...@@ -368,6 +368,7 @@ int exynos_drm_crtc_create(struct exynos_drm_manager *manager)
return -ENOMEM; return -ENOMEM;
} }
manager->crtc = &exynos_crtc->drm_crtc;
crtc = &exynos_crtc->drm_crtc; crtc = &exynos_crtc->drm_crtc;
private->crtc[manager->pipe] = crtc; private->crtc[manager->pipe] = crtc;
...@@ -491,3 +492,19 @@ void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb) ...@@ -491,3 +492,19 @@ void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb)
manager->ops->wait_for_vblank(manager); 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); ...@@ -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_enable(struct drm_crtc *crtc, int zpos);
void exynos_drm_crtc_plane_disable(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 #endif
...@@ -40,20 +40,10 @@ exynos_dpi_detect(struct drm_connector *connector, bool force) ...@@ -40,20 +40,10 @@ exynos_dpi_detect(struct drm_connector *connector, bool force)
{ {
struct exynos_dpi *ctx = connector_to_dpi(connector); struct exynos_dpi *ctx = connector_to_dpi(connector);
/* panels supported only by boot-loader are always connected */ if (!ctx->panel->connector)
if (!ctx->panel_node)
return connector_status_connected;
if (!ctx->panel) {
ctx->panel = of_drm_find_panel(ctx->panel_node);
if (ctx->panel)
drm_panel_attach(ctx->panel, &ctx->connector); drm_panel_attach(ctx->panel, &ctx->connector);
}
if (ctx->panel)
return connector_status_connected; return connector_status_connected;
return connector_status_disconnected;
} }
static void exynos_dpi_connector_destroy(struct drm_connector *connector) 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, ...@@ -116,9 +106,6 @@ static int exynos_dpi_create_connector(struct exynos_drm_display *display,
ctx->encoder = encoder; ctx->encoder = encoder;
if (ctx->panel_node)
connector->polled = DRM_CONNECTOR_POLL_CONNECT;
else
connector->polled = DRM_CONNECTOR_POLL_HPD; connector->polled = DRM_CONNECTOR_POLL_HPD;
ret = drm_connector_init(encoder->dev, connector, ret = drm_connector_init(encoder->dev, connector,
...@@ -287,8 +274,10 @@ static int exynos_dpi_parse_dt(struct exynos_dpi *ctx) ...@@ -287,8 +274,10 @@ static int exynos_dpi_parse_dt(struct exynos_dpi *ctx)
return -ENOMEM; return -ENOMEM;
ret = of_get_videomode(dn, vm, 0); ret = of_get_videomode(dn, vm, 0);
if (ret < 0) if (ret < 0) {
devm_kfree(dev, vm);
return ret; return ret;
}
ctx->vm = vm; ctx->vm = vm;
...@@ -301,32 +290,58 @@ static int exynos_dpi_parse_dt(struct exynos_dpi *ctx) ...@@ -301,32 +290,58 @@ static int exynos_dpi_parse_dt(struct exynos_dpi *ctx)
return 0; return 0;
} }
int exynos_dpi_probe(struct device *dev) struct exynos_drm_display *exynos_dpi_probe(struct device *dev)
{ {
struct exynos_dpi *ctx; struct exynos_dpi *ctx;
int ret; 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); ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx) if (!ctx)
return -ENOMEM; goto err_del_component;
ctx->dev = dev; ctx->dev = dev;
exynos_dpi_display.ctx = ctx; exynos_dpi_display.ctx = ctx;
ctx->dpms_mode = DRM_MODE_DPMS_OFF; ctx->dpms_mode = DRM_MODE_DPMS_OFF;
ret = exynos_dpi_parse_dt(ctx); ret = exynos_dpi_parse_dt(ctx);
if (ret < 0) if (ret < 0) {
return ret; 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) 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_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; return 0;
} }
This diff is collapsed.
...@@ -42,6 +42,13 @@ struct drm_connector; ...@@ -42,6 +42,13 @@ struct drm_connector;
extern unsigned int drm_vblank_offdelay; 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. */ /* this enumerates display type. */
enum exynos_drm_output_type { enum exynos_drm_output_type {
EXYNOS_DISPLAY_TYPE_NONE, EXYNOS_DISPLAY_TYPE_NONE,
...@@ -122,7 +129,6 @@ struct exynos_drm_overlay { ...@@ -122,7 +129,6 @@ struct exynos_drm_overlay {
* Exynos DRM Display Structure. * Exynos DRM Display Structure.
* - this structure is common to analog tv, digital tv and lcd panel. * - 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 * @remove: cleans up the display for removal
* @mode_fixup: fix mode data comparing to hw specific display mode. * @mode_fixup: fix mode data comparing to hw specific display mode.
* @mode_set: convert drm_display_mode to hw specific display mode and * @mode_set: convert drm_display_mode to hw specific display mode and
...@@ -133,8 +139,6 @@ struct exynos_drm_overlay { ...@@ -133,8 +139,6 @@ struct exynos_drm_overlay {
*/ */
struct exynos_drm_display; struct exynos_drm_display;
struct exynos_drm_display_ops { 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, int (*create_connector)(struct exynos_drm_display *display,
struct drm_encoder *encoder); struct drm_encoder *encoder);
void (*remove)(struct exynos_drm_display *display); void (*remove)(struct exynos_drm_display *display);
...@@ -172,8 +176,6 @@ struct exynos_drm_display { ...@@ -172,8 +176,6 @@ struct exynos_drm_display {
/* /*
* Exynos drm manager ops * Exynos drm manager ops
* *
* @initialize: initializes the manager with drm_dev
* @remove: cleans up the manager for removal
* @dpms: control device power. * @dpms: control device power.
* @mode_fixup: fix mode data before applying it * @mode_fixup: fix mode data before applying it
* @mode_set: set the given mode to the manager * @mode_set: set the given mode to the manager
...@@ -189,9 +191,6 @@ struct exynos_drm_display { ...@@ -189,9 +191,6 @@ struct exynos_drm_display {
*/ */
struct exynos_drm_manager; struct exynos_drm_manager;
struct exynos_drm_manager_ops { 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); void (*dpms)(struct exynos_drm_manager *mgr, int mode);
bool (*mode_fixup)(struct exynos_drm_manager *mgr, bool (*mode_fixup)(struct exynos_drm_manager *mgr,
const struct drm_display_mode *mode, const struct drm_display_mode *mode,
...@@ -215,6 +214,7 @@ struct exynos_drm_manager_ops { ...@@ -215,6 +214,7 @@ struct exynos_drm_manager_ops {
* @list: the list entry for this manager * @list: the list entry for this manager
* @type: one of EXYNOS_DISPLAY_TYPE_LCD and HDMI. * @type: one of EXYNOS_DISPLAY_TYPE_LCD and HDMI.
* @drm_dev: pointer to the drm device * @drm_dev: pointer to the drm device
* @crtc: crtc object.
* @pipe: the pipe number for this crtc/manager * @pipe: the pipe number for this crtc/manager
* @ops: pointer to callbacks for exynos drm specific functionality * @ops: pointer to callbacks for exynos drm specific functionality
* @ctx: A pointer to the manager's implementation specific context * @ctx: A pointer to the manager's implementation specific context
...@@ -223,6 +223,7 @@ struct exynos_drm_manager { ...@@ -223,6 +223,7 @@ struct exynos_drm_manager {
struct list_head list; struct list_head list;
enum exynos_drm_output_type type; enum exynos_drm_output_type type;
struct drm_device *drm_dev; struct drm_device *drm_dev;
struct drm_crtc *crtc;
int pipe; int pipe;
struct exynos_drm_manager_ops *ops; struct exynos_drm_manager_ops *ops;
void *ctx; void *ctx;
...@@ -254,6 +255,7 @@ struct drm_exynos_file_private { ...@@ -254,6 +255,7 @@ struct drm_exynos_file_private {
* otherwise default one. * otherwise default one.
* @da_space_size: size of device address space. * @da_space_size: size of device address space.
* if 0 then default value is used for it. * if 0 then default value is used for it.
* @pipe: the pipe number for this crtc/manager.
*/ */
struct exynos_drm_private { struct exynos_drm_private {
struct drm_fb_helper *fb_helper; struct drm_fb_helper *fb_helper;
...@@ -271,6 +273,8 @@ struct exynos_drm_private { ...@@ -271,6 +273,8 @@ struct exynos_drm_private {
unsigned long da_start; unsigned long da_start;
unsigned long da_space_size; unsigned long da_space_size;
unsigned int pipe;
}; };
/* /*
...@@ -302,39 +306,14 @@ struct exynos_drm_subdrv { ...@@ -302,39 +306,14 @@ struct exynos_drm_subdrv {
struct drm_file *file); struct drm_file *file);
}; };
/* /* This function would be called by non kms drivers such as g2d and ipp. */
* 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.
*/
int exynos_drm_subdrv_register(struct exynos_drm_subdrv *drm_subdrv); int exynos_drm_subdrv_register(struct exynos_drm_subdrv *drm_subdrv);
/* this function removes subdrv list from exynos drm driver */ /* this function removes subdrv list from exynos drm driver */
int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *drm_subdrv); 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); 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); void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file);
...@@ -360,18 +339,40 @@ int exynos_platform_device_ipp_register(void); ...@@ -360,18 +339,40 @@ int exynos_platform_device_ipp_register(void);
void exynos_platform_device_ipp_unregister(void); void exynos_platform_device_ipp_unregister(void);
#ifdef CONFIG_DRM_EXYNOS_DPI #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); int exynos_dpi_remove(struct device *dev);
#else #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; } static inline int exynos_dpi_remove(struct device *dev) { return 0; }
#endif #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 dp_driver;
extern struct platform_driver dsi_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 mixer_driver;
extern struct platform_driver hdmi_driver;
extern struct platform_driver exynos_drm_common_hdmi_driver; extern struct platform_driver exynos_drm_common_hdmi_driver;
extern struct platform_driver vidi_driver; extern struct platform_driver vidi_driver;
extern struct platform_driver g2d_driver; extern struct platform_driver g2d_driver;
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/phy/phy.h> #include <linux/phy/phy.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/component.h>
#include <video/mipi_display.h> #include <video/mipi_display.h>
#include <video/videomode.h> #include <video/videomode.h>
...@@ -1378,16 +1379,60 @@ static int exynos_dsi_parse_dt(struct exynos_dsi *dsi) ...@@ -1378,16 +1379,60 @@ static int exynos_dsi_parse_dt(struct exynos_dsi *dsi)
return ret; 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) static int exynos_dsi_probe(struct platform_device *pdev)
{ {
struct resource *res; struct resource *res;
struct exynos_dsi *dsi; struct exynos_dsi *dsi;
int ret; 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); dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL);
if (!dsi) { if (!dsi) {
dev_err(&pdev->dev, "failed to allocate dsi object.\n"); dev_err(&pdev->dev, "failed to allocate dsi object.\n");
return -ENOMEM; ret = -ENOMEM;
goto err_del_component;
} }
init_completion(&dsi->completed); init_completion(&dsi->completed);
...@@ -1401,7 +1446,7 @@ static int exynos_dsi_probe(struct platform_device *pdev) ...@@ -1401,7 +1446,7 @@ static int exynos_dsi_probe(struct platform_device *pdev)
ret = exynos_dsi_parse_dt(dsi); ret = exynos_dsi_parse_dt(dsi);
if (ret) if (ret)
return ret; goto err_del_component;
dsi->supplies[0].supply = "vddcore"; dsi->supplies[0].supply = "vddcore";
dsi->supplies[1].supply = "vddio"; dsi->supplies[1].supply = "vddio";
...@@ -1415,32 +1460,37 @@ static int exynos_dsi_probe(struct platform_device *pdev) ...@@ -1415,32 +1460,37 @@ static int exynos_dsi_probe(struct platform_device *pdev)
dsi->pll_clk = devm_clk_get(&pdev->dev, "pll_clk"); dsi->pll_clk = devm_clk_get(&pdev->dev, "pll_clk");
if (IS_ERR(dsi->pll_clk)) { if (IS_ERR(dsi->pll_clk)) {
dev_info(&pdev->dev, "failed to get dsi pll input clock\n"); 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"); dsi->bus_clk = devm_clk_get(&pdev->dev, "bus_clk");
if (IS_ERR(dsi->bus_clk)) { if (IS_ERR(dsi->bus_clk)) {
dev_info(&pdev->dev, "failed to get dsi bus clock\n"); 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); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dsi->reg_base = devm_ioremap_resource(&pdev->dev, res); dsi->reg_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(dsi->reg_base)) { if (IS_ERR(dsi->reg_base)) {
dev_err(&pdev->dev, "failed to remap io region\n"); 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"); dsi->phy = devm_phy_get(&pdev->dev, "dsim");
if (IS_ERR(dsi->phy)) { if (IS_ERR(dsi->phy)) {
dev_info(&pdev->dev, "failed to get dsim phy\n"); 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); dsi->irq = platform_get_irq(pdev, 0);
if (dsi->irq < 0) { if (dsi->irq < 0) {
dev_err(&pdev->dev, "failed to request dsi irq resource\n"); 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); irq_set_status_flags(dsi->irq, IRQ_NOAUTOEN);
...@@ -1449,58 +1499,31 @@ static int exynos_dsi_probe(struct platform_device *pdev) ...@@ -1449,58 +1499,31 @@ static int exynos_dsi_probe(struct platform_device *pdev)
dev_name(&pdev->dev), dsi); dev_name(&pdev->dev), dsi);
if (ret) { if (ret) {
dev_err(&pdev->dev, "failed to request dsi irq\n"); dev_err(&pdev->dev, "failed to request dsi irq\n");
return ret; goto err_del_component;
} }
exynos_dsi_display.ctx = dsi; exynos_dsi_display.ctx = dsi;
platform_set_drvdata(pdev, &exynos_dsi_display); platform_set_drvdata(pdev, &exynos_dsi_display);
exynos_drm_display_register(&exynos_dsi_display);
return mipi_dsi_host_register(&dsi->dsi_host); ret = component_add(&pdev->dev, &exynos_dsi_component_ops);
} if (ret)
goto err_del_component;
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;
if (dsi->state & DSIM_STATE_ENABLED) { return ret;
dsi->state &= ~DSIM_STATE_ENABLED;
exynos_dsi_enable(dsi);
}
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; component_del(&pdev->dev, &exynos_dsi_component_ops);
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
if (dsi->state & DSIM_STATE_ENABLED) {
exynos_dsi_disable(dsi);
dsi->state |= DSIM_STATE_ENABLED;
}
return 0; 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[] = { static struct of_device_id exynos_dsi_of_match[] = {
{ .compatible = "samsung,exynos4210-mipi-dsi" }, { .compatible = "samsung,exynos4210-mipi-dsi" },
...@@ -1513,7 +1536,6 @@ struct platform_driver dsi_driver = { ...@@ -1513,7 +1536,6 @@ struct platform_driver dsi_driver = {
.driver = { .driver = {
.name = "exynos-dsi", .name = "exynos-dsi",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &exynos_dsi_pm_ops,
.of_match_table = exynos_dsi_of_match, .of_match_table = exynos_dsi_of_match,
}, },
}; };
......
...@@ -121,16 +121,8 @@ static int exynos_drm_fbdev_update(struct drm_fb_helper *helper, ...@@ -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.xoffset * (fb->bits_per_pixel >> 3);
offset += fbi->var.yoffset * fb->pitches[0]; offset += fbi->var.yoffset * fb->pitches[0];
dev->mode_config.fb_base = (resource_size_t)buffer->dma_addr;
fbi->screen_base = buffer->kvaddr + offset; 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->screen_size = size;
fbi->fix.smem_len = size;
return 0; return 0;
} }
...@@ -237,7 +229,7 @@ static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = { ...@@ -237,7 +229,7 @@ static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
.fb_probe = exynos_drm_fbdev_create, .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; struct drm_connector *connector;
bool ret = false; bool ret = false;
......
This diff is collapsed.
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/component.h>
#include <video/of_display_timing.h> #include <video/of_display_timing.h>
#include <video/of_videomode.h> #include <video/of_videomode.h>
...@@ -38,6 +39,7 @@ ...@@ -38,6 +39,7 @@
*/ */
#define FIMD_DEFAULT_FRAMERATE 60 #define FIMD_DEFAULT_FRAMERATE 60
#define MIN_FB_WIDTH_FOR_16WORD_BURST 128
/* position control register for hardware window 0, 2 ~ 4.*/ /* position control register for hardware window 0, 2 ~ 4.*/
#define VIDOSD_A(win) (VIDOSD_BASE + 0x00 + (win) * 16) #define VIDOSD_A(win) (VIDOSD_BASE + 0x00 + (win) * 16)
...@@ -122,6 +124,7 @@ struct fimd_context { ...@@ -122,6 +124,7 @@ struct fimd_context {
struct exynos_drm_panel_info panel; struct exynos_drm_panel_info panel;
struct fimd_driver_data *driver_data; struct fimd_driver_data *driver_data;
struct exynos_drm_display *display;
}; };
static const struct of_device_id fimd_driver_dt_match[] = { 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( ...@@ -143,13 +146,57 @@ static inline struct fimd_driver_data *drm_fimd_get_driver_data(
return (struct fimd_driver_data *)of_id->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, 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 fimd_context *ctx = mgr->ctx;
struct exynos_drm_private *priv;
priv = drm_dev->dev_private;
ctx->drm_dev = drm_dev; mgr->drm_dev = ctx->drm_dev = drm_dev;
ctx->pipe = pipe; mgr->pipe = ctx->pipe = priv->pipe++;
/* /*
* enable drm irq mode. * enable drm irq mode.
...@@ -169,8 +216,14 @@ static int fimd_mgr_initialize(struct exynos_drm_manager *mgr, ...@@ -169,8 +216,14 @@ static int fimd_mgr_initialize(struct exynos_drm_manager *mgr,
drm_dev->vblank_disable_allowed = true; drm_dev->vblank_disable_allowed = true;
/* attach this sub driver to iommu mapping if supported. */ /* 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); drm_iommu_attach_device(ctx->drm_dev, ctx->dev);
}
return 0; return 0;
} }
...@@ -324,25 +377,6 @@ static void fimd_disable_vblank(struct exynos_drm_manager *mgr) ...@@ -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, static void fimd_win_mode_set(struct exynos_drm_manager *mgr,
struct exynos_drm_overlay *overlay) struct exynos_drm_overlay *overlay)
{ {
...@@ -446,6 +480,19 @@ static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win) ...@@ -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); 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)); writel(val, ctx->regs + WINCON(win));
} }
...@@ -656,19 +703,6 @@ static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos) ...@@ -656,19 +703,6 @@ static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos)
win_data->enabled = false; 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) static void fimd_window_suspend(struct exynos_drm_manager *mgr)
{ {
struct fimd_context *ctx = mgr->ctx; struct fimd_context *ctx = mgr->ctx;
...@@ -803,8 +837,6 @@ static void fimd_dpms(struct exynos_drm_manager *mgr, int mode) ...@@ -803,8 +837,6 @@ static void fimd_dpms(struct exynos_drm_manager *mgr, int mode)
} }
static struct exynos_drm_manager_ops fimd_manager_ops = { static struct exynos_drm_manager_ops fimd_manager_ops = {
.initialize = fimd_mgr_initialize,
.remove = fimd_mgr_remove,
.dpms = fimd_dpms, .dpms = fimd_dpms,
.mode_fixup = fimd_mode_fixup, .mode_fixup = fimd_mode_fixup,
.mode_set = fimd_mode_set, .mode_set = fimd_mode_set,
...@@ -849,20 +881,64 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id) ...@@ -849,20 +881,64 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
return IRQ_HANDLED; 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) static int fimd_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct fimd_context *ctx; struct fimd_context *ctx;
struct resource *res; struct resource *res;
int win;
int ret = -EINVAL; int ret = -EINVAL;
if (!dev->of_node) ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC,
return -ENODEV; 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); ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx) if (!ctx) {
return -ENOMEM; ret = -ENOMEM;
goto err_del_component;
}
ctx->dev = dev; ctx->dev = dev;
ctx->suspended = true; ctx->suspended = true;
...@@ -875,32 +951,37 @@ static int fimd_probe(struct platform_device *pdev) ...@@ -875,32 +951,37 @@ static int fimd_probe(struct platform_device *pdev)
ctx->bus_clk = devm_clk_get(dev, "fimd"); ctx->bus_clk = devm_clk_get(dev, "fimd");
if (IS_ERR(ctx->bus_clk)) { if (IS_ERR(ctx->bus_clk)) {
dev_err(dev, "failed to get bus clock\n"); 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"); ctx->lcd_clk = devm_clk_get(dev, "sclk_fimd");
if (IS_ERR(ctx->lcd_clk)) { if (IS_ERR(ctx->lcd_clk)) {
dev_err(dev, "failed to get lcd clock\n"); 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); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ctx->regs = devm_ioremap_resource(dev, res); ctx->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(ctx->regs)) if (IS_ERR(ctx->regs)) {
return PTR_ERR(ctx->regs); ret = PTR_ERR(ctx->regs);
goto err_del_component;
}
res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "vsync"); res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "vsync");
if (!res) { if (!res) {
dev_err(dev, "irq request failed.\n"); 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, ret = devm_request_irq(dev, res->start, fimd_irq_handler,
0, "drm_fimd", ctx); 0, "drm_fimd", ctx);
if (ret) { if (ret) {
dev_err(dev, "irq request failed.\n"); dev_err(dev, "irq request failed.\n");
return ret; goto err_del_component;
} }
ctx->driver_data = drm_fimd_get_driver_data(pdev); ctx->driver_data = drm_fimd_get_driver_data(pdev);
...@@ -910,30 +991,34 @@ static int fimd_probe(struct platform_device *pdev) ...@@ -910,30 +991,34 @@ static int fimd_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, &fimd_manager); platform_set_drvdata(pdev, &fimd_manager);
fimd_manager.ctx = ctx; 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++) ret = component_add(&pdev->dev, &fimd_component_ops);
fimd_clear_win(ctx, win); 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) 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); pm_runtime_disable(&pdev->dev);
component_del(&pdev->dev, &fimd_component_ops);
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
return 0; return 0;
} }
......
...@@ -612,22 +612,20 @@ int exynos_drm_gem_dumb_create(struct drm_file *file_priv, ...@@ -612,22 +612,20 @@ int exynos_drm_gem_dumb_create(struct drm_file *file_priv,
args->pitch = args->width * ((args->bpp + 7) / 8); args->pitch = args->width * ((args->bpp + 7) / 8);
args->size = args->pitch * args->height; args->size = args->pitch * args->height;
exynos_gem_obj = exynos_drm_gem_create(dev, EXYNOS_BO_CONTIG | if (is_drm_iommu_supported(dev)) {
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");
exynos_gem_obj = exynos_drm_gem_create(dev, exynos_gem_obj = exynos_drm_gem_create(dev,
EXYNOS_BO_NONCONTIG | EXYNOS_BO_WC, EXYNOS_BO_NONCONTIG | EXYNOS_BO_WC,
args->size); 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); return PTR_ERR(exynos_gem_obj);
}
ret = exynos_drm_gem_handle_create(&exynos_gem_obj->base, file_priv, ret = exynos_drm_gem_handle_create(&exynos_gem_obj->base, file_priv,
&args->handle); &args->handle);
......
...@@ -1335,11 +1335,7 @@ static irqreturn_t gsc_irq_handler(int irq, void *dev_id) ...@@ -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) static int gsc_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
{ {
struct drm_exynos_ipp_prop_list *prop_list; struct drm_exynos_ipp_prop_list *prop_list = &ippdrv->prop_list;
prop_list = devm_kzalloc(ippdrv->dev, sizeof(*prop_list), GFP_KERNEL);
if (!prop_list)
return -ENOMEM;
prop_list->version = 1; prop_list->version = 1;
prop_list->writeback = 1; prop_list->writeback = 1;
...@@ -1363,8 +1359,6 @@ static int gsc_init_prop_list(struct exynos_drm_ippdrv *ippdrv) ...@@ -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.hsize = GSC_SCALE_MIN;
prop_list->scale_min.vsize = GSC_SCALE_MIN; prop_list->scale_min.vsize = GSC_SCALE_MIN;
ippdrv->prop_list = prop_list;
return 0; return 0;
} }
...@@ -1387,7 +1381,7 @@ static int gsc_ippdrv_check_property(struct device *dev, ...@@ -1387,7 +1381,7 @@ static int gsc_ippdrv_check_property(struct device *dev,
{ {
struct gsc_context *ctx = get_gsc_context(dev); struct gsc_context *ctx = get_gsc_context(dev);
struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; 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_ipp_config *config;
struct drm_exynos_pos *pos; struct drm_exynos_pos *pos;
struct drm_exynos_sz *sz; struct drm_exynos_sz *sz;
......
This diff is collapsed.
...@@ -52,7 +52,7 @@ struct drm_exynos_ipp_cmd_work { ...@@ -52,7 +52,7 @@ struct drm_exynos_ipp_cmd_work {
* @list: list head to command queue information. * @list: list head to command queue information.
* @event_list: list head of event. * @event_list: list head of event.
* @mem_list: list head to source,destination memory queue information. * @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. * @mem_lock: lock for synchronization of access to memory nodes.
* @event_lock: lock for synchronization of access to scheduled event. * @event_lock: lock for synchronization of access to scheduled event.
* @start_complete: completion of start of command. * @start_complete: completion of start of command.
...@@ -68,7 +68,7 @@ struct drm_exynos_ipp_cmd_node { ...@@ -68,7 +68,7 @@ struct drm_exynos_ipp_cmd_node {
struct list_head list; struct list_head list;
struct list_head event_list; struct list_head event_list;
struct list_head mem_list[EXYNOS_DRM_OPS_MAX]; struct list_head mem_list[EXYNOS_DRM_OPS_MAX];
struct mutex cmd_lock; struct mutex lock;
struct mutex mem_lock; struct mutex mem_lock;
struct mutex event_lock; struct mutex event_lock;
struct completion start_complete; struct completion start_complete;
...@@ -83,7 +83,7 @@ struct drm_exynos_ipp_cmd_node { ...@@ -83,7 +83,7 @@ struct drm_exynos_ipp_cmd_node {
/* /*
* A structure of buffer information. * 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. * @base: Y, Cb, Cr each planar address.
*/ */
struct drm_exynos_ipp_buf_info { struct drm_exynos_ipp_buf_info {
...@@ -142,12 +142,12 @@ struct exynos_drm_ipp_ops { ...@@ -142,12 +142,12 @@ struct exynos_drm_ipp_ops {
* @parent_dev: parent device information. * @parent_dev: parent device information.
* @dev: platform device. * @dev: platform device.
* @drm_dev: drm device. * @drm_dev: drm device.
* @ipp_id: id of ipp driver.
* @dedicated: dedicated ipp device. * @dedicated: dedicated ipp device.
* @ops: source, destination operations. * @ops: source, destination operations.
* @event_workq: event work queue. * @event_workq: event work queue.
* @c_node: current command information. * @c_node: current command information.
* @cmd_list: list head for 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. * @prop_list: property informations of current ipp driver.
* @check_property: check property about format, size, buffer. * @check_property: check property about format, size, buffer.
* @reset: reset ipp block. * @reset: reset ipp block.
...@@ -160,13 +160,13 @@ struct exynos_drm_ippdrv { ...@@ -160,13 +160,13 @@ struct exynos_drm_ippdrv {
struct device *parent_dev; struct device *parent_dev;
struct device *dev; struct device *dev;
struct drm_device *drm_dev; struct drm_device *drm_dev;
u32 ipp_id;
bool dedicated; bool dedicated;
struct exynos_drm_ipp_ops *ops[EXYNOS_DRM_OPS_MAX]; struct exynos_drm_ipp_ops *ops[EXYNOS_DRM_OPS_MAX];
struct workqueue_struct *event_workq; struct workqueue_struct *event_workq;
struct drm_exynos_ipp_cmd_node *c_node; struct drm_exynos_ipp_cmd_node *c_node;
struct list_head cmd_list; 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, int (*check_property)(struct device *dev,
struct drm_exynos_ipp_property *property); struct drm_exynos_ipp_property *property);
......
...@@ -158,8 +158,9 @@ static irqreturn_t rotator_irq_handler(int irq, void *arg) ...@@ -158,8 +158,9 @@ static irqreturn_t rotator_irq_handler(int irq, void *arg)
rot->cur_buf_id[EXYNOS_DRM_OPS_DST]; rot->cur_buf_id[EXYNOS_DRM_OPS_DST];
queue_work(ippdrv->event_workq, queue_work(ippdrv->event_workq,
(struct work_struct *)event_work); (struct work_struct *)event_work);
} else } else {
DRM_ERROR("the SFR is set illegally\n"); DRM_ERROR("the SFR is set illegally\n");
}
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -469,11 +470,7 @@ static struct exynos_drm_ipp_ops rot_dst_ops = { ...@@ -469,11 +470,7 @@ static struct exynos_drm_ipp_ops rot_dst_ops = {
static int rotator_init_prop_list(struct exynos_drm_ippdrv *ippdrv) static int rotator_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
{ {
struct drm_exynos_ipp_prop_list *prop_list; struct drm_exynos_ipp_prop_list *prop_list = &ippdrv->prop_list;
prop_list = devm_kzalloc(ippdrv->dev, sizeof(*prop_list), GFP_KERNEL);
if (!prop_list)
return -ENOMEM;
prop_list->version = 1; prop_list->version = 1;
prop_list->flip = (1 << EXYNOS_DRM_FLIP_VERTICAL) | prop_list->flip = (1 << EXYNOS_DRM_FLIP_VERTICAL) |
...@@ -486,8 +483,6 @@ static int rotator_init_prop_list(struct exynos_drm_ippdrv *ippdrv) ...@@ -486,8 +483,6 @@ static int rotator_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
prop_list->crop = 0; prop_list->crop = 0;
prop_list->scale = 0; prop_list->scale = 0;
ippdrv->prop_list = prop_list;
return 0; return 0;
} }
......
...@@ -51,6 +51,7 @@ struct vidi_context { ...@@ -51,6 +51,7 @@ struct vidi_context {
struct drm_crtc *crtc; struct drm_crtc *crtc;
struct drm_encoder *encoder; struct drm_encoder *encoder;
struct drm_connector connector; struct drm_connector connector;
struct exynos_drm_subdrv subdrv;
struct vidi_win_data win_data[WINDOWS_NR]; struct vidi_win_data win_data[WINDOWS_NR];
struct edid *raw_edid; struct edid *raw_edid;
unsigned int clkdiv; unsigned int clkdiv;
...@@ -294,14 +295,13 @@ static void vidi_dpms(struct exynos_drm_manager *mgr, int mode) ...@@ -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, 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 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); mgr->drm_dev = ctx->drm_dev = drm_dev;
mgr->pipe = ctx->pipe = priv->pipe++;
ctx->drm_dev = drm_dev;
ctx->pipe = pipe;
/* /*
* enable drm irq mode. * enable drm irq mode.
...@@ -324,7 +324,6 @@ static int vidi_mgr_initialize(struct exynos_drm_manager *mgr, ...@@ -324,7 +324,6 @@ static int vidi_mgr_initialize(struct exynos_drm_manager *mgr,
} }
static struct exynos_drm_manager_ops vidi_manager_ops = { static struct exynos_drm_manager_ops vidi_manager_ops = {
.initialize = vidi_mgr_initialize,
.dpms = vidi_dpms, .dpms = vidi_dpms,
.commit = vidi_commit, .commit = vidi_commit,
.enable_vblank = vidi_enable_vblank, .enable_vblank = vidi_enable_vblank,
...@@ -579,13 +578,38 @@ static struct exynos_drm_display vidi_display = { ...@@ -579,13 +578,38 @@ static struct exynos_drm_display vidi_display = {
.ops = &vidi_display_ops, .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) static int vidi_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct exynos_drm_subdrv *subdrv;
struct vidi_context *ctx; struct vidi_context *ctx;
int ret; int ret;
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx) if (!ctx)
return -ENOMEM; return -ENOMEM;
...@@ -600,28 +624,43 @@ static int vidi_probe(struct platform_device *pdev) ...@@ -600,28 +624,43 @@ static int vidi_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, &vidi_manager); platform_set_drvdata(pdev, &vidi_manager);
ret = device_create_file(dev, &dev_attr_connection); subdrv = &ctx->subdrv;
if (ret < 0) subdrv->dev = &pdev->dev;
DRM_INFO("failed to create connection sysfs.\n"); 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); ret = device_create_file(&pdev->dev, &dev_attr_connection);
exynos_drm_display_register(&vidi_display); if (ret < 0) {
exynos_drm_subdrv_unregister(subdrv);
DRM_INFO("failed to create connection sysfs.\n");
}
return 0; return 0;
} }
static int vidi_remove(struct platform_device *pdev) static int vidi_remove(struct platform_device *pdev)
{ {
struct vidi_context *ctx = platform_get_drvdata(pdev); struct exynos_drm_manager *mgr = platform_get_drvdata(pdev);
struct vidi_context *ctx = mgr->ctx;
exynos_drm_display_unregister(&vidi_display); struct drm_encoder *encoder = ctx->encoder;
exynos_drm_manager_unregister(&vidi_manager); struct drm_crtc *crtc = mgr->crtc;
if (ctx->raw_edid != (struct edid *)fake_edid_info) { if (ctx->raw_edid != (struct edid *)fake_edid_info) {
kfree(ctx->raw_edid); kfree(ctx->raw_edid);
ctx->raw_edid = NULL; ctx->raw_edid = NULL;
return -EINVAL;
} }
crtc->funcs->destroy(crtc);
encoder->funcs->destroy(encoder);
drm_connector_cleanup(&ctx->connector);
return 0; return 0;
} }
...@@ -633,3 +672,31 @@ struct platform_driver vidi_driver = { ...@@ -633,3 +672,31 @@ struct platform_driver vidi_driver = {
.owner = THIS_MODULE, .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 @@ ...@@ -31,6 +31,7 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/component.h>
#include <drm/exynos_drm.h> #include <drm/exynos_drm.h>
...@@ -830,13 +831,15 @@ static int vp_resources_init(struct mixer_context *mixer_ctx) ...@@ -830,13 +831,15 @@ static int vp_resources_init(struct mixer_context *mixer_ctx)
} }
static int mixer_initialize(struct exynos_drm_manager *mgr, static int mixer_initialize(struct exynos_drm_manager *mgr,
struct drm_device *drm_dev, int pipe) struct drm_device *drm_dev)
{ {
int ret; int ret;
struct mixer_context *mixer_ctx = mgr->ctx; struct mixer_context *mixer_ctx = mgr->ctx;
struct exynos_drm_private *priv;
priv = drm_dev->dev_private;
mixer_ctx->drm_dev = drm_dev; mgr->drm_dev = mixer_ctx->drm_dev = drm_dev;
mixer_ctx->pipe = pipe; mgr->pipe = mixer_ctx->pipe = priv->pipe++;
/* acquire resources: regs, irqs, clocks */ /* acquire resources: regs, irqs, clocks */
ret = mixer_resources_init(mixer_ctx); ret = mixer_resources_init(mixer_ctx);
...@@ -1142,8 +1145,6 @@ int mixer_check_mode(struct drm_display_mode *mode) ...@@ -1142,8 +1145,6 @@ int mixer_check_mode(struct drm_display_mode *mode)
} }
static struct exynos_drm_manager_ops mixer_manager_ops = { static struct exynos_drm_manager_ops mixer_manager_ops = {
.initialize = mixer_initialize,
.remove = mixer_mgr_remove,
.dpms = mixer_dpms, .dpms = mixer_dpms,
.enable_vblank = mixer_enable_vblank, .enable_vblank = mixer_enable_vblank,
.disable_vblank = mixer_disable_vblank, .disable_vblank = mixer_disable_vblank,
...@@ -1200,11 +1201,13 @@ static struct of_device_id mixer_match_types[] = { ...@@ -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_context *ctx;
struct mixer_drv_data *drv; struct mixer_drv_data *drv;
int ret;
dev_info(dev, "probe start\n"); dev_info(dev, "probe start\n");
...@@ -1233,19 +1236,61 @@ static int mixer_probe(struct platform_device *pdev) ...@@ -1233,19 +1236,61 @@ static int mixer_probe(struct platform_device *pdev)
atomic_set(&ctx->wait_vsync_event, 0); atomic_set(&ctx->wait_vsync_event, 0);
mixer_manager.ctx = ctx; mixer_manager.ctx = ctx;
ret = mixer_initialize(&mixer_manager, drm_dev);
if (ret)
return ret;
platform_set_drvdata(pdev, &mixer_manager); 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); pm_runtime_enable(dev);
return 0; 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; return 0;
} }
......
...@@ -578,4 +578,20 @@ ...@@ -578,4 +578,20 @@
#define HDMI_TG_VACT_ST4_H HDMI_TG_BASE(0x0074) #define HDMI_TG_VACT_ST4_H HDMI_TG_BASE(0x0074)
#define HDMI_TG_3D HDMI_TG_BASE(0x00F0) #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 */ #endif /* SAMSUNG_REGS_HDMI_H */
...@@ -310,6 +310,8 @@ ...@@ -310,6 +310,8 @@
# define DP_TEST_NAK (1 << 1) # define DP_TEST_NAK (1 << 1)
# define DP_TEST_EDID_CHECKSUM_WRITE (1 << 2) # define DP_TEST_EDID_CHECKSUM_WRITE (1 << 2)
#define DP_TEST_EDID_CHECKSUM 0x261
#define DP_TEST_SINK 0x270 #define DP_TEST_SINK 0x270
#define DP_TEST_SINK_START (1 << 0) #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