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,
};
...@@ -18,6 +18,9 @@ ...@@ -18,6 +18,9 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/component.h>
#include <linux/phy/phy.h> #include <linux/phy/phy.h>
#include <video/of_display_timing.h> #include <video/of_display_timing.h>
#include <video/of_videomode.h> #include <video/of_videomode.h>
...@@ -141,15 +144,15 @@ static int exynos_dp_read_edid(struct exynos_dp_device *dp) ...@@ -141,15 +144,15 @@ static int exynos_dp_read_edid(struct exynos_dp_device *dp)
return -EIO; return -EIO;
} }
exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_TEST_REQUEST, exynos_dp_read_byte_from_dpcd(dp, DP_TEST_REQUEST,
&test_vector); &test_vector);
if (test_vector & DPCD_TEST_EDID_READ) { if (test_vector & DP_TEST_LINK_EDID_READ) {
exynos_dp_write_byte_to_dpcd(dp, exynos_dp_write_byte_to_dpcd(dp,
DPCD_ADDR_TEST_EDID_CHECKSUM, DP_TEST_EDID_CHECKSUM,
edid[EDID_BLOCK_LENGTH + EDID_CHECKSUM]); edid[EDID_BLOCK_LENGTH + EDID_CHECKSUM]);
exynos_dp_write_byte_to_dpcd(dp, exynos_dp_write_byte_to_dpcd(dp,
DPCD_ADDR_TEST_RESPONSE, DP_TEST_RESPONSE,
DPCD_TEST_EDID_CHECKSUM_WRITE); DP_TEST_EDID_CHECKSUM_WRITE);
} }
} else { } else {
dev_info(dp->dev, "EDID data does not include any extensions.\n"); dev_info(dp->dev, "EDID data does not include any extensions.\n");
...@@ -171,15 +174,15 @@ static int exynos_dp_read_edid(struct exynos_dp_device *dp) ...@@ -171,15 +174,15 @@ static int exynos_dp_read_edid(struct exynos_dp_device *dp)
} }
exynos_dp_read_byte_from_dpcd(dp, exynos_dp_read_byte_from_dpcd(dp,
DPCD_ADDR_TEST_REQUEST, DP_TEST_REQUEST,
&test_vector); &test_vector);
if (test_vector & DPCD_TEST_EDID_READ) { if (test_vector & DP_TEST_LINK_EDID_READ) {
exynos_dp_write_byte_to_dpcd(dp, exynos_dp_write_byte_to_dpcd(dp,
DPCD_ADDR_TEST_EDID_CHECKSUM, DP_TEST_EDID_CHECKSUM,
edid[EDID_CHECKSUM]); edid[EDID_CHECKSUM]);
exynos_dp_write_byte_to_dpcd(dp, exynos_dp_write_byte_to_dpcd(dp,
DPCD_ADDR_TEST_RESPONSE, DP_TEST_RESPONSE,
DPCD_TEST_EDID_CHECKSUM_WRITE); DP_TEST_EDID_CHECKSUM_WRITE);
} }
} }
...@@ -193,8 +196,8 @@ static int exynos_dp_handle_edid(struct exynos_dp_device *dp) ...@@ -193,8 +196,8 @@ static int exynos_dp_handle_edid(struct exynos_dp_device *dp)
int i; int i;
int retval; int retval;
/* Read DPCD DPCD_ADDR_DPCD_REV~RECEIVE_PORT1_CAP_1 */ /* Read DPCD DP_DPCD_REV~RECEIVE_PORT1_CAP_1 */
retval = exynos_dp_read_bytes_from_dpcd(dp, DPCD_ADDR_DPCD_REV, retval = exynos_dp_read_bytes_from_dpcd(dp, DP_DPCD_REV,
12, buf); 12, buf);
if (retval) if (retval)
return retval; return retval;
...@@ -214,14 +217,14 @@ static void exynos_dp_enable_rx_to_enhanced_mode(struct exynos_dp_device *dp, ...@@ -214,14 +217,14 @@ static void exynos_dp_enable_rx_to_enhanced_mode(struct exynos_dp_device *dp,
{ {
u8 data; u8 data;
exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_LANE_COUNT_SET, &data); exynos_dp_read_byte_from_dpcd(dp, DP_LANE_COUNT_SET, &data);
if (enable) if (enable)
exynos_dp_write_byte_to_dpcd(dp, DPCD_ADDR_LANE_COUNT_SET, exynos_dp_write_byte_to_dpcd(dp, DP_LANE_COUNT_SET,
DPCD_ENHANCED_FRAME_EN | DP_LANE_COUNT_ENHANCED_FRAME_EN |
DPCD_LANE_COUNT_SET(data)); DPCD_LANE_COUNT_SET(data));
else else
exynos_dp_write_byte_to_dpcd(dp, DPCD_ADDR_LANE_COUNT_SET, exynos_dp_write_byte_to_dpcd(dp, DP_LANE_COUNT_SET,
DPCD_LANE_COUNT_SET(data)); DPCD_LANE_COUNT_SET(data));
} }
...@@ -230,7 +233,7 @@ static int exynos_dp_is_enhanced_mode_available(struct exynos_dp_device *dp) ...@@ -230,7 +233,7 @@ static int exynos_dp_is_enhanced_mode_available(struct exynos_dp_device *dp)
u8 data; u8 data;
int retval; int retval;
exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_MAX_LANE_COUNT, &data); exynos_dp_read_byte_from_dpcd(dp, DP_MAX_LANE_COUNT, &data);
retval = DPCD_ENHANCED_FRAME_CAP(data); retval = DPCD_ENHANCED_FRAME_CAP(data);
return retval; return retval;
...@@ -250,8 +253,8 @@ static void exynos_dp_training_pattern_dis(struct exynos_dp_device *dp) ...@@ -250,8 +253,8 @@ static void exynos_dp_training_pattern_dis(struct exynos_dp_device *dp)
exynos_dp_set_training_pattern(dp, DP_NONE); exynos_dp_set_training_pattern(dp, DP_NONE);
exynos_dp_write_byte_to_dpcd(dp, exynos_dp_write_byte_to_dpcd(dp,
DPCD_ADDR_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_SET,
DPCD_TRAINING_PATTERN_DISABLED); DP_TRAINING_PATTERN_DISABLE);
} }
static void exynos_dp_set_lane_lane_pre_emphasis(struct exynos_dp_device *dp, static void exynos_dp_set_lane_lane_pre_emphasis(struct exynos_dp_device *dp,
...@@ -295,7 +298,7 @@ static int exynos_dp_link_start(struct exynos_dp_device *dp) ...@@ -295,7 +298,7 @@ static int exynos_dp_link_start(struct exynos_dp_device *dp)
/* Setup RX configuration */ /* Setup RX configuration */
buf[0] = dp->link_train.link_rate; buf[0] = dp->link_train.link_rate;
buf[1] = dp->link_train.lane_count; buf[1] = dp->link_train.lane_count;
retval = exynos_dp_write_bytes_to_dpcd(dp, DPCD_ADDR_LINK_BW_SET, retval = exynos_dp_write_bytes_to_dpcd(dp, DP_LINK_BW_SET,
2, buf); 2, buf);
if (retval) if (retval)
return retval; return retval;
...@@ -322,16 +325,16 @@ static int exynos_dp_link_start(struct exynos_dp_device *dp) ...@@ -322,16 +325,16 @@ static int exynos_dp_link_start(struct exynos_dp_device *dp)
/* Set RX training pattern */ /* Set RX training pattern */
retval = exynos_dp_write_byte_to_dpcd(dp, retval = exynos_dp_write_byte_to_dpcd(dp,
DPCD_ADDR_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_SET,
DPCD_SCRAMBLING_DISABLED | DPCD_TRAINING_PATTERN_1); DP_LINK_SCRAMBLING_DISABLE | DP_TRAINING_PATTERN_1);
if (retval) if (retval)
return retval; return retval;
for (lane = 0; lane < lane_count; lane++) for (lane = 0; lane < lane_count; lane++)
buf[lane] = DPCD_PRE_EMPHASIS_PATTERN2_LEVEL0 | buf[lane] = DP_TRAIN_PRE_EMPHASIS_0 |
DPCD_VOLTAGE_SWING_PATTERN1_LEVEL0; DP_TRAIN_VOLTAGE_SWING_400;
retval = exynos_dp_write_bytes_to_dpcd(dp, DPCD_ADDR_TRAINING_LANE0_SET, retval = exynos_dp_write_bytes_to_dpcd(dp, DP_TRAINING_LANE0_SET,
lane_count, buf); lane_count, buf);
return retval; return retval;
...@@ -352,7 +355,7 @@ static int exynos_dp_clock_recovery_ok(u8 link_status[2], int lane_count) ...@@ -352,7 +355,7 @@ static int exynos_dp_clock_recovery_ok(u8 link_status[2], int lane_count)
for (lane = 0; lane < lane_count; lane++) { for (lane = 0; lane < lane_count; lane++) {
lane_status = exynos_dp_get_lane_status(link_status, lane); lane_status = exynos_dp_get_lane_status(link_status, lane);
if ((lane_status & DPCD_LANE_CR_DONE) == 0) if ((lane_status & DP_LANE_CR_DONE) == 0)
return -EINVAL; return -EINVAL;
} }
return 0; return 0;
...@@ -364,13 +367,13 @@ static int exynos_dp_channel_eq_ok(u8 link_status[2], u8 link_align, ...@@ -364,13 +367,13 @@ static int exynos_dp_channel_eq_ok(u8 link_status[2], u8 link_align,
int lane; int lane;
u8 lane_status; u8 lane_status;
if ((link_align & DPCD_INTERLANE_ALIGN_DONE) == 0) if ((link_align & DP_INTERLANE_ALIGN_DONE) == 0)
return -EINVAL; return -EINVAL;
for (lane = 0; lane < lane_count; lane++) { for (lane = 0; lane < lane_count; lane++) {
lane_status = exynos_dp_get_lane_status(link_status, lane); lane_status = exynos_dp_get_lane_status(link_status, lane);
lane_status &= DPCD_CHANNEL_EQ_BITS; lane_status &= DP_CHANNEL_EQ_BITS;
if (lane_status != DPCD_CHANNEL_EQ_BITS) if (lane_status != DP_CHANNEL_EQ_BITS)
return -EINVAL; return -EINVAL;
} }
...@@ -468,9 +471,9 @@ static void exynos_dp_get_adjust_training_lane(struct exynos_dp_device *dp, ...@@ -468,9 +471,9 @@ static void exynos_dp_get_adjust_training_lane(struct exynos_dp_device *dp,
DPCD_PRE_EMPHASIS_SET(pre_emphasis); DPCD_PRE_EMPHASIS_SET(pre_emphasis);
if (voltage_swing == VOLTAGE_LEVEL_3) if (voltage_swing == VOLTAGE_LEVEL_3)
training_lane |= DPCD_MAX_SWING_REACHED; training_lane |= DP_TRAIN_MAX_SWING_REACHED;
if (pre_emphasis == PRE_EMPHASIS_LEVEL_3) if (pre_emphasis == PRE_EMPHASIS_LEVEL_3)
training_lane |= DPCD_MAX_PRE_EMPHASIS_REACHED; training_lane |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
dp->link_train.training_lane[lane] = training_lane; dp->link_train.training_lane[lane] = training_lane;
} }
...@@ -487,12 +490,12 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp) ...@@ -487,12 +490,12 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp)
lane_count = dp->link_train.lane_count; lane_count = dp->link_train.lane_count;
retval = exynos_dp_read_bytes_from_dpcd(dp, retval = exynos_dp_read_bytes_from_dpcd(dp,
DPCD_ADDR_LANE0_1_STATUS, 2, link_status); DP_LANE0_1_STATUS, 2, link_status);
if (retval) if (retval)
return retval; return retval;
retval = exynos_dp_read_bytes_from_dpcd(dp, retval = exynos_dp_read_bytes_from_dpcd(dp,
DPCD_ADDR_ADJUST_REQUEST_LANE0_1, 2, adjust_request); DP_ADJUST_REQUEST_LANE0_1, 2, adjust_request);
if (retval) if (retval)
return retval; return retval;
...@@ -501,9 +504,9 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp) ...@@ -501,9 +504,9 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp)
exynos_dp_set_training_pattern(dp, TRAINING_PTN2); exynos_dp_set_training_pattern(dp, TRAINING_PTN2);
retval = exynos_dp_write_byte_to_dpcd(dp, retval = exynos_dp_write_byte_to_dpcd(dp,
DPCD_ADDR_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_SET,
DPCD_SCRAMBLING_DISABLED | DP_LINK_SCRAMBLING_DISABLE |
DPCD_TRAINING_PATTERN_2); DP_TRAINING_PATTERN_2);
if (retval) if (retval)
return retval; return retval;
...@@ -543,7 +546,7 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp) ...@@ -543,7 +546,7 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp)
dp->link_train.training_lane[lane], lane); dp->link_train.training_lane[lane], lane);
retval = exynos_dp_write_bytes_to_dpcd(dp, retval = exynos_dp_write_bytes_to_dpcd(dp,
DPCD_ADDR_TRAINING_LANE0_SET, lane_count, DP_TRAINING_LANE0_SET, lane_count,
dp->link_train.training_lane); dp->link_train.training_lane);
if (retval) if (retval)
return retval; return retval;
...@@ -562,7 +565,7 @@ static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp) ...@@ -562,7 +565,7 @@ static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp)
lane_count = dp->link_train.lane_count; lane_count = dp->link_train.lane_count;
retval = exynos_dp_read_bytes_from_dpcd(dp, retval = exynos_dp_read_bytes_from_dpcd(dp,
DPCD_ADDR_LANE0_1_STATUS, 2, link_status); DP_LANE0_1_STATUS, 2, link_status);
if (retval) if (retval)
return retval; return retval;
...@@ -572,12 +575,12 @@ static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp) ...@@ -572,12 +575,12 @@ static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp)
} }
retval = exynos_dp_read_bytes_from_dpcd(dp, retval = exynos_dp_read_bytes_from_dpcd(dp,
DPCD_ADDR_ADJUST_REQUEST_LANE0_1, 2, adjust_request); DP_ADJUST_REQUEST_LANE0_1, 2, adjust_request);
if (retval) if (retval)
return retval; return retval;
retval = exynos_dp_read_byte_from_dpcd(dp, retval = exynos_dp_read_byte_from_dpcd(dp,
DPCD_ADDR_LANE_ALIGN_STATUS_UPDATED, &link_align); DP_LANE_ALIGN_STATUS_UPDATED, &link_align);
if (retval) if (retval)
return retval; return retval;
...@@ -619,7 +622,7 @@ static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp) ...@@ -619,7 +622,7 @@ static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp)
exynos_dp_set_lane_link_training(dp, exynos_dp_set_lane_link_training(dp,
dp->link_train.training_lane[lane], lane); dp->link_train.training_lane[lane], lane);
retval = exynos_dp_write_bytes_to_dpcd(dp, DPCD_ADDR_TRAINING_LANE0_SET, retval = exynos_dp_write_bytes_to_dpcd(dp, DP_TRAINING_LANE0_SET,
lane_count, dp->link_train.training_lane); lane_count, dp->link_train.training_lane);
return retval; return retval;
...@@ -634,7 +637,7 @@ static void exynos_dp_get_max_rx_bandwidth(struct exynos_dp_device *dp, ...@@ -634,7 +637,7 @@ static void exynos_dp_get_max_rx_bandwidth(struct exynos_dp_device *dp,
* For DP rev.1.1, Maximum link rate of Main Link lanes * For DP rev.1.1, Maximum link rate of Main Link lanes
* 0x06 = 1.62 Gbps, 0x0a = 2.7 Gbps * 0x06 = 1.62 Gbps, 0x0a = 2.7 Gbps
*/ */
exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_MAX_LINK_RATE, &data); exynos_dp_read_byte_from_dpcd(dp, DP_MAX_LINK_RATE, &data);
*bandwidth = data; *bandwidth = data;
} }
...@@ -647,7 +650,7 @@ static void exynos_dp_get_max_rx_lane_count(struct exynos_dp_device *dp, ...@@ -647,7 +650,7 @@ static void exynos_dp_get_max_rx_lane_count(struct exynos_dp_device *dp,
* For DP rev.1.1, Maximum number of Main Link lanes * For DP rev.1.1, Maximum number of Main Link lanes
* 0x01 = 1 lane, 0x02 = 2 lanes, 0x04 = 4 lanes * 0x01 = 1 lane, 0x02 = 2 lanes, 0x04 = 4 lanes
*/ */
exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_MAX_LANE_COUNT, &data); exynos_dp_read_byte_from_dpcd(dp, DP_MAX_LANE_COUNT, &data);
*lane_count = DPCD_MAX_LANE_COUNT(data); *lane_count = DPCD_MAX_LANE_COUNT(data);
} }
...@@ -819,20 +822,20 @@ static void exynos_dp_enable_scramble(struct exynos_dp_device *dp, bool enable) ...@@ -819,20 +822,20 @@ static void exynos_dp_enable_scramble(struct exynos_dp_device *dp, bool enable)
exynos_dp_enable_scrambling(dp); exynos_dp_enable_scrambling(dp);
exynos_dp_read_byte_from_dpcd(dp, exynos_dp_read_byte_from_dpcd(dp,
DPCD_ADDR_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_SET,
&data); &data);
exynos_dp_write_byte_to_dpcd(dp, exynos_dp_write_byte_to_dpcd(dp,
DPCD_ADDR_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_SET,
(u8)(data & ~DPCD_SCRAMBLING_DISABLED)); (u8)(data & ~DP_LINK_SCRAMBLING_DISABLE));
} else { } else {
exynos_dp_disable_scrambling(dp); exynos_dp_disable_scrambling(dp);
exynos_dp_read_byte_from_dpcd(dp, exynos_dp_read_byte_from_dpcd(dp,
DPCD_ADDR_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_SET,
&data); &data);
exynos_dp_write_byte_to_dpcd(dp, exynos_dp_write_byte_to_dpcd(dp,
DPCD_ADDR_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_SET,
(u8)(data | DPCD_SCRAMBLING_DISABLED)); (u8)(data | DP_LINK_SCRAMBLING_DISABLE));
} }
} }
...@@ -962,16 +965,6 @@ static struct drm_connector_helper_funcs exynos_dp_connector_helper_funcs = { ...@@ -962,16 +965,6 @@ static struct drm_connector_helper_funcs exynos_dp_connector_helper_funcs = {
.best_encoder = exynos_dp_best_encoder, .best_encoder = exynos_dp_best_encoder,
}; };
static int exynos_dp_initialize(struct exynos_drm_display *display,
struct drm_device *drm_dev)
{
struct exynos_dp_device *dp = display->ctx;
dp->drm_dev = drm_dev;
return 0;
}
static bool find_bridge(const char *compat, struct bridge_init *bridge) static bool find_bridge(const char *compat, struct bridge_init *bridge)
{ {
bridge->client = NULL; bridge->client = NULL;
...@@ -1099,7 +1092,6 @@ static void exynos_dp_dpms(struct exynos_drm_display *display, int mode) ...@@ -1099,7 +1092,6 @@ static void exynos_dp_dpms(struct exynos_drm_display *display, int mode)
} }
static struct exynos_drm_display_ops exynos_dp_display_ops = { static struct exynos_drm_display_ops exynos_dp_display_ops = {
.initialize = exynos_dp_initialize,
.create_connector = exynos_dp_create_connector, .create_connector = exynos_dp_create_connector,
.dpms = exynos_dp_dpms, .dpms = exynos_dp_dpms,
}; };
...@@ -1116,10 +1108,8 @@ static struct video_info *exynos_dp_dt_parse_pdata(struct device *dev) ...@@ -1116,10 +1108,8 @@ static struct video_info *exynos_dp_dt_parse_pdata(struct device *dev)
dp_video_config = devm_kzalloc(dev, dp_video_config = devm_kzalloc(dev,
sizeof(*dp_video_config), GFP_KERNEL); sizeof(*dp_video_config), GFP_KERNEL);
if (!dp_video_config) { if (!dp_video_config)
dev_err(dev, "memory allocation for video config failed\n");
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
}
dp_video_config->h_sync_polarity = dp_video_config->h_sync_polarity =
of_property_read_bool(dp_node, "hsync-active-high"); of_property_read_bool(dp_node, "hsync-active-high");
...@@ -1178,10 +1168,7 @@ static int exynos_dp_dt_parse_phydata(struct exynos_dp_device *dp) ...@@ -1178,10 +1168,7 @@ static int exynos_dp_dt_parse_phydata(struct exynos_dp_device *dp)
dp_phy_node = of_find_node_by_name(dp_phy_node, "dptx-phy"); dp_phy_node = of_find_node_by_name(dp_phy_node, "dptx-phy");
if (!dp_phy_node) { if (!dp_phy_node) {
dp->phy = devm_phy_get(dp->dev, "dp"); dp->phy = devm_phy_get(dp->dev, "dp");
if (IS_ERR(dp->phy)) return PTR_ERR_OR_ZERO(dp->phy);
return PTR_ERR(dp->phy);
else
return 0;
} }
if (of_property_read_u32(dp_phy_node, "reg", &phy_base)) { if (of_property_read_u32(dp_phy_node, "reg", &phy_base)) {
...@@ -1223,19 +1210,20 @@ static int exynos_dp_dt_parse_panel(struct exynos_dp_device *dp) ...@@ -1223,19 +1210,20 @@ static int exynos_dp_dt_parse_panel(struct exynos_dp_device *dp)
return 0; return 0;
} }
static int exynos_dp_probe(struct platform_device *pdev) static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
{ {
struct platform_device *pdev = to_platform_device(dev);
struct drm_device *drm_dev = data;
struct resource *res; struct resource *res;
struct exynos_dp_device *dp; struct exynos_dp_device *dp;
unsigned int irq_flags;
int ret = 0; int ret = 0;
dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device), dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device),
GFP_KERNEL); GFP_KERNEL);
if (!dp) { if (!dp)
dev_err(&pdev->dev, "no memory for device data\n");
return -ENOMEM; return -ENOMEM;
}
dp->dev = &pdev->dev; dp->dev = &pdev->dev;
dp->dpms_mode = DRM_MODE_DPMS_OFF; dp->dpms_mode = DRM_MODE_DPMS_OFF;
...@@ -1266,7 +1254,30 @@ static int exynos_dp_probe(struct platform_device *pdev) ...@@ -1266,7 +1254,30 @@ static int exynos_dp_probe(struct platform_device *pdev)
if (IS_ERR(dp->reg_base)) if (IS_ERR(dp->reg_base))
return PTR_ERR(dp->reg_base); return PTR_ERR(dp->reg_base);
dp->irq = platform_get_irq(pdev, 0); dp->hpd_gpio = of_get_named_gpio(dev->of_node, "samsung,hpd-gpio", 0);
if (gpio_is_valid(dp->hpd_gpio)) {
/*
* Set up the hotplug GPIO from the device tree as an interrupt.
* Simply specifying a different interrupt in the device tree
* doesn't work since we handle hotplug rather differently when
* using a GPIO. We also need the actual GPIO specifier so
* that we can get the current state of the GPIO.
*/
ret = devm_gpio_request_one(&pdev->dev, dp->hpd_gpio, GPIOF_IN,
"hpd_gpio");
if (ret) {
dev_err(&pdev->dev, "failed to get hpd gpio\n");
return ret;
}
dp->irq = gpio_to_irq(dp->hpd_gpio);
irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
} else {
dp->hpd_gpio = -ENODEV;
dp->irq = platform_get_irq(pdev, 0);
irq_flags = 0;
}
if (dp->irq == -ENXIO) { if (dp->irq == -ENXIO) {
dev_err(&pdev->dev, "failed to get irq\n"); dev_err(&pdev->dev, "failed to get irq\n");
return -ENODEV; return -ENODEV;
...@@ -1278,28 +1289,61 @@ static int exynos_dp_probe(struct platform_device *pdev) ...@@ -1278,28 +1289,61 @@ static int exynos_dp_probe(struct platform_device *pdev)
exynos_dp_init_dp(dp); exynos_dp_init_dp(dp);
ret = devm_request_irq(&pdev->dev, dp->irq, exynos_dp_irq_handler, 0, ret = devm_request_irq(&pdev->dev, dp->irq, exynos_dp_irq_handler,
"exynos-dp", dp); irq_flags, "exynos-dp", dp);
if (ret) { if (ret) {
dev_err(&pdev->dev, "failed to request irq\n"); dev_err(&pdev->dev, "failed to request irq\n");
return ret; return ret;
} }
disable_irq(dp->irq); disable_irq(dp->irq);
dp->drm_dev = drm_dev;
exynos_dp_display.ctx = dp; exynos_dp_display.ctx = dp;
platform_set_drvdata(pdev, &exynos_dp_display); platform_set_drvdata(pdev, &exynos_dp_display);
exynos_drm_display_register(&exynos_dp_display);
return 0; return exynos_drm_create_enc_conn(drm_dev, &exynos_dp_display);
} }
static int exynos_dp_remove(struct platform_device *pdev) static void exynos_dp_unbind(struct device *dev, struct device *master,
void *data)
{ {
struct exynos_drm_display *display = platform_get_drvdata(pdev); struct exynos_drm_display *display = dev_get_drvdata(dev);
struct exynos_dp_device *dp = display->ctx;
struct drm_encoder *encoder = dp->encoder;
exynos_dp_dpms(display, DRM_MODE_DPMS_OFF); exynos_dp_dpms(display, DRM_MODE_DPMS_OFF);
exynos_drm_display_unregister(&exynos_dp_display);
encoder->funcs->destroy(encoder);
drm_connector_cleanup(&dp->connector);
}
static const struct component_ops exynos_dp_ops = {
.bind = exynos_dp_bind,
.unbind = exynos_dp_unbind,
};
static int exynos_dp_probe(struct platform_device *pdev)
{
int ret;
ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
exynos_dp_display.type);
if (ret)
return ret;
ret = component_add(&pdev->dev, &exynos_dp_ops);
if (ret)
exynos_drm_component_del(&pdev->dev,
EXYNOS_DEVICE_TYPE_CONNECTOR);
return ret;
}
static int exynos_dp_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &exynos_dp_ops);
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
return 0; return 0;
} }
......
...@@ -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,19 +355,27 @@ enum dp_irq_type exynos_dp_get_irq_type(struct exynos_dp_device *dp) ...@@ -348,19 +355,27 @@ enum dp_irq_type exynos_dp_get_irq_type(struct exynos_dp_device *dp)
{ {
u32 reg; u32 reg;
/* Parse hotplug interrupt status register */ if (gpio_is_valid(dp->hpd_gpio)) {
reg = readl(dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4); 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);
if (reg & PLUG) if (reg & PLUG)
return DP_IRQ_TYPE_HP_CABLE_IN; return DP_IRQ_TYPE_HP_CABLE_IN;
if (reg & HPD_LOST) if (reg & HPD_LOST)
return DP_IRQ_TYPE_HP_CABLE_OUT; return DP_IRQ_TYPE_HP_CABLE_OUT;
if (reg & HOTPLUG_CHG) if (reg & HOTPLUG_CHG)
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;
reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3); if (gpio_is_valid(dp->hpd_gpio)) {
if (reg & HPD_STATUS) if (gpio_get_value(dp->hpd_gpio))
return 0; return 0;
} else {
reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3);
if (reg & HPD_STATUS)
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) {
int ret;
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 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;
}
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)
{ {
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_add_tail(&subdrv->list, &exynos_drm_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);
} }
EXPORT_SYMBOL_GPL(exynos_drm_subdrv_register);
int exynos_drm_initialize_displays(struct drm_device *dev) int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *subdrv)
{ {
struct exynos_drm_display *display, *n; if (!subdrv)
int ret, initialized = 0; return -EINVAL;
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++; list_del(&subdrv->list);
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; 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) {
if (err) { subdrv->drm_dev = dev;
DRM_DEBUG("exynos drm subdrv probe failed.\n");
list_del(&subdrv->list); /*
continue; * 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; 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) drm_panel_attach(ctx->panel, &ctx->connector);
return connector_status_connected;
if (!ctx->panel) { return connector_status_connected;
ctx->panel = of_drm_find_panel(ctx->panel_node);
if (ctx->panel)
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) static void exynos_dpi_connector_destroy(struct drm_connector *connector)
...@@ -116,10 +106,7 @@ static int exynos_dpi_create_connector(struct exynos_drm_display *display, ...@@ -116,10 +106,7 @@ 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_HPD;
connector->polled = DRM_CONNECTOR_POLL_CONNECT;
else
connector->polled = DRM_CONNECTOR_POLL_HPD;
ret = drm_connector_init(encoder->dev, connector, ret = drm_connector_init(encoder->dev, connector,
&exynos_dpi_connector_funcs, &exynos_dpi_connector_funcs,
...@@ -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;
}
exynos_drm_display_register(&exynos_dpi_display); 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);
}
}
return 0; return &exynos_dpi_display;
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;
} }
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <drm/drm_crtc_helper.h> #include <drm/drm_crtc_helper.h>
#include <linux/anon_inodes.h> #include <linux/anon_inodes.h>
#include <linux/component.h>
#include <drm/exynos_drm.h> #include <drm/exynos_drm.h>
...@@ -40,9 +41,19 @@ ...@@ -40,9 +41,19 @@
#define VBLANK_OFF_DELAY 50000 #define VBLANK_OFF_DELAY 50000
/* platform device pointer for eynos drm device. */
static struct platform_device *exynos_drm_pdev; static struct platform_device *exynos_drm_pdev;
static DEFINE_MUTEX(drm_component_lock);
static LIST_HEAD(drm_component_list);
struct component_dev {
struct list_head list;
struct device *crtc_dev;
struct device *conn_dev;
enum exynos_drm_output_type out_type;
unsigned int dev_type_flag;
};
static int exynos_drm_load(struct drm_device *dev, unsigned long flags) static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
{ {
struct exynos_drm_private *private; struct exynos_drm_private *private;
...@@ -73,38 +84,21 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags) ...@@ -73,38 +84,21 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
exynos_drm_mode_config_init(dev); exynos_drm_mode_config_init(dev);
ret = exynos_drm_initialize_managers(dev);
if (ret)
goto err_mode_config_cleanup;
for (nr = 0; nr < MAX_PLANE; nr++) { for (nr = 0; nr < MAX_PLANE; nr++) {
struct drm_plane *plane; struct drm_plane *plane;
unsigned long possible_crtcs = (1 << MAX_CRTC) - 1; unsigned long possible_crtcs = (1 << MAX_CRTC) - 1;
plane = exynos_plane_init(dev, possible_crtcs, false); plane = exynos_plane_init(dev, possible_crtcs, false);
if (!plane) if (!plane)
goto err_manager_cleanup; goto err_mode_config_cleanup;
} }
ret = exynos_drm_initialize_displays(dev);
if (ret)
goto err_manager_cleanup;
/* init kms poll for handling hpd */ /* init kms poll for handling hpd */
drm_kms_helper_poll_init(dev); drm_kms_helper_poll_init(dev);
ret = drm_vblank_init(dev, MAX_CRTC); ret = drm_vblank_init(dev, MAX_CRTC);
if (ret) if (ret)
goto err_display_cleanup; goto err_mode_config_cleanup;
/*
* probe sub drivers such as display controller and hdmi driver,
* that were registered at probe() of platform driver
* to the sub driver and create encoder and connector for them.
*/
ret = exynos_drm_device_register(dev);
if (ret)
goto err_vblank;
/* setup possible_clones. */ /* setup possible_clones. */
exynos_drm_encoder_setup(dev); exynos_drm_encoder_setup(dev);
...@@ -113,17 +107,25 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags) ...@@ -113,17 +107,25 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
platform_set_drvdata(dev->platformdev, dev); platform_set_drvdata(dev->platformdev, dev);
/* Try to bind all sub drivers. */
ret = component_bind_all(dev->dev, dev);
if (ret)
goto err_cleanup_vblank;
/* Probe non kms sub drivers and virtual display driver. */
ret = exynos_drm_device_subdrv_probe(dev);
if (ret)
goto err_unbind_all;
/* force connectors detection */ /* force connectors detection */
drm_helper_hpd_irq_event(dev); drm_helper_hpd_irq_event(dev);
return 0; return 0;
err_vblank: err_unbind_all:
component_unbind_all(dev->dev, dev);
err_cleanup_vblank:
drm_vblank_cleanup(dev); drm_vblank_cleanup(dev);
err_display_cleanup:
exynos_drm_remove_displays(dev);
err_manager_cleanup:
exynos_drm_remove_managers(dev);
err_mode_config_cleanup: err_mode_config_cleanup:
drm_mode_config_cleanup(dev); drm_mode_config_cleanup(dev);
drm_release_iommu_mapping(dev); drm_release_iommu_mapping(dev);
...@@ -135,17 +137,17 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags) ...@@ -135,17 +137,17 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
static int exynos_drm_unload(struct drm_device *dev) static int exynos_drm_unload(struct drm_device *dev)
{ {
exynos_drm_device_subdrv_remove(dev);
exynos_drm_fbdev_fini(dev); exynos_drm_fbdev_fini(dev);
exynos_drm_device_unregister(dev);
drm_vblank_cleanup(dev); drm_vblank_cleanup(dev);
drm_kms_helper_poll_fini(dev); drm_kms_helper_poll_fini(dev);
exynos_drm_remove_displays(dev);
exynos_drm_remove_managers(dev);
drm_mode_config_cleanup(dev); drm_mode_config_cleanup(dev);
drm_release_iommu_mapping(dev); drm_release_iommu_mapping(dev);
kfree(dev->dev_private); kfree(dev->dev_private);
component_unbind_all(dev->dev, dev);
dev->dev_private = NULL; dev->dev_private = NULL;
return 0; return 0;
...@@ -183,9 +185,9 @@ static int exynos_drm_resume(struct drm_device *dev) ...@@ -183,9 +185,9 @@ static int exynos_drm_resume(struct drm_device *dev)
if (connector->funcs->dpms) if (connector->funcs->dpms)
connector->funcs->dpms(connector, connector->dpms); connector->funcs->dpms(connector, connector->dpms);
} }
drm_modeset_unlock_all(dev);
drm_helper_resume_force_mode(dev); drm_helper_resume_force_mode(dev);
drm_modeset_unlock_all(dev);
return 0; return 0;
} }
...@@ -323,8 +325,7 @@ static const struct file_operations exynos_drm_driver_fops = { ...@@ -323,8 +325,7 @@ static const struct file_operations exynos_drm_driver_fops = {
}; };
static struct drm_driver exynos_drm_driver = { static struct drm_driver exynos_drm_driver = {
.driver_features = DRIVER_HAVE_IRQ | DRIVER_MODESET | .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
DRIVER_GEM | DRIVER_PRIME,
.load = exynos_drm_load, .load = exynos_drm_load,
.unload = exynos_drm_unload, .unload = exynos_drm_unload,
.suspend = exynos_drm_suspend, .suspend = exynos_drm_suspend,
...@@ -355,27 +356,6 @@ static struct drm_driver exynos_drm_driver = { ...@@ -355,27 +356,6 @@ static struct drm_driver exynos_drm_driver = {
.minor = DRIVER_MINOR, .minor = DRIVER_MINOR,
}; };
static int exynos_drm_platform_probe(struct platform_device *pdev)
{
int ret;
ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
if (ret)
return ret;
pm_runtime_enable(&pdev->dev);
pm_runtime_get_sync(&pdev->dev);
return drm_platform_init(&exynos_drm_driver, pdev);
}
static int exynos_drm_platform_remove(struct platform_device *pdev)
{
drm_put_dev(platform_get_drvdata(pdev));
return 0;
}
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
static int exynos_drm_sys_suspend(struct device *dev) static int exynos_drm_sys_suspend(struct device *dev)
{ {
...@@ -400,196 +380,319 @@ static int exynos_drm_sys_resume(struct device *dev) ...@@ -400,196 +380,319 @@ static int exynos_drm_sys_resume(struct device *dev)
} }
#endif #endif
#ifdef CONFIG_PM_RUNTIME static const struct dev_pm_ops exynos_drm_pm_ops = {
static int exynos_drm_runtime_suspend(struct device *dev) SET_SYSTEM_SLEEP_PM_OPS(exynos_drm_sys_suspend, exynos_drm_sys_resume)
};
int exynos_drm_component_add(struct device *dev,
enum exynos_drm_device_type dev_type,
enum exynos_drm_output_type out_type)
{ {
struct drm_device *drm_dev = dev_get_drvdata(dev); struct component_dev *cdev;
pm_message_t message;
if (pm_runtime_suspended(dev)) if (dev_type != EXYNOS_DEVICE_TYPE_CRTC &&
return 0; dev_type != EXYNOS_DEVICE_TYPE_CONNECTOR) {
DRM_ERROR("invalid device type.\n");
return -EINVAL;
}
message.event = PM_EVENT_SUSPEND; mutex_lock(&drm_component_lock);
return exynos_drm_suspend(drm_dev, message);
/*
* Make sure to check if there is a component which has two device
* objects, for connector and for encoder/connector.
* It should make sure that crtc and encoder/connector drivers are
* ready before exynos drm core binds them.
*/
list_for_each_entry(cdev, &drm_component_list, list) {
if (cdev->out_type == out_type) {
/*
* If crtc and encoder/connector device objects are
* added already just return.
*/
if (cdev->dev_type_flag == (EXYNOS_DEVICE_TYPE_CRTC |
EXYNOS_DEVICE_TYPE_CONNECTOR)) {
mutex_unlock(&drm_component_lock);
return 0;
}
if (dev_type == EXYNOS_DEVICE_TYPE_CRTC) {
cdev->crtc_dev = dev;
cdev->dev_type_flag |= dev_type;
}
if (dev_type == EXYNOS_DEVICE_TYPE_CONNECTOR) {
cdev->conn_dev = dev;
cdev->dev_type_flag |= dev_type;
}
mutex_unlock(&drm_component_lock);
return 0;
}
}
mutex_unlock(&drm_component_lock);
cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
if (!cdev)
return -ENOMEM;
if (dev_type == EXYNOS_DEVICE_TYPE_CRTC)
cdev->crtc_dev = dev;
if (dev_type == EXYNOS_DEVICE_TYPE_CONNECTOR)
cdev->conn_dev = dev;
cdev->out_type = out_type;
cdev->dev_type_flag = dev_type;
mutex_lock(&drm_component_lock);
list_add_tail(&cdev->list, &drm_component_list);
mutex_unlock(&drm_component_lock);
return 0;
} }
static int exynos_drm_runtime_resume(struct device *dev) void exynos_drm_component_del(struct device *dev,
enum exynos_drm_device_type dev_type)
{ {
struct drm_device *drm_dev = dev_get_drvdata(dev); struct component_dev *cdev, *next;
if (!pm_runtime_suspended(dev)) mutex_lock(&drm_component_lock);
return 0;
return exynos_drm_resume(drm_dev); list_for_each_entry_safe(cdev, next, &drm_component_list, list) {
if (dev_type == EXYNOS_DEVICE_TYPE_CRTC) {
if (cdev->crtc_dev == dev) {
cdev->crtc_dev = NULL;
cdev->dev_type_flag &= ~dev_type;
}
}
if (dev_type == EXYNOS_DEVICE_TYPE_CONNECTOR) {
if (cdev->conn_dev == dev) {
cdev->conn_dev = NULL;
cdev->dev_type_flag &= ~dev_type;
}
}
/*
* Release cdev object only in case that both of crtc and
* encoder/connector device objects are NULL.
*/
if (!cdev->crtc_dev && !cdev->conn_dev) {
list_del(&cdev->list);
kfree(cdev);
}
break;
}
mutex_unlock(&drm_component_lock);
} }
#endif
static const struct dev_pm_ops exynos_drm_pm_ops = { static int compare_of(struct device *dev, void *data)
SET_SYSTEM_SLEEP_PM_OPS(exynos_drm_sys_suspend, exynos_drm_sys_resume) {
SET_RUNTIME_PM_OPS(exynos_drm_runtime_suspend, return dev == (struct device *)data;
exynos_drm_runtime_resume, NULL) }
};
static struct platform_driver exynos_drm_platform_driver = { static int exynos_drm_add_components(struct device *dev, struct master *m)
.probe = exynos_drm_platform_probe, {
.remove = exynos_drm_platform_remove, struct component_dev *cdev;
.driver = { unsigned int attach_cnt = 0;
.owner = THIS_MODULE,
.name = "exynos-drm", mutex_lock(&drm_component_lock);
.pm = &exynos_drm_pm_ops,
}, list_for_each_entry(cdev, &drm_component_list, list) {
int ret;
/*
* Add components to master only in case that crtc and
* encoder/connector device objects exist.
*/
if (!cdev->crtc_dev || !cdev->conn_dev)
continue;
attach_cnt++;
mutex_unlock(&drm_component_lock);
/*
* fimd and dpi modules have same device object so add
* only crtc device object in this case.
*
* TODO. if dpi module follows driver-model driver then
* below codes can be removed.
*/
if (cdev->crtc_dev == cdev->conn_dev) {
ret = component_master_add_child(m, compare_of,
cdev->crtc_dev);
if (ret < 0)
return ret;
goto out_lock;
}
/*
* Do not chage below call order.
* crtc device first should be added to master because
* connector/encoder need pipe number of crtc when they
* are created.
*/
ret = component_master_add_child(m, compare_of, cdev->crtc_dev);
ret |= component_master_add_child(m, compare_of,
cdev->conn_dev);
if (ret < 0)
return ret;
out_lock:
mutex_lock(&drm_component_lock);
}
mutex_unlock(&drm_component_lock);
return attach_cnt ? 0 : -ENODEV;
}
static int exynos_drm_bind(struct device *dev)
{
return drm_platform_init(&exynos_drm_driver, to_platform_device(dev));
}
static void exynos_drm_unbind(struct device *dev)
{
drm_put_dev(dev_get_drvdata(dev));
}
static const struct component_master_ops exynos_drm_ops = {
.add_components = exynos_drm_add_components,
.bind = exynos_drm_bind,
.unbind = exynos_drm_unbind,
}; };
static int __init exynos_drm_init(void) static int exynos_drm_platform_probe(struct platform_device *pdev)
{ {
int ret; int ret;
pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
exynos_drm_driver.num_ioctls = DRM_ARRAY_SIZE(exynos_ioctls);
#ifdef CONFIG_DRM_EXYNOS_FIMD
ret = platform_driver_register(&fimd_driver);
if (ret < 0)
return ret;
#endif
#ifdef CONFIG_DRM_EXYNOS_DP #ifdef CONFIG_DRM_EXYNOS_DP
ret = platform_driver_register(&dp_driver); ret = platform_driver_register(&dp_driver);
if (ret < 0) if (ret < 0)
goto out_dp; goto err_unregister_fimd_drv;
#endif #endif
#ifdef CONFIG_DRM_EXYNOS_DSI #ifdef CONFIG_DRM_EXYNOS_DSI
ret = platform_driver_register(&dsi_driver); ret = platform_driver_register(&dsi_driver);
if (ret < 0) if (ret < 0)
goto out_dsi; goto err_unregister_dp_drv;
#endif
#ifdef CONFIG_DRM_EXYNOS_FIMD
ret = platform_driver_register(&fimd_driver);
if (ret < 0)
goto out_fimd;
#endif #endif
#ifdef CONFIG_DRM_EXYNOS_HDMI #ifdef CONFIG_DRM_EXYNOS_HDMI
ret = platform_driver_register(&hdmi_driver);
if (ret < 0)
goto out_hdmi;
ret = platform_driver_register(&mixer_driver); ret = platform_driver_register(&mixer_driver);
if (ret < 0) if (ret < 0)
goto out_mixer; goto err_unregister_dsi_drv;
#endif ret = platform_driver_register(&hdmi_driver);
#ifdef CONFIG_DRM_EXYNOS_VIDI
ret = platform_driver_register(&vidi_driver);
if (ret < 0) if (ret < 0)
goto out_vidi; goto err_unregister_mixer_drv;
#endif #endif
#ifdef CONFIG_DRM_EXYNOS_G2D #ifdef CONFIG_DRM_EXYNOS_G2D
ret = platform_driver_register(&g2d_driver); ret = platform_driver_register(&g2d_driver);
if (ret < 0) if (ret < 0)
goto out_g2d; goto err_unregister_hdmi_drv;
#endif #endif
#ifdef CONFIG_DRM_EXYNOS_FIMC #ifdef CONFIG_DRM_EXYNOS_FIMC
ret = platform_driver_register(&fimc_driver); ret = platform_driver_register(&fimc_driver);
if (ret < 0) if (ret < 0)
goto out_fimc; goto err_unregister_g2d_drv;
#endif #endif
#ifdef CONFIG_DRM_EXYNOS_ROTATOR #ifdef CONFIG_DRM_EXYNOS_ROTATOR
ret = platform_driver_register(&rotator_driver); ret = platform_driver_register(&rotator_driver);
if (ret < 0) if (ret < 0)
goto out_rotator; goto err_unregister_fimc_drv;
#endif #endif
#ifdef CONFIG_DRM_EXYNOS_GSC #ifdef CONFIG_DRM_EXYNOS_GSC
ret = platform_driver_register(&gsc_driver); ret = platform_driver_register(&gsc_driver);
if (ret < 0) if (ret < 0)
goto out_gsc; goto err_unregister_rotator_drv;
#endif #endif
#ifdef CONFIG_DRM_EXYNOS_IPP #ifdef CONFIG_DRM_EXYNOS_IPP
ret = platform_driver_register(&ipp_driver); ret = platform_driver_register(&ipp_driver);
if (ret < 0) if (ret < 0)
goto out_ipp; goto err_unregister_gsc_drv;
ret = exynos_platform_device_ipp_register(); ret = exynos_platform_device_ipp_register();
if (ret < 0) if (ret < 0)
goto out_ipp_dev; goto err_unregister_ipp_drv;
#endif #endif
ret = platform_driver_register(&exynos_drm_platform_driver); ret = component_master_add(&pdev->dev, &exynos_drm_ops);
if (ret < 0) if (ret < 0)
goto out_drm; DRM_DEBUG_KMS("re-tried by last sub driver probed later.\n");
exynos_drm_pdev = platform_device_register_simple("exynos-drm", -1,
NULL, 0);
if (IS_ERR(exynos_drm_pdev)) {
ret = PTR_ERR(exynos_drm_pdev);
goto out;
}
return 0; return 0;
out:
platform_driver_unregister(&exynos_drm_platform_driver);
out_drm:
#ifdef CONFIG_DRM_EXYNOS_IPP #ifdef CONFIG_DRM_EXYNOS_IPP
exynos_platform_device_ipp_unregister(); err_unregister_ipp_drv:
out_ipp_dev:
platform_driver_unregister(&ipp_driver); platform_driver_unregister(&ipp_driver);
out_ipp: err_unregister_gsc_drv:
#endif #endif
#ifdef CONFIG_DRM_EXYNOS_GSC #ifdef CONFIG_DRM_EXYNOS_GSC
platform_driver_unregister(&gsc_driver); platform_driver_unregister(&gsc_driver);
out_gsc: err_unregister_rotator_drv:
#endif #endif
#ifdef CONFIG_DRM_EXYNOS_ROTATOR #ifdef CONFIG_DRM_EXYNOS_ROTATOR
platform_driver_unregister(&rotator_driver); platform_driver_unregister(&rotator_driver);
out_rotator: err_unregister_fimc_drv:
#endif #endif
#ifdef CONFIG_DRM_EXYNOS_FIMC #ifdef CONFIG_DRM_EXYNOS_FIMC
platform_driver_unregister(&fimc_driver); platform_driver_unregister(&fimc_driver);
out_fimc: err_unregister_g2d_drv:
#endif #endif
#ifdef CONFIG_DRM_EXYNOS_G2D #ifdef CONFIG_DRM_EXYNOS_G2D
platform_driver_unregister(&g2d_driver); platform_driver_unregister(&g2d_driver);
out_g2d: err_unregister_hdmi_drv:
#endif
#ifdef CONFIG_DRM_EXYNOS_VIDI
platform_driver_unregister(&vidi_driver);
out_vidi:
#endif #endif
#ifdef CONFIG_DRM_EXYNOS_HDMI #ifdef CONFIG_DRM_EXYNOS_HDMI
platform_driver_unregister(&mixer_driver);
out_mixer:
platform_driver_unregister(&hdmi_driver); platform_driver_unregister(&hdmi_driver);
out_hdmi: err_unregister_mixer_drv:
#endif platform_driver_unregister(&mixer_driver);
err_unregister_dsi_drv:
#ifdef CONFIG_DRM_EXYNOS_FIMD
platform_driver_unregister(&fimd_driver);
out_fimd:
#endif #endif
#ifdef CONFIG_DRM_EXYNOS_DSI #ifdef CONFIG_DRM_EXYNOS_DSI
platform_driver_unregister(&dsi_driver); platform_driver_unregister(&dsi_driver);
out_dsi: err_unregister_dp_drv:
#endif #endif
#ifdef CONFIG_DRM_EXYNOS_DP #ifdef CONFIG_DRM_EXYNOS_DP
platform_driver_unregister(&dp_driver); platform_driver_unregister(&dp_driver);
out_dp: err_unregister_fimd_drv:
#endif
#ifdef CONFIG_DRM_EXYNOS_FIMD
platform_driver_unregister(&fimd_driver);
#endif #endif
return ret; return ret;
} }
static void __exit exynos_drm_exit(void) static int exynos_drm_platform_remove(struct platform_device *pdev)
{ {
platform_device_unregister(exynos_drm_pdev);
platform_driver_unregister(&exynos_drm_platform_driver);
#ifdef CONFIG_DRM_EXYNOS_IPP #ifdef CONFIG_DRM_EXYNOS_IPP
exynos_platform_device_ipp_unregister(); exynos_platform_device_ipp_unregister();
platform_driver_unregister(&ipp_driver); platform_driver_unregister(&ipp_driver);
...@@ -616,10 +719,6 @@ static void __exit exynos_drm_exit(void) ...@@ -616,10 +719,6 @@ static void __exit exynos_drm_exit(void)
platform_driver_unregister(&hdmi_driver); platform_driver_unregister(&hdmi_driver);
#endif #endif
#ifdef CONFIG_DRM_EXYNOS_VIDI
platform_driver_unregister(&vidi_driver);
#endif
#ifdef CONFIG_DRM_EXYNOS_FIMD #ifdef CONFIG_DRM_EXYNOS_FIMD
platform_driver_unregister(&fimd_driver); platform_driver_unregister(&fimd_driver);
#endif #endif
...@@ -631,6 +730,59 @@ static void __exit exynos_drm_exit(void) ...@@ -631,6 +730,59 @@ static void __exit exynos_drm_exit(void)
#ifdef CONFIG_DRM_EXYNOS_DP #ifdef CONFIG_DRM_EXYNOS_DP
platform_driver_unregister(&dp_driver); platform_driver_unregister(&dp_driver);
#endif #endif
component_master_del(&pdev->dev, &exynos_drm_ops);
return 0;
}
static struct platform_driver exynos_drm_platform_driver = {
.probe = exynos_drm_platform_probe,
.remove = exynos_drm_platform_remove,
.driver = {
.owner = THIS_MODULE,
.name = "exynos-drm",
.pm = &exynos_drm_pm_ops,
},
};
static int exynos_drm_init(void)
{
int ret;
exynos_drm_pdev = platform_device_register_simple("exynos-drm", -1,
NULL, 0);
if (IS_ERR(exynos_drm_pdev))
return PTR_ERR(exynos_drm_pdev);
#ifdef CONFIG_DRM_EXYNOS_VIDI
ret = exynos_drm_probe_vidi();
if (ret < 0)
goto err_unregister_pd;
#endif
ret = platform_driver_register(&exynos_drm_platform_driver);
if (ret)
goto err_remove_vidi;
return 0;
err_unregister_pd:
platform_device_unregister(exynos_drm_pdev);
err_remove_vidi:
#ifdef CONFIG_DRM_EXYNOS_VIDI
exynos_drm_remove_vidi();
#endif
return ret;
}
static void exynos_drm_exit(void)
{
#ifdef CONFIG_DRM_EXYNOS_VIDI
exynos_drm_remove_vidi();
#endif
platform_device_unregister(exynos_drm_pdev);
platform_driver_unregister(&exynos_drm_platform_driver);
} }
module_init(exynos_drm_init); module_init(exynos_drm_init);
......
...@@ -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;
}; };
/* /*
...@@ -281,11 +285,11 @@ struct exynos_drm_private { ...@@ -281,11 +285,11 @@ struct exynos_drm_private {
* @drm_dev: pointer to drm_device and this pointer would be set * @drm_dev: pointer to drm_device and this pointer would be set
* when sub driver calls exynos_drm_subdrv_register(). * when sub driver calls exynos_drm_subdrv_register().
* @manager: subdrv has its own manager to control a hardware appropriately * @manager: subdrv has its own manager to control a hardware appropriately
* and we can access a hardware drawing on this manager. * and we can access a hardware drawing on this manager.
* @probe: this callback would be called by exynos drm driver after * @probe: this callback would be called by exynos drm driver after
* subdrv is registered to it. * subdrv is registered to it.
* @remove: this callback is used to release resources created * @remove: this callback is used to release resources created
* by probe callback. * by probe callback.
* @open: this would be called with drm device file open. * @open: this would be called with drm device file open.
* @close: this would be called with drm device file close. * @close: this would be called with drm device file close.
*/ */
...@@ -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);
}
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 ret = component_add(&pdev->dev, &exynos_dsi_component_ops);
static int exynos_dsi_resume(struct device *dev) if (ret)
{ goto err_del_component;
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;
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/spinlock.h>
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/exynos_drm.h> #include <drm/exynos_drm.h>
...@@ -57,7 +58,6 @@ ...@@ -57,7 +58,6 @@
#define FIMC_SHFACTOR 10 #define FIMC_SHFACTOR 10
#define FIMC_BUF_STOP 1 #define FIMC_BUF_STOP 1
#define FIMC_BUF_START 2 #define FIMC_BUF_START 2
#define FIMC_REG_SZ 32
#define FIMC_WIDTH_ITU_709 1280 #define FIMC_WIDTH_ITU_709 1280
#define FIMC_REFRESH_MAX 60 #define FIMC_REFRESH_MAX 60
#define FIMC_REFRESH_MIN 12 #define FIMC_REFRESH_MIN 12
...@@ -69,9 +69,6 @@ ...@@ -69,9 +69,6 @@
#define get_fimc_context(dev) platform_get_drvdata(to_platform_device(dev)) #define get_fimc_context(dev) platform_get_drvdata(to_platform_device(dev))
#define get_ctx_from_ippdrv(ippdrv) container_of(ippdrv,\ #define get_ctx_from_ippdrv(ippdrv) container_of(ippdrv,\
struct fimc_context, ippdrv); struct fimc_context, ippdrv);
#define fimc_read(offset) readl(ctx->regs + (offset))
#define fimc_write(cfg, offset) writel(cfg, ctx->regs + (offset))
enum fimc_wb { enum fimc_wb {
FIMC_WB_NONE, FIMC_WB_NONE,
FIMC_WB_A, FIMC_WB_A,
...@@ -161,7 +158,7 @@ struct fimc_context { ...@@ -161,7 +158,7 @@ struct fimc_context {
struct exynos_drm_ippdrv ippdrv; struct exynos_drm_ippdrv ippdrv;
struct resource *regs_res; struct resource *regs_res;
void __iomem *regs; void __iomem *regs;
struct mutex lock; spinlock_t lock;
struct clk *clocks[FIMC_CLKS_MAX]; struct clk *clocks[FIMC_CLKS_MAX];
u32 clk_frequency; u32 clk_frequency;
struct regmap *sysreg; struct regmap *sysreg;
...@@ -172,39 +169,53 @@ struct fimc_context { ...@@ -172,39 +169,53 @@ struct fimc_context {
bool suspended; bool suspended;
}; };
static u32 fimc_read(struct fimc_context *ctx, u32 reg)
{
return readl(ctx->regs + reg);
}
static void fimc_write(struct fimc_context *ctx, u32 val, u32 reg)
{
writel(val, ctx->regs + reg);
}
static void fimc_set_bits(struct fimc_context *ctx, u32 reg, u32 bits)
{
void __iomem *r = ctx->regs + reg;
writel(readl(r) | bits, r);
}
static void fimc_clear_bits(struct fimc_context *ctx, u32 reg, u32 bits)
{
void __iomem *r = ctx->regs + reg;
writel(readl(r) & ~bits, r);
}
static void fimc_sw_reset(struct fimc_context *ctx) static void fimc_sw_reset(struct fimc_context *ctx)
{ {
u32 cfg; u32 cfg;
/* stop dma operation */ /* stop dma operation */
cfg = fimc_read(EXYNOS_CISTATUS); cfg = fimc_read(ctx, EXYNOS_CISTATUS);
if (EXYNOS_CISTATUS_GET_ENVID_STATUS(cfg)) { if (EXYNOS_CISTATUS_GET_ENVID_STATUS(cfg))
cfg = fimc_read(EXYNOS_MSCTRL); fimc_clear_bits(ctx, EXYNOS_MSCTRL, EXYNOS_MSCTRL_ENVID);
cfg &= ~EXYNOS_MSCTRL_ENVID;
fimc_write(cfg, EXYNOS_MSCTRL);
}
cfg = fimc_read(EXYNOS_CISRCFMT); fimc_set_bits(ctx, EXYNOS_CISRCFMT, EXYNOS_CISRCFMT_ITU601_8BIT);
cfg |= EXYNOS_CISRCFMT_ITU601_8BIT;
fimc_write(cfg, EXYNOS_CISRCFMT);
/* disable image capture */ /* disable image capture */
cfg = fimc_read(EXYNOS_CIIMGCPT); fimc_clear_bits(ctx, EXYNOS_CIIMGCPT,
cfg &= ~(EXYNOS_CIIMGCPT_IMGCPTEN_SC | EXYNOS_CIIMGCPT_IMGCPTEN); EXYNOS_CIIMGCPT_IMGCPTEN_SC | EXYNOS_CIIMGCPT_IMGCPTEN);
fimc_write(cfg, EXYNOS_CIIMGCPT);
/* s/w reset */ /* s/w reset */
cfg = fimc_read(EXYNOS_CIGCTRL); fimc_set_bits(ctx, EXYNOS_CIGCTRL, EXYNOS_CIGCTRL_SWRST);
cfg |= (EXYNOS_CIGCTRL_SWRST);
fimc_write(cfg, EXYNOS_CIGCTRL);
/* s/w reset complete */ /* s/w reset complete */
cfg = fimc_read(EXYNOS_CIGCTRL); fimc_clear_bits(ctx, EXYNOS_CIGCTRL, EXYNOS_CIGCTRL_SWRST);
cfg &= ~EXYNOS_CIGCTRL_SWRST;
fimc_write(cfg, EXYNOS_CIGCTRL);
/* reset sequence */ /* reset sequence */
fimc_write(0x0, EXYNOS_CIFCNTSEQ); fimc_write(ctx, 0x0, EXYNOS_CIFCNTSEQ);
} }
static int fimc_set_camblk_fimd0_wb(struct fimc_context *ctx) static int fimc_set_camblk_fimd0_wb(struct fimc_context *ctx)
...@@ -220,7 +231,7 @@ static void fimc_set_type_ctrl(struct fimc_context *ctx, enum fimc_wb wb) ...@@ -220,7 +231,7 @@ static void fimc_set_type_ctrl(struct fimc_context *ctx, enum fimc_wb wb)
DRM_DEBUG_KMS("wb[%d]\n", wb); DRM_DEBUG_KMS("wb[%d]\n", wb);
cfg = fimc_read(EXYNOS_CIGCTRL); cfg = fimc_read(ctx, EXYNOS_CIGCTRL);
cfg &= ~(EXYNOS_CIGCTRL_TESTPATTERN_MASK | cfg &= ~(EXYNOS_CIGCTRL_TESTPATTERN_MASK |
EXYNOS_CIGCTRL_SELCAM_ITU_MASK | EXYNOS_CIGCTRL_SELCAM_ITU_MASK |
EXYNOS_CIGCTRL_SELCAM_MIPI_MASK | EXYNOS_CIGCTRL_SELCAM_MIPI_MASK |
...@@ -246,7 +257,7 @@ static void fimc_set_type_ctrl(struct fimc_context *ctx, enum fimc_wb wb) ...@@ -246,7 +257,7 @@ static void fimc_set_type_ctrl(struct fimc_context *ctx, enum fimc_wb wb)
break; break;
} }
fimc_write(cfg, EXYNOS_CIGCTRL); fimc_write(ctx, cfg, EXYNOS_CIGCTRL);
} }
static void fimc_set_polarity(struct fimc_context *ctx, static void fimc_set_polarity(struct fimc_context *ctx,
...@@ -259,7 +270,7 @@ static void fimc_set_polarity(struct fimc_context *ctx, ...@@ -259,7 +270,7 @@ static void fimc_set_polarity(struct fimc_context *ctx,
DRM_DEBUG_KMS("inv_href[%d]inv_hsync[%d]\n", DRM_DEBUG_KMS("inv_href[%d]inv_hsync[%d]\n",
pol->inv_href, pol->inv_hsync); pol->inv_href, pol->inv_hsync);
cfg = fimc_read(EXYNOS_CIGCTRL); cfg = fimc_read(ctx, EXYNOS_CIGCTRL);
cfg &= ~(EXYNOS_CIGCTRL_INVPOLPCLK | EXYNOS_CIGCTRL_INVPOLVSYNC | cfg &= ~(EXYNOS_CIGCTRL_INVPOLPCLK | EXYNOS_CIGCTRL_INVPOLVSYNC |
EXYNOS_CIGCTRL_INVPOLHREF | EXYNOS_CIGCTRL_INVPOLHSYNC); EXYNOS_CIGCTRL_INVPOLHREF | EXYNOS_CIGCTRL_INVPOLHSYNC);
...@@ -272,7 +283,7 @@ static void fimc_set_polarity(struct fimc_context *ctx, ...@@ -272,7 +283,7 @@ static void fimc_set_polarity(struct fimc_context *ctx,
if (pol->inv_hsync) if (pol->inv_hsync)
cfg |= EXYNOS_CIGCTRL_INVPOLHSYNC; cfg |= EXYNOS_CIGCTRL_INVPOLHSYNC;
fimc_write(cfg, EXYNOS_CIGCTRL); fimc_write(ctx, cfg, EXYNOS_CIGCTRL);
} }
static void fimc_handle_jpeg(struct fimc_context *ctx, bool enable) static void fimc_handle_jpeg(struct fimc_context *ctx, bool enable)
...@@ -281,70 +292,54 @@ static void fimc_handle_jpeg(struct fimc_context *ctx, bool enable) ...@@ -281,70 +292,54 @@ static void fimc_handle_jpeg(struct fimc_context *ctx, bool enable)
DRM_DEBUG_KMS("enable[%d]\n", enable); DRM_DEBUG_KMS("enable[%d]\n", enable);
cfg = fimc_read(EXYNOS_CIGCTRL); cfg = fimc_read(ctx, EXYNOS_CIGCTRL);
if (enable) if (enable)
cfg |= EXYNOS_CIGCTRL_CAM_JPEG; cfg |= EXYNOS_CIGCTRL_CAM_JPEG;
else else
cfg &= ~EXYNOS_CIGCTRL_CAM_JPEG; cfg &= ~EXYNOS_CIGCTRL_CAM_JPEG;
fimc_write(cfg, EXYNOS_CIGCTRL); fimc_write(ctx, cfg, EXYNOS_CIGCTRL);
} }
static void fimc_handle_irq(struct fimc_context *ctx, bool enable, static void fimc_mask_irq(struct fimc_context *ctx, bool enable)
bool overflow, bool level)
{ {
u32 cfg; u32 cfg;
DRM_DEBUG_KMS("enable[%d]overflow[%d]level[%d]\n", DRM_DEBUG_KMS("enable[%d]\n", enable);
enable, overflow, level);
cfg = fimc_read(EXYNOS_CIGCTRL); cfg = fimc_read(ctx, EXYNOS_CIGCTRL);
if (enable) { if (enable) {
cfg &= ~(EXYNOS_CIGCTRL_IRQ_OVFEN | EXYNOS_CIGCTRL_IRQ_LEVEL); cfg &= ~EXYNOS_CIGCTRL_IRQ_OVFEN;
cfg |= EXYNOS_CIGCTRL_IRQ_ENABLE; cfg |= EXYNOS_CIGCTRL_IRQ_ENABLE | EXYNOS_CIGCTRL_IRQ_LEVEL;
if (overflow)
cfg |= EXYNOS_CIGCTRL_IRQ_OVFEN;
if (level)
cfg |= EXYNOS_CIGCTRL_IRQ_LEVEL;
} else } else
cfg &= ~(EXYNOS_CIGCTRL_IRQ_OVFEN | EXYNOS_CIGCTRL_IRQ_ENABLE); cfg &= ~EXYNOS_CIGCTRL_IRQ_ENABLE;
fimc_write(ctx, cfg, EXYNOS_CIGCTRL);
fimc_write(cfg, EXYNOS_CIGCTRL);
} }
static void fimc_clear_irq(struct fimc_context *ctx) static void fimc_clear_irq(struct fimc_context *ctx)
{ {
u32 cfg; fimc_set_bits(ctx, EXYNOS_CIGCTRL, EXYNOS_CIGCTRL_IRQ_CLR);
cfg = fimc_read(EXYNOS_CIGCTRL);
cfg |= EXYNOS_CIGCTRL_IRQ_CLR;
fimc_write(cfg, EXYNOS_CIGCTRL);
} }
static bool fimc_check_ovf(struct fimc_context *ctx) static bool fimc_check_ovf(struct fimc_context *ctx)
{ {
struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
u32 cfg, status, flag; u32 status, flag;
status = fimc_read(EXYNOS_CISTATUS); status = fimc_read(ctx, EXYNOS_CISTATUS);
flag = EXYNOS_CISTATUS_OVFIY | EXYNOS_CISTATUS_OVFICB | flag = EXYNOS_CISTATUS_OVFIY | EXYNOS_CISTATUS_OVFICB |
EXYNOS_CISTATUS_OVFICR; EXYNOS_CISTATUS_OVFICR;
DRM_DEBUG_KMS("flag[0x%x]\n", flag); DRM_DEBUG_KMS("flag[0x%x]\n", flag);
if (status & flag) { if (status & flag) {
cfg = fimc_read(EXYNOS_CIWDOFST); fimc_set_bits(ctx, EXYNOS_CIWDOFST,
cfg |= (EXYNOS_CIWDOFST_CLROVFIY | EXYNOS_CIWDOFST_CLROVFICB | EXYNOS_CIWDOFST_CLROVFIY | EXYNOS_CIWDOFST_CLROVFICB |
EXYNOS_CIWDOFST_CLROVFICR); EXYNOS_CIWDOFST_CLROVFICR);
fimc_clear_bits(ctx, EXYNOS_CIWDOFST,
fimc_write(cfg, EXYNOS_CIWDOFST); EXYNOS_CIWDOFST_CLROVFIY | EXYNOS_CIWDOFST_CLROVFICB |
cfg = fimc_read(EXYNOS_CIWDOFST);
cfg &= ~(EXYNOS_CIWDOFST_CLROVFIY | EXYNOS_CIWDOFST_CLROVFICB |
EXYNOS_CIWDOFST_CLROVFICR); EXYNOS_CIWDOFST_CLROVFICR);
fimc_write(cfg, EXYNOS_CIWDOFST);
dev_err(ippdrv->dev, "occurred overflow at %d, status 0x%x.\n", dev_err(ippdrv->dev, "occurred overflow at %d, status 0x%x.\n",
ctx->id, status); ctx->id, status);
return true; return true;
...@@ -357,7 +352,7 @@ static bool fimc_check_frame_end(struct fimc_context *ctx) ...@@ -357,7 +352,7 @@ static bool fimc_check_frame_end(struct fimc_context *ctx)
{ {
u32 cfg; u32 cfg;
cfg = fimc_read(EXYNOS_CISTATUS); cfg = fimc_read(ctx, EXYNOS_CISTATUS);
DRM_DEBUG_KMS("cfg[0x%x]\n", cfg); DRM_DEBUG_KMS("cfg[0x%x]\n", cfg);
...@@ -365,7 +360,7 @@ static bool fimc_check_frame_end(struct fimc_context *ctx) ...@@ -365,7 +360,7 @@ static bool fimc_check_frame_end(struct fimc_context *ctx)
return false; return false;
cfg &= ~(EXYNOS_CISTATUS_FRAMEEND); cfg &= ~(EXYNOS_CISTATUS_FRAMEEND);
fimc_write(cfg, EXYNOS_CISTATUS); fimc_write(ctx, cfg, EXYNOS_CISTATUS);
return true; return true;
} }
...@@ -375,7 +370,7 @@ static int fimc_get_buf_id(struct fimc_context *ctx) ...@@ -375,7 +370,7 @@ static int fimc_get_buf_id(struct fimc_context *ctx)
u32 cfg; u32 cfg;
int frame_cnt, buf_id; int frame_cnt, buf_id;
cfg = fimc_read(EXYNOS_CISTATUS2); cfg = fimc_read(ctx, EXYNOS_CISTATUS2);
frame_cnt = EXYNOS_CISTATUS2_GET_FRAMECOUNT_BEFORE(cfg); frame_cnt = EXYNOS_CISTATUS2_GET_FRAMECOUNT_BEFORE(cfg);
if (frame_cnt == 0) if (frame_cnt == 0)
...@@ -402,13 +397,13 @@ static void fimc_handle_lastend(struct fimc_context *ctx, bool enable) ...@@ -402,13 +397,13 @@ static void fimc_handle_lastend(struct fimc_context *ctx, bool enable)
DRM_DEBUG_KMS("enable[%d]\n", enable); DRM_DEBUG_KMS("enable[%d]\n", enable);
cfg = fimc_read(EXYNOS_CIOCTRL); cfg = fimc_read(ctx, EXYNOS_CIOCTRL);
if (enable) if (enable)
cfg |= EXYNOS_CIOCTRL_LASTENDEN; cfg |= EXYNOS_CIOCTRL_LASTENDEN;
else else
cfg &= ~EXYNOS_CIOCTRL_LASTENDEN; cfg &= ~EXYNOS_CIOCTRL_LASTENDEN;
fimc_write(cfg, EXYNOS_CIOCTRL); fimc_write(ctx, cfg, EXYNOS_CIOCTRL);
} }
...@@ -420,18 +415,18 @@ static int fimc_src_set_fmt_order(struct fimc_context *ctx, u32 fmt) ...@@ -420,18 +415,18 @@ static int fimc_src_set_fmt_order(struct fimc_context *ctx, u32 fmt)
DRM_DEBUG_KMS("fmt[0x%x]\n", fmt); DRM_DEBUG_KMS("fmt[0x%x]\n", fmt);
/* RGB */ /* RGB */
cfg = fimc_read(EXYNOS_CISCCTRL); cfg = fimc_read(ctx, EXYNOS_CISCCTRL);
cfg &= ~EXYNOS_CISCCTRL_INRGB_FMT_RGB_MASK; cfg &= ~EXYNOS_CISCCTRL_INRGB_FMT_RGB_MASK;
switch (fmt) { switch (fmt) {
case DRM_FORMAT_RGB565: case DRM_FORMAT_RGB565:
cfg |= EXYNOS_CISCCTRL_INRGB_FMT_RGB565; cfg |= EXYNOS_CISCCTRL_INRGB_FMT_RGB565;
fimc_write(cfg, EXYNOS_CISCCTRL); fimc_write(ctx, cfg, EXYNOS_CISCCTRL);
return 0; return 0;
case DRM_FORMAT_RGB888: case DRM_FORMAT_RGB888:
case DRM_FORMAT_XRGB8888: case DRM_FORMAT_XRGB8888:
cfg |= EXYNOS_CISCCTRL_INRGB_FMT_RGB888; cfg |= EXYNOS_CISCCTRL_INRGB_FMT_RGB888;
fimc_write(cfg, EXYNOS_CISCCTRL); fimc_write(ctx, cfg, EXYNOS_CISCCTRL);
return 0; return 0;
default: default:
/* bypass */ /* bypass */
...@@ -439,7 +434,7 @@ static int fimc_src_set_fmt_order(struct fimc_context *ctx, u32 fmt) ...@@ -439,7 +434,7 @@ static int fimc_src_set_fmt_order(struct fimc_context *ctx, u32 fmt)
} }
/* YUV */ /* YUV */
cfg = fimc_read(EXYNOS_MSCTRL); cfg = fimc_read(ctx, EXYNOS_MSCTRL);
cfg &= ~(EXYNOS_MSCTRL_ORDER2P_SHIFT_MASK | cfg &= ~(EXYNOS_MSCTRL_ORDER2P_SHIFT_MASK |
EXYNOS_MSCTRL_C_INT_IN_2PLANE | EXYNOS_MSCTRL_C_INT_IN_2PLANE |
EXYNOS_MSCTRL_ORDER422_YCBYCR); EXYNOS_MSCTRL_ORDER422_YCBYCR);
...@@ -479,7 +474,7 @@ static int fimc_src_set_fmt_order(struct fimc_context *ctx, u32 fmt) ...@@ -479,7 +474,7 @@ static int fimc_src_set_fmt_order(struct fimc_context *ctx, u32 fmt)
return -EINVAL; return -EINVAL;
} }
fimc_write(cfg, EXYNOS_MSCTRL); fimc_write(ctx, cfg, EXYNOS_MSCTRL);
return 0; return 0;
} }
...@@ -492,7 +487,7 @@ static int fimc_src_set_fmt(struct device *dev, u32 fmt) ...@@ -492,7 +487,7 @@ static int fimc_src_set_fmt(struct device *dev, u32 fmt)
DRM_DEBUG_KMS("fmt[0x%x]\n", fmt); DRM_DEBUG_KMS("fmt[0x%x]\n", fmt);
cfg = fimc_read(EXYNOS_MSCTRL); cfg = fimc_read(ctx, EXYNOS_MSCTRL);
cfg &= ~EXYNOS_MSCTRL_INFORMAT_RGB; cfg &= ~EXYNOS_MSCTRL_INFORMAT_RGB;
switch (fmt) { switch (fmt) {
...@@ -527,9 +522,9 @@ static int fimc_src_set_fmt(struct device *dev, u32 fmt) ...@@ -527,9 +522,9 @@ static int fimc_src_set_fmt(struct device *dev, u32 fmt)
return -EINVAL; return -EINVAL;
} }
fimc_write(cfg, EXYNOS_MSCTRL); fimc_write(ctx, cfg, EXYNOS_MSCTRL);
cfg = fimc_read(EXYNOS_CIDMAPARAM); cfg = fimc_read(ctx, EXYNOS_CIDMAPARAM);
cfg &= ~EXYNOS_CIDMAPARAM_R_MODE_MASK; cfg &= ~EXYNOS_CIDMAPARAM_R_MODE_MASK;
if (fmt == DRM_FORMAT_NV12MT) if (fmt == DRM_FORMAT_NV12MT)
...@@ -537,7 +532,7 @@ static int fimc_src_set_fmt(struct device *dev, u32 fmt) ...@@ -537,7 +532,7 @@ static int fimc_src_set_fmt(struct device *dev, u32 fmt)
else else
cfg |= EXYNOS_CIDMAPARAM_R_MODE_LINEAR; cfg |= EXYNOS_CIDMAPARAM_R_MODE_LINEAR;
fimc_write(cfg, EXYNOS_CIDMAPARAM); fimc_write(ctx, cfg, EXYNOS_CIDMAPARAM);
return fimc_src_set_fmt_order(ctx, fmt); return fimc_src_set_fmt_order(ctx, fmt);
} }
...@@ -552,11 +547,11 @@ static int fimc_src_set_transf(struct device *dev, ...@@ -552,11 +547,11 @@ static int fimc_src_set_transf(struct device *dev,
DRM_DEBUG_KMS("degree[%d]flip[0x%x]\n", degree, flip); DRM_DEBUG_KMS("degree[%d]flip[0x%x]\n", degree, flip);
cfg1 = fimc_read(EXYNOS_MSCTRL); cfg1 = fimc_read(ctx, EXYNOS_MSCTRL);
cfg1 &= ~(EXYNOS_MSCTRL_FLIP_X_MIRROR | cfg1 &= ~(EXYNOS_MSCTRL_FLIP_X_MIRROR |
EXYNOS_MSCTRL_FLIP_Y_MIRROR); EXYNOS_MSCTRL_FLIP_Y_MIRROR);
cfg2 = fimc_read(EXYNOS_CITRGFMT); cfg2 = fimc_read(ctx, EXYNOS_CITRGFMT);
cfg2 &= ~EXYNOS_CITRGFMT_INROT90_CLOCKWISE; cfg2 &= ~EXYNOS_CITRGFMT_INROT90_CLOCKWISE;
switch (degree) { switch (degree) {
...@@ -595,8 +590,8 @@ static int fimc_src_set_transf(struct device *dev, ...@@ -595,8 +590,8 @@ static int fimc_src_set_transf(struct device *dev,
return -EINVAL; return -EINVAL;
} }
fimc_write(cfg1, EXYNOS_MSCTRL); fimc_write(ctx, cfg1, EXYNOS_MSCTRL);
fimc_write(cfg2, EXYNOS_CITRGFMT); fimc_write(ctx, cfg2, EXYNOS_CITRGFMT);
*swap = (cfg2 & EXYNOS_CITRGFMT_INROT90_CLOCKWISE) ? 1 : 0; *swap = (cfg2 & EXYNOS_CITRGFMT_INROT90_CLOCKWISE) ? 1 : 0;
return 0; return 0;
...@@ -621,17 +616,17 @@ static int fimc_set_window(struct fimc_context *ctx, ...@@ -621,17 +616,17 @@ static int fimc_set_window(struct fimc_context *ctx,
* set window offset 1, 2 size * set window offset 1, 2 size
* check figure 43-21 in user manual * check figure 43-21 in user manual
*/ */
cfg = fimc_read(EXYNOS_CIWDOFST); cfg = fimc_read(ctx, EXYNOS_CIWDOFST);
cfg &= ~(EXYNOS_CIWDOFST_WINHOROFST_MASK | cfg &= ~(EXYNOS_CIWDOFST_WINHOROFST_MASK |
EXYNOS_CIWDOFST_WINVEROFST_MASK); EXYNOS_CIWDOFST_WINVEROFST_MASK);
cfg |= (EXYNOS_CIWDOFST_WINHOROFST(h1) | cfg |= (EXYNOS_CIWDOFST_WINHOROFST(h1) |
EXYNOS_CIWDOFST_WINVEROFST(v1)); EXYNOS_CIWDOFST_WINVEROFST(v1));
cfg |= EXYNOS_CIWDOFST_WINOFSEN; cfg |= EXYNOS_CIWDOFST_WINOFSEN;
fimc_write(cfg, EXYNOS_CIWDOFST); fimc_write(ctx, cfg, EXYNOS_CIWDOFST);
cfg = (EXYNOS_CIWDOFST2_WINHOROFST2(h2) | cfg = (EXYNOS_CIWDOFST2_WINHOROFST2(h2) |
EXYNOS_CIWDOFST2_WINVEROFST2(v2)); EXYNOS_CIWDOFST2_WINVEROFST2(v2));
fimc_write(cfg, EXYNOS_CIWDOFST2); fimc_write(ctx, cfg, EXYNOS_CIWDOFST2);
return 0; return 0;
} }
...@@ -651,7 +646,7 @@ static int fimc_src_set_size(struct device *dev, int swap, ...@@ -651,7 +646,7 @@ static int fimc_src_set_size(struct device *dev, int swap,
cfg = (EXYNOS_ORGISIZE_HORIZONTAL(img_sz.hsize) | cfg = (EXYNOS_ORGISIZE_HORIZONTAL(img_sz.hsize) |
EXYNOS_ORGISIZE_VERTICAL(img_sz.vsize)); EXYNOS_ORGISIZE_VERTICAL(img_sz.vsize));
fimc_write(cfg, EXYNOS_ORGISIZE); fimc_write(ctx, cfg, EXYNOS_ORGISIZE);
DRM_DEBUG_KMS("x[%d]y[%d]w[%d]h[%d]\n", pos->x, pos->y, pos->w, pos->h); DRM_DEBUG_KMS("x[%d]y[%d]w[%d]h[%d]\n", pos->x, pos->y, pos->w, pos->h);
...@@ -663,12 +658,12 @@ static int fimc_src_set_size(struct device *dev, int swap, ...@@ -663,12 +658,12 @@ static int fimc_src_set_size(struct device *dev, int swap,
} }
/* set input DMA image size */ /* set input DMA image size */
cfg = fimc_read(EXYNOS_CIREAL_ISIZE); cfg = fimc_read(ctx, EXYNOS_CIREAL_ISIZE);
cfg &= ~(EXYNOS_CIREAL_ISIZE_HEIGHT_MASK | cfg &= ~(EXYNOS_CIREAL_ISIZE_HEIGHT_MASK |
EXYNOS_CIREAL_ISIZE_WIDTH_MASK); EXYNOS_CIREAL_ISIZE_WIDTH_MASK);
cfg |= (EXYNOS_CIREAL_ISIZE_WIDTH(img_pos.w) | cfg |= (EXYNOS_CIREAL_ISIZE_WIDTH(img_pos.w) |
EXYNOS_CIREAL_ISIZE_HEIGHT(img_pos.h)); EXYNOS_CIREAL_ISIZE_HEIGHT(img_pos.h));
fimc_write(cfg, EXYNOS_CIREAL_ISIZE); fimc_write(ctx, cfg, EXYNOS_CIREAL_ISIZE);
/* /*
* set input FIFO image size * set input FIFO image size
...@@ -677,18 +672,18 @@ static int fimc_src_set_size(struct device *dev, int swap, ...@@ -677,18 +672,18 @@ static int fimc_src_set_size(struct device *dev, int swap,
cfg = (EXYNOS_CISRCFMT_ITU601_8BIT | cfg = (EXYNOS_CISRCFMT_ITU601_8BIT |
EXYNOS_CISRCFMT_SOURCEHSIZE(img_sz.hsize) | EXYNOS_CISRCFMT_SOURCEHSIZE(img_sz.hsize) |
EXYNOS_CISRCFMT_SOURCEVSIZE(img_sz.vsize)); EXYNOS_CISRCFMT_SOURCEVSIZE(img_sz.vsize));
fimc_write(cfg, EXYNOS_CISRCFMT); fimc_write(ctx, cfg, EXYNOS_CISRCFMT);
/* offset Y(RGB), Cb, Cr */ /* offset Y(RGB), Cb, Cr */
cfg = (EXYNOS_CIIYOFF_HORIZONTAL(img_pos.x) | cfg = (EXYNOS_CIIYOFF_HORIZONTAL(img_pos.x) |
EXYNOS_CIIYOFF_VERTICAL(img_pos.y)); EXYNOS_CIIYOFF_VERTICAL(img_pos.y));
fimc_write(cfg, EXYNOS_CIIYOFF); fimc_write(ctx, cfg, EXYNOS_CIIYOFF);
cfg = (EXYNOS_CIICBOFF_HORIZONTAL(img_pos.x) | cfg = (EXYNOS_CIICBOFF_HORIZONTAL(img_pos.x) |
EXYNOS_CIICBOFF_VERTICAL(img_pos.y)); EXYNOS_CIICBOFF_VERTICAL(img_pos.y));
fimc_write(cfg, EXYNOS_CIICBOFF); fimc_write(ctx, cfg, EXYNOS_CIICBOFF);
cfg = (EXYNOS_CIICROFF_HORIZONTAL(img_pos.x) | cfg = (EXYNOS_CIICROFF_HORIZONTAL(img_pos.x) |
EXYNOS_CIICROFF_VERTICAL(img_pos.y)); EXYNOS_CIICROFF_VERTICAL(img_pos.y));
fimc_write(cfg, EXYNOS_CIICROFF); fimc_write(ctx, cfg, EXYNOS_CIICROFF);
return fimc_set_window(ctx, &img_pos, &img_sz); return fimc_set_window(ctx, &img_pos, &img_sz);
} }
...@@ -722,25 +717,25 @@ static int fimc_src_set_addr(struct device *dev, ...@@ -722,25 +717,25 @@ static int fimc_src_set_addr(struct device *dev,
switch (buf_type) { switch (buf_type) {
case IPP_BUF_ENQUEUE: case IPP_BUF_ENQUEUE:
config = &property->config[EXYNOS_DRM_OPS_SRC]; config = &property->config[EXYNOS_DRM_OPS_SRC];
fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_Y], fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_Y],
EXYNOS_CIIYSA(buf_id)); EXYNOS_CIIYSA(buf_id));
if (config->fmt == DRM_FORMAT_YVU420) { if (config->fmt == DRM_FORMAT_YVU420) {
fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR], fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CR],
EXYNOS_CIICBSA(buf_id)); EXYNOS_CIICBSA(buf_id));
fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB], fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CB],
EXYNOS_CIICRSA(buf_id)); EXYNOS_CIICRSA(buf_id));
} else { } else {
fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB], fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CB],
EXYNOS_CIICBSA(buf_id)); EXYNOS_CIICBSA(buf_id));
fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR], fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CR],
EXYNOS_CIICRSA(buf_id)); EXYNOS_CIICRSA(buf_id));
} }
break; break;
case IPP_BUF_DEQUEUE: case IPP_BUF_DEQUEUE:
fimc_write(0x0, EXYNOS_CIIYSA(buf_id)); fimc_write(ctx, 0x0, EXYNOS_CIIYSA(buf_id));
fimc_write(0x0, EXYNOS_CIICBSA(buf_id)); fimc_write(ctx, 0x0, EXYNOS_CIICBSA(buf_id));
fimc_write(0x0, EXYNOS_CIICRSA(buf_id)); fimc_write(ctx, 0x0, EXYNOS_CIICRSA(buf_id));
break; break;
default: default:
/* bypass */ /* bypass */
...@@ -765,22 +760,22 @@ static int fimc_dst_set_fmt_order(struct fimc_context *ctx, u32 fmt) ...@@ -765,22 +760,22 @@ static int fimc_dst_set_fmt_order(struct fimc_context *ctx, u32 fmt)
DRM_DEBUG_KMS("fmt[0x%x]\n", fmt); DRM_DEBUG_KMS("fmt[0x%x]\n", fmt);
/* RGB */ /* RGB */
cfg = fimc_read(EXYNOS_CISCCTRL); cfg = fimc_read(ctx, EXYNOS_CISCCTRL);
cfg &= ~EXYNOS_CISCCTRL_OUTRGB_FMT_RGB_MASK; cfg &= ~EXYNOS_CISCCTRL_OUTRGB_FMT_RGB_MASK;
switch (fmt) { switch (fmt) {
case DRM_FORMAT_RGB565: case DRM_FORMAT_RGB565:
cfg |= EXYNOS_CISCCTRL_OUTRGB_FMT_RGB565; cfg |= EXYNOS_CISCCTRL_OUTRGB_FMT_RGB565;
fimc_write(cfg, EXYNOS_CISCCTRL); fimc_write(ctx, cfg, EXYNOS_CISCCTRL);
return 0; return 0;
case DRM_FORMAT_RGB888: case DRM_FORMAT_RGB888:
cfg |= EXYNOS_CISCCTRL_OUTRGB_FMT_RGB888; cfg |= EXYNOS_CISCCTRL_OUTRGB_FMT_RGB888;
fimc_write(cfg, EXYNOS_CISCCTRL); fimc_write(ctx, cfg, EXYNOS_CISCCTRL);
return 0; return 0;
case DRM_FORMAT_XRGB8888: case DRM_FORMAT_XRGB8888:
cfg |= (EXYNOS_CISCCTRL_OUTRGB_FMT_RGB888 | cfg |= (EXYNOS_CISCCTRL_OUTRGB_FMT_RGB888 |
EXYNOS_CISCCTRL_EXTRGB_EXTENSION); EXYNOS_CISCCTRL_EXTRGB_EXTENSION);
fimc_write(cfg, EXYNOS_CISCCTRL); fimc_write(ctx, cfg, EXYNOS_CISCCTRL);
break; break;
default: default:
/* bypass */ /* bypass */
...@@ -788,7 +783,7 @@ static int fimc_dst_set_fmt_order(struct fimc_context *ctx, u32 fmt) ...@@ -788,7 +783,7 @@ static int fimc_dst_set_fmt_order(struct fimc_context *ctx, u32 fmt)
} }
/* YUV */ /* YUV */
cfg = fimc_read(EXYNOS_CIOCTRL); cfg = fimc_read(ctx, EXYNOS_CIOCTRL);
cfg &= ~(EXYNOS_CIOCTRL_ORDER2P_MASK | cfg &= ~(EXYNOS_CIOCTRL_ORDER2P_MASK |
EXYNOS_CIOCTRL_ORDER422_MASK | EXYNOS_CIOCTRL_ORDER422_MASK |
EXYNOS_CIOCTRL_YCBCR_PLANE_MASK); EXYNOS_CIOCTRL_YCBCR_PLANE_MASK);
...@@ -830,7 +825,7 @@ static int fimc_dst_set_fmt_order(struct fimc_context *ctx, u32 fmt) ...@@ -830,7 +825,7 @@ static int fimc_dst_set_fmt_order(struct fimc_context *ctx, u32 fmt)
return -EINVAL; return -EINVAL;
} }
fimc_write(cfg, EXYNOS_CIOCTRL); fimc_write(ctx, cfg, EXYNOS_CIOCTRL);
return 0; return 0;
} }
...@@ -843,16 +838,16 @@ static int fimc_dst_set_fmt(struct device *dev, u32 fmt) ...@@ -843,16 +838,16 @@ static int fimc_dst_set_fmt(struct device *dev, u32 fmt)
DRM_DEBUG_KMS("fmt[0x%x]\n", fmt); DRM_DEBUG_KMS("fmt[0x%x]\n", fmt);
cfg = fimc_read(EXYNOS_CIEXTEN); cfg = fimc_read(ctx, EXYNOS_CIEXTEN);
if (fmt == DRM_FORMAT_AYUV) { if (fmt == DRM_FORMAT_AYUV) {
cfg |= EXYNOS_CIEXTEN_YUV444_OUT; cfg |= EXYNOS_CIEXTEN_YUV444_OUT;
fimc_write(cfg, EXYNOS_CIEXTEN); fimc_write(ctx, cfg, EXYNOS_CIEXTEN);
} else { } else {
cfg &= ~EXYNOS_CIEXTEN_YUV444_OUT; cfg &= ~EXYNOS_CIEXTEN_YUV444_OUT;
fimc_write(cfg, EXYNOS_CIEXTEN); fimc_write(ctx, cfg, EXYNOS_CIEXTEN);
cfg = fimc_read(EXYNOS_CITRGFMT); cfg = fimc_read(ctx, EXYNOS_CITRGFMT);
cfg &= ~EXYNOS_CITRGFMT_OUTFORMAT_MASK; cfg &= ~EXYNOS_CITRGFMT_OUTFORMAT_MASK;
switch (fmt) { switch (fmt) {
...@@ -885,10 +880,10 @@ static int fimc_dst_set_fmt(struct device *dev, u32 fmt) ...@@ -885,10 +880,10 @@ static int fimc_dst_set_fmt(struct device *dev, u32 fmt)
return -EINVAL; return -EINVAL;
} }
fimc_write(cfg, EXYNOS_CITRGFMT); fimc_write(ctx, cfg, EXYNOS_CITRGFMT);
} }
cfg = fimc_read(EXYNOS_CIDMAPARAM); cfg = fimc_read(ctx, EXYNOS_CIDMAPARAM);
cfg &= ~EXYNOS_CIDMAPARAM_W_MODE_MASK; cfg &= ~EXYNOS_CIDMAPARAM_W_MODE_MASK;
if (fmt == DRM_FORMAT_NV12MT) if (fmt == DRM_FORMAT_NV12MT)
...@@ -896,7 +891,7 @@ static int fimc_dst_set_fmt(struct device *dev, u32 fmt) ...@@ -896,7 +891,7 @@ static int fimc_dst_set_fmt(struct device *dev, u32 fmt)
else else
cfg |= EXYNOS_CIDMAPARAM_W_MODE_LINEAR; cfg |= EXYNOS_CIDMAPARAM_W_MODE_LINEAR;
fimc_write(cfg, EXYNOS_CIDMAPARAM); fimc_write(ctx, cfg, EXYNOS_CIDMAPARAM);
return fimc_dst_set_fmt_order(ctx, fmt); return fimc_dst_set_fmt_order(ctx, fmt);
} }
...@@ -911,7 +906,7 @@ static int fimc_dst_set_transf(struct device *dev, ...@@ -911,7 +906,7 @@ static int fimc_dst_set_transf(struct device *dev,
DRM_DEBUG_KMS("degree[%d]flip[0x%x]\n", degree, flip); DRM_DEBUG_KMS("degree[%d]flip[0x%x]\n", degree, flip);
cfg = fimc_read(EXYNOS_CITRGFMT); cfg = fimc_read(ctx, EXYNOS_CITRGFMT);
cfg &= ~EXYNOS_CITRGFMT_FLIP_MASK; cfg &= ~EXYNOS_CITRGFMT_FLIP_MASK;
cfg &= ~EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE; cfg &= ~EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE;
...@@ -951,53 +946,23 @@ static int fimc_dst_set_transf(struct device *dev, ...@@ -951,53 +946,23 @@ static int fimc_dst_set_transf(struct device *dev,
return -EINVAL; return -EINVAL;
} }
fimc_write(cfg, EXYNOS_CITRGFMT); fimc_write(ctx, cfg, EXYNOS_CITRGFMT);
*swap = (cfg & EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE) ? 1 : 0; *swap = (cfg & EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE) ? 1 : 0;
return 0; return 0;
} }
static int fimc_get_ratio_shift(u32 src, u32 dst, u32 *ratio, u32 *shift)
{
DRM_DEBUG_KMS("src[%d]dst[%d]\n", src, dst);
if (src >= dst * 64) {
DRM_ERROR("failed to make ratio and shift.\n");
return -EINVAL;
} else if (src >= dst * 32) {
*ratio = 32;
*shift = 5;
} else if (src >= dst * 16) {
*ratio = 16;
*shift = 4;
} else if (src >= dst * 8) {
*ratio = 8;
*shift = 3;
} else if (src >= dst * 4) {
*ratio = 4;
*shift = 2;
} else if (src >= dst * 2) {
*ratio = 2;
*shift = 1;
} else {
*ratio = 1;
*shift = 0;
}
return 0;
}
static int fimc_set_prescaler(struct fimc_context *ctx, struct fimc_scaler *sc, static int fimc_set_prescaler(struct fimc_context *ctx, struct fimc_scaler *sc,
struct drm_exynos_pos *src, struct drm_exynos_pos *dst) struct drm_exynos_pos *src, struct drm_exynos_pos *dst)
{ {
struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
u32 cfg, cfg_ext, shfactor; u32 cfg, cfg_ext, shfactor;
u32 pre_dst_width, pre_dst_height; u32 pre_dst_width, pre_dst_height;
u32 pre_hratio, hfactor, pre_vratio, vfactor; u32 hfactor, vfactor;
int ret = 0; int ret = 0;
u32 src_w, src_h, dst_w, dst_h; u32 src_w, src_h, dst_w, dst_h;
cfg_ext = fimc_read(EXYNOS_CITRGFMT); cfg_ext = fimc_read(ctx, EXYNOS_CITRGFMT);
if (cfg_ext & EXYNOS_CITRGFMT_INROT90_CLOCKWISE) { if (cfg_ext & EXYNOS_CITRGFMT_INROT90_CLOCKWISE) {
src_w = src->h; src_w = src->h;
src_h = src->w; src_h = src->w;
...@@ -1014,24 +979,24 @@ static int fimc_set_prescaler(struct fimc_context *ctx, struct fimc_scaler *sc, ...@@ -1014,24 +979,24 @@ static int fimc_set_prescaler(struct fimc_context *ctx, struct fimc_scaler *sc,
dst_h = dst->h; dst_h = dst->h;
} }
ret = fimc_get_ratio_shift(src_w, dst_w, &pre_hratio, &hfactor); /* fimc_ippdrv_check_property assures that dividers are not null */
if (ret) { hfactor = fls(src_w / dst_w / 2);
if (hfactor > FIMC_SHFACTOR / 2) {
dev_err(ippdrv->dev, "failed to get ratio horizontal.\n"); dev_err(ippdrv->dev, "failed to get ratio horizontal.\n");
return ret; return -EINVAL;
} }
ret = fimc_get_ratio_shift(src_h, dst_h, &pre_vratio, &vfactor); vfactor = fls(src_h / dst_h / 2);
if (ret) { if (vfactor > FIMC_SHFACTOR / 2) {
dev_err(ippdrv->dev, "failed to get ratio vertical.\n"); dev_err(ippdrv->dev, "failed to get ratio vertical.\n");
return ret; return -EINVAL;
} }
pre_dst_width = src_w / pre_hratio; pre_dst_width = src_w >> hfactor;
pre_dst_height = src_h / pre_vratio; pre_dst_height = src_h >> vfactor;
DRM_DEBUG_KMS("pre_dst_width[%d]pre_dst_height[%d]\n", DRM_DEBUG_KMS("pre_dst_width[%d]pre_dst_height[%d]\n",
pre_dst_width, pre_dst_height); pre_dst_width, pre_dst_height);
DRM_DEBUG_KMS("pre_hratio[%d]hfactor[%d]pre_vratio[%d]vfactor[%d]\n", DRM_DEBUG_KMS("hfactor[%d]vfactor[%d]\n", hfactor, vfactor);
pre_hratio, hfactor, pre_vratio, vfactor);
sc->hratio = (src_w << 14) / (dst_w << hfactor); sc->hratio = (src_w << 14) / (dst_w << hfactor);
sc->vratio = (src_h << 14) / (dst_h << vfactor); sc->vratio = (src_h << 14) / (dst_h << vfactor);
...@@ -1044,13 +1009,13 @@ static int fimc_set_prescaler(struct fimc_context *ctx, struct fimc_scaler *sc, ...@@ -1044,13 +1009,13 @@ static int fimc_set_prescaler(struct fimc_context *ctx, struct fimc_scaler *sc,
DRM_DEBUG_KMS("shfactor[%d]\n", shfactor); DRM_DEBUG_KMS("shfactor[%d]\n", shfactor);
cfg = (EXYNOS_CISCPRERATIO_SHFACTOR(shfactor) | cfg = (EXYNOS_CISCPRERATIO_SHFACTOR(shfactor) |
EXYNOS_CISCPRERATIO_PREHORRATIO(pre_hratio) | EXYNOS_CISCPRERATIO_PREHORRATIO(1 << hfactor) |
EXYNOS_CISCPRERATIO_PREVERRATIO(pre_vratio)); EXYNOS_CISCPRERATIO_PREVERRATIO(1 << vfactor));
fimc_write(cfg, EXYNOS_CISCPRERATIO); fimc_write(ctx, cfg, EXYNOS_CISCPRERATIO);
cfg = (EXYNOS_CISCPREDST_PREDSTWIDTH(pre_dst_width) | cfg = (EXYNOS_CISCPREDST_PREDSTWIDTH(pre_dst_width) |
EXYNOS_CISCPREDST_PREDSTHEIGHT(pre_dst_height)); EXYNOS_CISCPREDST_PREDSTHEIGHT(pre_dst_height));
fimc_write(cfg, EXYNOS_CISCPREDST); fimc_write(ctx, cfg, EXYNOS_CISCPREDST);
return ret; return ret;
} }
...@@ -1064,7 +1029,7 @@ static void fimc_set_scaler(struct fimc_context *ctx, struct fimc_scaler *sc) ...@@ -1064,7 +1029,7 @@ static void fimc_set_scaler(struct fimc_context *ctx, struct fimc_scaler *sc)
DRM_DEBUG_KMS("hratio[%d]vratio[%d]\n", DRM_DEBUG_KMS("hratio[%d]vratio[%d]\n",
sc->hratio, sc->vratio); sc->hratio, sc->vratio);
cfg = fimc_read(EXYNOS_CISCCTRL); cfg = fimc_read(ctx, EXYNOS_CISCCTRL);
cfg &= ~(EXYNOS_CISCCTRL_SCALERBYPASS | cfg &= ~(EXYNOS_CISCCTRL_SCALERBYPASS |
EXYNOS_CISCCTRL_SCALEUP_H | EXYNOS_CISCCTRL_SCALEUP_V | EXYNOS_CISCCTRL_SCALEUP_H | EXYNOS_CISCCTRL_SCALEUP_V |
EXYNOS_CISCCTRL_MAIN_V_RATIO_MASK | EXYNOS_CISCCTRL_MAIN_V_RATIO_MASK |
...@@ -1084,14 +1049,14 @@ static void fimc_set_scaler(struct fimc_context *ctx, struct fimc_scaler *sc) ...@@ -1084,14 +1049,14 @@ static void fimc_set_scaler(struct fimc_context *ctx, struct fimc_scaler *sc)
cfg |= (EXYNOS_CISCCTRL_MAINHORRATIO((sc->hratio >> 6)) | cfg |= (EXYNOS_CISCCTRL_MAINHORRATIO((sc->hratio >> 6)) |
EXYNOS_CISCCTRL_MAINVERRATIO((sc->vratio >> 6))); EXYNOS_CISCCTRL_MAINVERRATIO((sc->vratio >> 6)));
fimc_write(cfg, EXYNOS_CISCCTRL); fimc_write(ctx, cfg, EXYNOS_CISCCTRL);
cfg_ext = fimc_read(EXYNOS_CIEXTEN); cfg_ext = fimc_read(ctx, EXYNOS_CIEXTEN);
cfg_ext &= ~EXYNOS_CIEXTEN_MAINHORRATIO_EXT_MASK; cfg_ext &= ~EXYNOS_CIEXTEN_MAINHORRATIO_EXT_MASK;
cfg_ext &= ~EXYNOS_CIEXTEN_MAINVERRATIO_EXT_MASK; cfg_ext &= ~EXYNOS_CIEXTEN_MAINVERRATIO_EXT_MASK;
cfg_ext |= (EXYNOS_CIEXTEN_MAINHORRATIO_EXT(sc->hratio) | cfg_ext |= (EXYNOS_CIEXTEN_MAINHORRATIO_EXT(sc->hratio) |
EXYNOS_CIEXTEN_MAINVERRATIO_EXT(sc->vratio)); EXYNOS_CIEXTEN_MAINVERRATIO_EXT(sc->vratio));
fimc_write(cfg_ext, EXYNOS_CIEXTEN); fimc_write(ctx, cfg_ext, EXYNOS_CIEXTEN);
} }
static int fimc_dst_set_size(struct device *dev, int swap, static int fimc_dst_set_size(struct device *dev, int swap,
...@@ -1109,12 +1074,12 @@ static int fimc_dst_set_size(struct device *dev, int swap, ...@@ -1109,12 +1074,12 @@ static int fimc_dst_set_size(struct device *dev, int swap,
cfg = (EXYNOS_ORGOSIZE_HORIZONTAL(img_sz.hsize) | cfg = (EXYNOS_ORGOSIZE_HORIZONTAL(img_sz.hsize) |
EXYNOS_ORGOSIZE_VERTICAL(img_sz.vsize)); EXYNOS_ORGOSIZE_VERTICAL(img_sz.vsize));
fimc_write(cfg, EXYNOS_ORGOSIZE); fimc_write(ctx, cfg, EXYNOS_ORGOSIZE);
DRM_DEBUG_KMS("x[%d]y[%d]w[%d]h[%d]\n", pos->x, pos->y, pos->w, pos->h); DRM_DEBUG_KMS("x[%d]y[%d]w[%d]h[%d]\n", pos->x, pos->y, pos->w, pos->h);
/* CSC ITU */ /* CSC ITU */
cfg = fimc_read(EXYNOS_CIGCTRL); cfg = fimc_read(ctx, EXYNOS_CIGCTRL);
cfg &= ~EXYNOS_CIGCTRL_CSC_MASK; cfg &= ~EXYNOS_CIGCTRL_CSC_MASK;
if (sz->hsize >= FIMC_WIDTH_ITU_709) if (sz->hsize >= FIMC_WIDTH_ITU_709)
...@@ -1122,7 +1087,7 @@ static int fimc_dst_set_size(struct device *dev, int swap, ...@@ -1122,7 +1087,7 @@ static int fimc_dst_set_size(struct device *dev, int swap,
else else
cfg |= EXYNOS_CIGCTRL_CSC_ITU601; cfg |= EXYNOS_CIGCTRL_CSC_ITU601;
fimc_write(cfg, EXYNOS_CIGCTRL); fimc_write(ctx, cfg, EXYNOS_CIGCTRL);
if (swap) { if (swap) {
img_pos.w = pos->h; img_pos.w = pos->h;
...@@ -1132,41 +1097,38 @@ static int fimc_dst_set_size(struct device *dev, int swap, ...@@ -1132,41 +1097,38 @@ static int fimc_dst_set_size(struct device *dev, int swap,
} }
/* target image size */ /* target image size */
cfg = fimc_read(EXYNOS_CITRGFMT); cfg = fimc_read(ctx, EXYNOS_CITRGFMT);
cfg &= ~(EXYNOS_CITRGFMT_TARGETH_MASK | cfg &= ~(EXYNOS_CITRGFMT_TARGETH_MASK |
EXYNOS_CITRGFMT_TARGETV_MASK); EXYNOS_CITRGFMT_TARGETV_MASK);
cfg |= (EXYNOS_CITRGFMT_TARGETHSIZE(img_pos.w) | cfg |= (EXYNOS_CITRGFMT_TARGETHSIZE(img_pos.w) |
EXYNOS_CITRGFMT_TARGETVSIZE(img_pos.h)); EXYNOS_CITRGFMT_TARGETVSIZE(img_pos.h));
fimc_write(cfg, EXYNOS_CITRGFMT); fimc_write(ctx, cfg, EXYNOS_CITRGFMT);
/* target area */ /* target area */
cfg = EXYNOS_CITAREA_TARGET_AREA(img_pos.w * img_pos.h); cfg = EXYNOS_CITAREA_TARGET_AREA(img_pos.w * img_pos.h);
fimc_write(cfg, EXYNOS_CITAREA); fimc_write(ctx, cfg, EXYNOS_CITAREA);
/* offset Y(RGB), Cb, Cr */ /* offset Y(RGB), Cb, Cr */
cfg = (EXYNOS_CIOYOFF_HORIZONTAL(img_pos.x) | cfg = (EXYNOS_CIOYOFF_HORIZONTAL(img_pos.x) |
EXYNOS_CIOYOFF_VERTICAL(img_pos.y)); EXYNOS_CIOYOFF_VERTICAL(img_pos.y));
fimc_write(cfg, EXYNOS_CIOYOFF); fimc_write(ctx, cfg, EXYNOS_CIOYOFF);
cfg = (EXYNOS_CIOCBOFF_HORIZONTAL(img_pos.x) | cfg = (EXYNOS_CIOCBOFF_HORIZONTAL(img_pos.x) |
EXYNOS_CIOCBOFF_VERTICAL(img_pos.y)); EXYNOS_CIOCBOFF_VERTICAL(img_pos.y));
fimc_write(cfg, EXYNOS_CIOCBOFF); fimc_write(ctx, cfg, EXYNOS_CIOCBOFF);
cfg = (EXYNOS_CIOCROFF_HORIZONTAL(img_pos.x) | cfg = (EXYNOS_CIOCROFF_HORIZONTAL(img_pos.x) |
EXYNOS_CIOCROFF_VERTICAL(img_pos.y)); EXYNOS_CIOCROFF_VERTICAL(img_pos.y));
fimc_write(cfg, EXYNOS_CIOCROFF); fimc_write(ctx, cfg, EXYNOS_CIOCROFF);
return 0; return 0;
} }
static int fimc_dst_get_buf_seq(struct fimc_context *ctx) static int fimc_dst_get_buf_count(struct fimc_context *ctx)
{ {
u32 cfg, i, buf_num = 0; u32 cfg, buf_num;
u32 mask = 0x00000001;
cfg = fimc_read(EXYNOS_CIFCNTSEQ); cfg = fimc_read(ctx, EXYNOS_CIFCNTSEQ);
for (i = 0; i < FIMC_REG_SZ; i++) buf_num = hweight32(cfg);
if (cfg & (mask << i))
buf_num++;
DRM_DEBUG_KMS("buf_num[%d]\n", buf_num); DRM_DEBUG_KMS("buf_num[%d]\n", buf_num);
...@@ -1181,13 +1143,14 @@ static int fimc_dst_set_buf_seq(struct fimc_context *ctx, u32 buf_id, ...@@ -1181,13 +1143,14 @@ static int fimc_dst_set_buf_seq(struct fimc_context *ctx, u32 buf_id,
u32 cfg; u32 cfg;
u32 mask = 0x00000001 << buf_id; u32 mask = 0x00000001 << buf_id;
int ret = 0; int ret = 0;
unsigned long flags;
DRM_DEBUG_KMS("buf_id[%d]buf_type[%d]\n", buf_id, buf_type); DRM_DEBUG_KMS("buf_id[%d]buf_type[%d]\n", buf_id, buf_type);
mutex_lock(&ctx->lock); spin_lock_irqsave(&ctx->lock, flags);
/* mask register set */ /* mask register set */
cfg = fimc_read(EXYNOS_CIFCNTSEQ); cfg = fimc_read(ctx, EXYNOS_CIFCNTSEQ);
switch (buf_type) { switch (buf_type) {
case IPP_BUF_ENQUEUE: case IPP_BUF_ENQUEUE:
...@@ -1205,20 +1168,20 @@ static int fimc_dst_set_buf_seq(struct fimc_context *ctx, u32 buf_id, ...@@ -1205,20 +1168,20 @@ static int fimc_dst_set_buf_seq(struct fimc_context *ctx, u32 buf_id,
/* sequence id */ /* sequence id */
cfg &= ~mask; cfg &= ~mask;
cfg |= (enable << buf_id); cfg |= (enable << buf_id);
fimc_write(cfg, EXYNOS_CIFCNTSEQ); fimc_write(ctx, cfg, EXYNOS_CIFCNTSEQ);
/* interrupt enable */ /* interrupt enable */
if (buf_type == IPP_BUF_ENQUEUE && if (buf_type == IPP_BUF_ENQUEUE &&
fimc_dst_get_buf_seq(ctx) >= FIMC_BUF_START) fimc_dst_get_buf_count(ctx) >= FIMC_BUF_START)
fimc_handle_irq(ctx, true, false, true); fimc_mask_irq(ctx, true);
/* interrupt disable */ /* interrupt disable */
if (buf_type == IPP_BUF_DEQUEUE && if (buf_type == IPP_BUF_DEQUEUE &&
fimc_dst_get_buf_seq(ctx) <= FIMC_BUF_STOP) fimc_dst_get_buf_count(ctx) <= FIMC_BUF_STOP)
fimc_handle_irq(ctx, false, false, true); fimc_mask_irq(ctx, false);
err_unlock: err_unlock:
mutex_unlock(&ctx->lock); spin_unlock_irqrestore(&ctx->lock, flags);
return ret; return ret;
} }
...@@ -1252,25 +1215,25 @@ static int fimc_dst_set_addr(struct device *dev, ...@@ -1252,25 +1215,25 @@ static int fimc_dst_set_addr(struct device *dev,
case IPP_BUF_ENQUEUE: case IPP_BUF_ENQUEUE:
config = &property->config[EXYNOS_DRM_OPS_DST]; config = &property->config[EXYNOS_DRM_OPS_DST];
fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_Y], fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_Y],
EXYNOS_CIOYSA(buf_id)); EXYNOS_CIOYSA(buf_id));
if (config->fmt == DRM_FORMAT_YVU420) { if (config->fmt == DRM_FORMAT_YVU420) {
fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR], fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CR],
EXYNOS_CIOCBSA(buf_id)); EXYNOS_CIOCBSA(buf_id));
fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB], fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CB],
EXYNOS_CIOCRSA(buf_id)); EXYNOS_CIOCRSA(buf_id));
} else { } else {
fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB], fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CB],
EXYNOS_CIOCBSA(buf_id)); EXYNOS_CIOCBSA(buf_id));
fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR], fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CR],
EXYNOS_CIOCRSA(buf_id)); EXYNOS_CIOCRSA(buf_id));
} }
break; break;
case IPP_BUF_DEQUEUE: case IPP_BUF_DEQUEUE:
fimc_write(0x0, EXYNOS_CIOYSA(buf_id)); fimc_write(ctx, 0x0, EXYNOS_CIOYSA(buf_id));
fimc_write(0x0, EXYNOS_CIOCBSA(buf_id)); fimc_write(ctx, 0x0, EXYNOS_CIOCBSA(buf_id));
fimc_write(0x0, EXYNOS_CIOCRSA(buf_id)); fimc_write(ctx, 0x0, EXYNOS_CIOCRSA(buf_id));
break; break;
default: default:
/* bypass */ /* bypass */
...@@ -1342,11 +1305,7 @@ static irqreturn_t fimc_irq_handler(int irq, void *dev_id) ...@@ -1342,11 +1305,7 @@ static irqreturn_t fimc_irq_handler(int irq, void *dev_id)
static int fimc_init_prop_list(struct exynos_drm_ippdrv *ippdrv) static int fimc_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;
...@@ -1371,8 +1330,6 @@ static int fimc_init_prop_list(struct exynos_drm_ippdrv *ippdrv) ...@@ -1371,8 +1330,6 @@ static int fimc_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
prop_list->scale_min.hsize = FIMC_SCALE_MIN; prop_list->scale_min.hsize = FIMC_SCALE_MIN;
prop_list->scale_min.vsize = FIMC_SCALE_MIN; prop_list->scale_min.vsize = FIMC_SCALE_MIN;
ippdrv->prop_list = prop_list;
return 0; return 0;
} }
...@@ -1395,7 +1352,7 @@ static int fimc_ippdrv_check_property(struct device *dev, ...@@ -1395,7 +1352,7 @@ static int fimc_ippdrv_check_property(struct device *dev,
{ {
struct fimc_context *ctx = get_fimc_context(dev); struct fimc_context *ctx = get_fimc_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;
...@@ -1508,15 +1465,15 @@ static void fimc_clear_addr(struct fimc_context *ctx) ...@@ -1508,15 +1465,15 @@ static void fimc_clear_addr(struct fimc_context *ctx)
int i; int i;
for (i = 0; i < FIMC_MAX_SRC; i++) { for (i = 0; i < FIMC_MAX_SRC; i++) {
fimc_write(0, EXYNOS_CIIYSA(i)); fimc_write(ctx, 0, EXYNOS_CIIYSA(i));
fimc_write(0, EXYNOS_CIICBSA(i)); fimc_write(ctx, 0, EXYNOS_CIICBSA(i));
fimc_write(0, EXYNOS_CIICRSA(i)); fimc_write(ctx, 0, EXYNOS_CIICRSA(i));
} }
for (i = 0; i < FIMC_MAX_DST; i++) { for (i = 0; i < FIMC_MAX_DST; i++) {
fimc_write(0, EXYNOS_CIOYSA(i)); fimc_write(ctx, 0, EXYNOS_CIOYSA(i));
fimc_write(0, EXYNOS_CIOCBSA(i)); fimc_write(ctx, 0, EXYNOS_CIOCBSA(i));
fimc_write(0, EXYNOS_CIOCRSA(i)); fimc_write(ctx, 0, EXYNOS_CIOCRSA(i));
} }
} }
...@@ -1556,7 +1513,7 @@ static int fimc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd) ...@@ -1556,7 +1513,7 @@ static int fimc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd)
property = &c_node->property; property = &c_node->property;
fimc_handle_irq(ctx, true, false, true); fimc_mask_irq(ctx, true);
for_each_ipp_ops(i) { for_each_ipp_ops(i) {
config = &property->config[i]; config = &property->config[i];
...@@ -1582,10 +1539,10 @@ static int fimc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd) ...@@ -1582,10 +1539,10 @@ static int fimc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd)
fimc_handle_lastend(ctx, false); fimc_handle_lastend(ctx, false);
/* setup dma */ /* setup dma */
cfg0 = fimc_read(EXYNOS_MSCTRL); cfg0 = fimc_read(ctx, EXYNOS_MSCTRL);
cfg0 &= ~EXYNOS_MSCTRL_INPUT_MASK; cfg0 &= ~EXYNOS_MSCTRL_INPUT_MASK;
cfg0 |= EXYNOS_MSCTRL_INPUT_MEMORY; cfg0 |= EXYNOS_MSCTRL_INPUT_MEMORY;
fimc_write(cfg0, EXYNOS_MSCTRL); fimc_write(ctx, cfg0, EXYNOS_MSCTRL);
break; break;
case IPP_CMD_WB: case IPP_CMD_WB:
fimc_set_type_ctrl(ctx, FIMC_WB_A); fimc_set_type_ctrl(ctx, FIMC_WB_A);
...@@ -1610,41 +1567,33 @@ static int fimc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd) ...@@ -1610,41 +1567,33 @@ static int fimc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd)
} }
/* Reset status */ /* Reset status */
fimc_write(0x0, EXYNOS_CISTATUS); fimc_write(ctx, 0x0, EXYNOS_CISTATUS);
cfg0 = fimc_read(EXYNOS_CIIMGCPT); cfg0 = fimc_read(ctx, EXYNOS_CIIMGCPT);
cfg0 &= ~EXYNOS_CIIMGCPT_IMGCPTEN_SC; cfg0 &= ~EXYNOS_CIIMGCPT_IMGCPTEN_SC;
cfg0 |= EXYNOS_CIIMGCPT_IMGCPTEN_SC; cfg0 |= EXYNOS_CIIMGCPT_IMGCPTEN_SC;
/* Scaler */ /* Scaler */
cfg1 = fimc_read(EXYNOS_CISCCTRL); cfg1 = fimc_read(ctx, EXYNOS_CISCCTRL);
cfg1 &= ~EXYNOS_CISCCTRL_SCAN_MASK; cfg1 &= ~EXYNOS_CISCCTRL_SCAN_MASK;
cfg1 |= (EXYNOS_CISCCTRL_PROGRESSIVE | cfg1 |= (EXYNOS_CISCCTRL_PROGRESSIVE |
EXYNOS_CISCCTRL_SCALERSTART); EXYNOS_CISCCTRL_SCALERSTART);
fimc_write(cfg1, EXYNOS_CISCCTRL); fimc_write(ctx, cfg1, EXYNOS_CISCCTRL);
/* Enable image capture*/ /* Enable image capture*/
cfg0 |= EXYNOS_CIIMGCPT_IMGCPTEN; cfg0 |= EXYNOS_CIIMGCPT_IMGCPTEN;
fimc_write(cfg0, EXYNOS_CIIMGCPT); fimc_write(ctx, cfg0, EXYNOS_CIIMGCPT);
/* Disable frame end irq */ /* Disable frame end irq */
cfg0 = fimc_read(EXYNOS_CIGCTRL); fimc_clear_bits(ctx, EXYNOS_CIGCTRL, EXYNOS_CIGCTRL_IRQ_END_DISABLE);
cfg0 &= ~EXYNOS_CIGCTRL_IRQ_END_DISABLE;
fimc_write(cfg0, EXYNOS_CIGCTRL);
cfg0 = fimc_read(EXYNOS_CIOCTRL); fimc_clear_bits(ctx, EXYNOS_CIOCTRL, EXYNOS_CIOCTRL_WEAVE_MASK);
cfg0 &= ~EXYNOS_CIOCTRL_WEAVE_MASK;
fimc_write(cfg0, EXYNOS_CIOCTRL);
if (cmd == IPP_CMD_M2M) { if (cmd == IPP_CMD_M2M) {
cfg0 = fimc_read(EXYNOS_MSCTRL); fimc_set_bits(ctx, EXYNOS_MSCTRL, EXYNOS_MSCTRL_ENVID);
cfg0 |= EXYNOS_MSCTRL_ENVID;
fimc_write(cfg0, EXYNOS_MSCTRL);
cfg0 = fimc_read(EXYNOS_MSCTRL); fimc_set_bits(ctx, EXYNOS_MSCTRL, EXYNOS_MSCTRL_ENVID);
cfg0 |= EXYNOS_MSCTRL_ENVID;
fimc_write(cfg0, EXYNOS_MSCTRL);
} }
return 0; return 0;
...@@ -1661,10 +1610,10 @@ static void fimc_ippdrv_stop(struct device *dev, enum drm_exynos_ipp_cmd cmd) ...@@ -1661,10 +1610,10 @@ static void fimc_ippdrv_stop(struct device *dev, enum drm_exynos_ipp_cmd cmd)
switch (cmd) { switch (cmd) {
case IPP_CMD_M2M: case IPP_CMD_M2M:
/* Source clear */ /* Source clear */
cfg = fimc_read(EXYNOS_MSCTRL); cfg = fimc_read(ctx, EXYNOS_MSCTRL);
cfg &= ~EXYNOS_MSCTRL_INPUT_MASK; cfg &= ~EXYNOS_MSCTRL_INPUT_MASK;
cfg &= ~EXYNOS_MSCTRL_ENVID; cfg &= ~EXYNOS_MSCTRL_ENVID;
fimc_write(cfg, EXYNOS_MSCTRL); fimc_write(ctx, cfg, EXYNOS_MSCTRL);
break; break;
case IPP_CMD_WB: case IPP_CMD_WB:
exynos_drm_ippnb_send_event(IPP_SET_WRITEBACK, (void *)&set_wb); exynos_drm_ippnb_send_event(IPP_SET_WRITEBACK, (void *)&set_wb);
...@@ -1675,25 +1624,20 @@ static void fimc_ippdrv_stop(struct device *dev, enum drm_exynos_ipp_cmd cmd) ...@@ -1675,25 +1624,20 @@ static void fimc_ippdrv_stop(struct device *dev, enum drm_exynos_ipp_cmd cmd)
break; break;
} }
fimc_handle_irq(ctx, false, false, true); fimc_mask_irq(ctx, false);
/* reset sequence */ /* reset sequence */
fimc_write(0x0, EXYNOS_CIFCNTSEQ); fimc_write(ctx, 0x0, EXYNOS_CIFCNTSEQ);
/* Scaler disable */ /* Scaler disable */
cfg = fimc_read(EXYNOS_CISCCTRL); fimc_clear_bits(ctx, EXYNOS_CISCCTRL, EXYNOS_CISCCTRL_SCALERSTART);
cfg &= ~EXYNOS_CISCCTRL_SCALERSTART;
fimc_write(cfg, EXYNOS_CISCCTRL);
/* Disable image capture */ /* Disable image capture */
cfg = fimc_read(EXYNOS_CIIMGCPT); fimc_clear_bits(ctx, EXYNOS_CIIMGCPT,
cfg &= ~(EXYNOS_CIIMGCPT_IMGCPTEN_SC | EXYNOS_CIIMGCPT_IMGCPTEN); EXYNOS_CIIMGCPT_IMGCPTEN_SC | EXYNOS_CIIMGCPT_IMGCPTEN);
fimc_write(cfg, EXYNOS_CIIMGCPT);
/* Enable frame end irq */ /* Enable frame end irq */
cfg = fimc_read(EXYNOS_CIGCTRL); fimc_set_bits(ctx, EXYNOS_CIGCTRL, EXYNOS_CIGCTRL_IRQ_END_DISABLE);
cfg |= EXYNOS_CIGCTRL_IRQ_END_DISABLE;
fimc_write(cfg, EXYNOS_CIGCTRL);
} }
static void fimc_put_clocks(struct fimc_context *ctx) static void fimc_put_clocks(struct fimc_context *ctx)
...@@ -1848,7 +1792,7 @@ static int fimc_probe(struct platform_device *pdev) ...@@ -1848,7 +1792,7 @@ static int fimc_probe(struct platform_device *pdev)
DRM_DEBUG_KMS("id[%d]ippdrv[0x%x]\n", ctx->id, (int)ippdrv); DRM_DEBUG_KMS("id[%d]ippdrv[0x%x]\n", ctx->id, (int)ippdrv);
mutex_init(&ctx->lock); spin_lock_init(&ctx->lock);
platform_set_drvdata(pdev, ctx); platform_set_drvdata(pdev, ctx);
pm_runtime_set_active(dev); pm_runtime_set_active(dev);
...@@ -1879,7 +1823,6 @@ static int fimc_remove(struct platform_device *pdev) ...@@ -1879,7 +1823,6 @@ static int fimc_remove(struct platform_device *pdev)
struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
exynos_drm_ippdrv_unregister(ippdrv); exynos_drm_ippdrv_unregister(ippdrv);
mutex_destroy(&ctx->lock);
fimc_put_clocks(ctx); fimc_put_clocks(ctx);
pm_runtime_set_suspended(dev); pm_runtime_set_suspended(dev);
......
...@@ -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); exynos_gem_obj = exynos_drm_gem_create(dev,
/* EXYNOS_BO_NONCONTIG | EXYNOS_BO_WC,
* If physically contiguous memory allocation fails and if IOMMU is args->size);
* supported then try to get buffer from non physically contiguous } else {
* 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_CONTIG | EXYNOS_BO_WC,
args->size); 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;
......
...@@ -167,6 +167,13 @@ static int ipp_create_id(struct idr *id_idr, struct mutex *lock, void *obj, ...@@ -167,6 +167,13 @@ static int ipp_create_id(struct idr *id_idr, struct mutex *lock, void *obj,
return 0; return 0;
} }
static void ipp_remove_id(struct idr *id_idr, struct mutex *lock, u32 id)
{
mutex_lock(lock);
idr_remove(id_idr, id);
mutex_unlock(lock);
}
static void *ipp_find_obj(struct idr *id_idr, struct mutex *lock, u32 id) static void *ipp_find_obj(struct idr *id_idr, struct mutex *lock, u32 id)
{ {
void *obj; void *obj;
...@@ -276,11 +283,6 @@ static struct exynos_drm_ippdrv *ipp_find_drv_by_handle(u32 prop_id) ...@@ -276,11 +283,6 @@ static struct exynos_drm_ippdrv *ipp_find_drv_by_handle(u32 prop_id)
DRM_DEBUG_KMS("prop_id[%d]\n", prop_id); DRM_DEBUG_KMS("prop_id[%d]\n", prop_id);
if (list_empty(&exynos_drm_ippdrv_list)) {
DRM_DEBUG_KMS("ippdrv_list is empty.\n");
return ERR_PTR(-ENODEV);
}
/* /*
* This case is search ipp driver by prop_id handle. * This case is search ipp driver by prop_id handle.
* sometimes, ipp subsystem find driver by prop_id. * sometimes, ipp subsystem find driver by prop_id.
...@@ -289,11 +291,14 @@ static struct exynos_drm_ippdrv *ipp_find_drv_by_handle(u32 prop_id) ...@@ -289,11 +291,14 @@ static struct exynos_drm_ippdrv *ipp_find_drv_by_handle(u32 prop_id)
list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) { list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) {
DRM_DEBUG_KMS("count[%d]ippdrv[0x%x]\n", count++, (int)ippdrv); DRM_DEBUG_KMS("count[%d]ippdrv[0x%x]\n", count++, (int)ippdrv);
if (!list_empty(&ippdrv->cmd_list)) { mutex_lock(&ippdrv->cmd_lock);
list_for_each_entry(c_node, &ippdrv->cmd_list, list) list_for_each_entry(c_node, &ippdrv->cmd_list, list) {
if (c_node->property.prop_id == prop_id) if (c_node->property.prop_id == prop_id) {
return ippdrv; mutex_unlock(&ippdrv->cmd_lock);
return ippdrv;
}
} }
mutex_unlock(&ippdrv->cmd_lock);
} }
return ERR_PTR(-ENODEV); return ERR_PTR(-ENODEV);
...@@ -325,6 +330,7 @@ int exynos_drm_ipp_get_property(struct drm_device *drm_dev, void *data, ...@@ -325,6 +330,7 @@ int exynos_drm_ipp_get_property(struct drm_device *drm_dev, void *data,
if (!prop_list->ipp_id) { if (!prop_list->ipp_id) {
list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list)
count++; count++;
/* /*
* Supports ippdrv list count for user application. * Supports ippdrv list count for user application.
* First step user application getting ippdrv count. * First step user application getting ippdrv count.
...@@ -346,7 +352,7 @@ int exynos_drm_ipp_get_property(struct drm_device *drm_dev, void *data, ...@@ -346,7 +352,7 @@ int exynos_drm_ipp_get_property(struct drm_device *drm_dev, void *data,
return PTR_ERR(ippdrv); return PTR_ERR(ippdrv);
} }
prop_list = ippdrv->prop_list; *prop_list = ippdrv->prop_list;
} }
return 0; return 0;
...@@ -386,9 +392,11 @@ static int ipp_find_and_set_property(struct drm_exynos_ipp_property *property) ...@@ -386,9 +392,11 @@ static int ipp_find_and_set_property(struct drm_exynos_ipp_property *property)
* when we find this command no using prop_id. * when we find this command no using prop_id.
* return property information set in this command node. * return property information set in this command node.
*/ */
mutex_lock(&ippdrv->cmd_lock);
list_for_each_entry(c_node, &ippdrv->cmd_list, list) { list_for_each_entry(c_node, &ippdrv->cmd_list, list) {
if ((c_node->property.prop_id == prop_id) && if ((c_node->property.prop_id == prop_id) &&
(c_node->state == IPP_STATE_STOP)) { (c_node->state == IPP_STATE_STOP)) {
mutex_unlock(&ippdrv->cmd_lock);
DRM_DEBUG_KMS("found cmd[%d]ippdrv[0x%x]\n", DRM_DEBUG_KMS("found cmd[%d]ippdrv[0x%x]\n",
property->cmd, (int)ippdrv); property->cmd, (int)ippdrv);
...@@ -396,6 +404,7 @@ static int ipp_find_and_set_property(struct drm_exynos_ipp_property *property) ...@@ -396,6 +404,7 @@ static int ipp_find_and_set_property(struct drm_exynos_ipp_property *property)
return 0; return 0;
} }
} }
mutex_unlock(&ippdrv->cmd_lock);
DRM_ERROR("failed to search property.\n"); DRM_ERROR("failed to search property.\n");
...@@ -499,7 +508,7 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data, ...@@ -499,7 +508,7 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data,
c_node->start_work = ipp_create_cmd_work(); c_node->start_work = ipp_create_cmd_work();
if (IS_ERR(c_node->start_work)) { if (IS_ERR(c_node->start_work)) {
DRM_ERROR("failed to create start work.\n"); DRM_ERROR("failed to create start work.\n");
goto err_clear; goto err_remove_id;
} }
c_node->stop_work = ipp_create_cmd_work(); c_node->stop_work = ipp_create_cmd_work();
...@@ -514,7 +523,7 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data, ...@@ -514,7 +523,7 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data,
goto err_free_stop; goto err_free_stop;
} }
mutex_init(&c_node->cmd_lock); mutex_init(&c_node->lock);
mutex_init(&c_node->mem_lock); mutex_init(&c_node->mem_lock);
mutex_init(&c_node->event_lock); mutex_init(&c_node->event_lock);
...@@ -526,7 +535,9 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data, ...@@ -526,7 +535,9 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data,
INIT_LIST_HEAD(&c_node->event_list); INIT_LIST_HEAD(&c_node->event_list);
list_splice_init(&priv->event_list, &c_node->event_list); list_splice_init(&priv->event_list, &c_node->event_list);
mutex_lock(&ippdrv->cmd_lock);
list_add_tail(&c_node->list, &ippdrv->cmd_list); list_add_tail(&c_node->list, &ippdrv->cmd_list);
mutex_unlock(&ippdrv->cmd_lock);
/* make dedicated state without m2m */ /* make dedicated state without m2m */
if (!ipp_is_m2m_cmd(property->cmd)) if (!ipp_is_m2m_cmd(property->cmd))
...@@ -538,18 +549,24 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data, ...@@ -538,18 +549,24 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data,
kfree(c_node->stop_work); kfree(c_node->stop_work);
err_free_start: err_free_start:
kfree(c_node->start_work); kfree(c_node->start_work);
err_remove_id:
ipp_remove_id(&ctx->prop_idr, &ctx->prop_lock, property->prop_id);
err_clear: err_clear:
kfree(c_node); kfree(c_node);
return ret; return ret;
} }
static void ipp_clean_cmd_node(struct drm_exynos_ipp_cmd_node *c_node) static void ipp_clean_cmd_node(struct ipp_context *ctx,
struct drm_exynos_ipp_cmd_node *c_node)
{ {
/* delete list */ /* delete list */
list_del(&c_node->list); list_del(&c_node->list);
ipp_remove_id(&ctx->prop_idr, &ctx->prop_lock,
c_node->property.prop_id);
/* destroy mutex */ /* destroy mutex */
mutex_destroy(&c_node->cmd_lock); mutex_destroy(&c_node->lock);
mutex_destroy(&c_node->mem_lock); mutex_destroy(&c_node->mem_lock);
mutex_destroy(&c_node->event_lock); mutex_destroy(&c_node->event_lock);
...@@ -567,17 +584,10 @@ static int ipp_check_mem_list(struct drm_exynos_ipp_cmd_node *c_node) ...@@ -567,17 +584,10 @@ static int ipp_check_mem_list(struct drm_exynos_ipp_cmd_node *c_node)
struct list_head *head; struct list_head *head;
int ret, i, count[EXYNOS_DRM_OPS_MAX] = { 0, }; int ret, i, count[EXYNOS_DRM_OPS_MAX] = { 0, };
mutex_lock(&c_node->mem_lock);
for_each_ipp_ops(i) { for_each_ipp_ops(i) {
/* source/destination memory list */ /* source/destination memory list */
head = &c_node->mem_list[i]; head = &c_node->mem_list[i];
if (list_empty(head)) {
DRM_DEBUG_KMS("%s memory empty.\n", i ? "dst" : "src");
continue;
}
/* find memory node entry */ /* find memory node entry */
list_for_each_entry(m_node, head, list) { list_for_each_entry(m_node, head, list) {
DRM_DEBUG_KMS("%s,count[%d]m_node[0x%x]\n", DRM_DEBUG_KMS("%s,count[%d]m_node[0x%x]\n",
...@@ -602,8 +612,6 @@ static int ipp_check_mem_list(struct drm_exynos_ipp_cmd_node *c_node) ...@@ -602,8 +612,6 @@ static int ipp_check_mem_list(struct drm_exynos_ipp_cmd_node *c_node)
ret = max(count[EXYNOS_DRM_OPS_SRC], ret = max(count[EXYNOS_DRM_OPS_SRC],
count[EXYNOS_DRM_OPS_DST]); count[EXYNOS_DRM_OPS_DST]);
mutex_unlock(&c_node->mem_lock);
return ret; return ret;
} }
...@@ -646,16 +654,13 @@ static int ipp_set_mem_node(struct exynos_drm_ippdrv *ippdrv, ...@@ -646,16 +654,13 @@ static int ipp_set_mem_node(struct exynos_drm_ippdrv *ippdrv,
return -EFAULT; return -EFAULT;
} }
mutex_lock(&c_node->mem_lock);
DRM_DEBUG_KMS("ops_id[%d]\n", m_node->ops_id); DRM_DEBUG_KMS("ops_id[%d]\n", m_node->ops_id);
/* get operations callback */ /* get operations callback */
ops = ippdrv->ops[m_node->ops_id]; ops = ippdrv->ops[m_node->ops_id];
if (!ops) { if (!ops) {
DRM_ERROR("not support ops.\n"); DRM_ERROR("not support ops.\n");
ret = -EFAULT; return -EFAULT;
goto err_unlock;
} }
/* set address and enable irq */ /* set address and enable irq */
...@@ -664,12 +669,10 @@ static int ipp_set_mem_node(struct exynos_drm_ippdrv *ippdrv, ...@@ -664,12 +669,10 @@ static int ipp_set_mem_node(struct exynos_drm_ippdrv *ippdrv,
m_node->buf_id, IPP_BUF_ENQUEUE); m_node->buf_id, IPP_BUF_ENQUEUE);
if (ret) { if (ret) {
DRM_ERROR("failed to set addr.\n"); DRM_ERROR("failed to set addr.\n");
goto err_unlock; return ret;
} }
} }
err_unlock:
mutex_unlock(&c_node->mem_lock);
return ret; return ret;
} }
...@@ -684,11 +687,9 @@ static struct drm_exynos_ipp_mem_node ...@@ -684,11 +687,9 @@ static struct drm_exynos_ipp_mem_node
void *addr; void *addr;
int i; int i;
mutex_lock(&c_node->mem_lock);
m_node = kzalloc(sizeof(*m_node), GFP_KERNEL); m_node = kzalloc(sizeof(*m_node), GFP_KERNEL);
if (!m_node) if (!m_node)
goto err_unlock; return ERR_PTR(-ENOMEM);
/* clear base address for error handling */ /* clear base address for error handling */
memset(&buf_info, 0x0, sizeof(buf_info)); memset(&buf_info, 0x0, sizeof(buf_info));
...@@ -722,15 +723,14 @@ static struct drm_exynos_ipp_mem_node ...@@ -722,15 +723,14 @@ static struct drm_exynos_ipp_mem_node
m_node->filp = file; m_node->filp = file;
m_node->buf_info = buf_info; m_node->buf_info = buf_info;
mutex_lock(&c_node->mem_lock);
list_add_tail(&m_node->list, &c_node->mem_list[qbuf->ops_id]); list_add_tail(&m_node->list, &c_node->mem_list[qbuf->ops_id]);
mutex_unlock(&c_node->mem_lock); mutex_unlock(&c_node->mem_lock);
return m_node; return m_node;
err_clear: err_clear:
kfree(m_node); kfree(m_node);
err_unlock:
mutex_unlock(&c_node->mem_lock);
return ERR_PTR(-EFAULT); return ERR_PTR(-EFAULT);
} }
...@@ -747,13 +747,6 @@ static int ipp_put_mem_node(struct drm_device *drm_dev, ...@@ -747,13 +747,6 @@ static int ipp_put_mem_node(struct drm_device *drm_dev,
return -EFAULT; return -EFAULT;
} }
if (list_empty(&m_node->list)) {
DRM_ERROR("empty memory node.\n");
return -ENOMEM;
}
mutex_lock(&c_node->mem_lock);
DRM_DEBUG_KMS("ops_id[%d]\n", m_node->ops_id); DRM_DEBUG_KMS("ops_id[%d]\n", m_node->ops_id);
/* put gem buffer */ /* put gem buffer */
...@@ -768,8 +761,6 @@ static int ipp_put_mem_node(struct drm_device *drm_dev, ...@@ -768,8 +761,6 @@ static int ipp_put_mem_node(struct drm_device *drm_dev,
list_del(&m_node->list); list_del(&m_node->list);
kfree(m_node); kfree(m_node);
mutex_unlock(&c_node->mem_lock);
return 0; return 0;
} }
...@@ -805,7 +796,9 @@ static int ipp_get_event(struct drm_device *drm_dev, ...@@ -805,7 +796,9 @@ static int ipp_get_event(struct drm_device *drm_dev,
e->base.event = &e->event.base; e->base.event = &e->event.base;
e->base.file_priv = file; e->base.file_priv = file;
e->base.destroy = ipp_free_event; e->base.destroy = ipp_free_event;
mutex_lock(&c_node->event_lock);
list_add_tail(&e->base.link, &c_node->event_list); list_add_tail(&e->base.link, &c_node->event_list);
mutex_unlock(&c_node->event_lock);
return 0; return 0;
} }
...@@ -816,11 +809,7 @@ static void ipp_put_event(struct drm_exynos_ipp_cmd_node *c_node, ...@@ -816,11 +809,7 @@ static void ipp_put_event(struct drm_exynos_ipp_cmd_node *c_node,
struct drm_exynos_ipp_send_event *e, *te; struct drm_exynos_ipp_send_event *e, *te;
int count = 0; int count = 0;
if (list_empty(&c_node->event_list)) { mutex_lock(&c_node->event_lock);
DRM_DEBUG_KMS("event_list is empty.\n");
return;
}
list_for_each_entry_safe(e, te, &c_node->event_list, base.link) { list_for_each_entry_safe(e, te, &c_node->event_list, base.link) {
DRM_DEBUG_KMS("count[%d]e[0x%x]\n", count++, (int)e); DRM_DEBUG_KMS("count[%d]e[0x%x]\n", count++, (int)e);
...@@ -841,9 +830,13 @@ static void ipp_put_event(struct drm_exynos_ipp_cmd_node *c_node, ...@@ -841,9 +830,13 @@ static void ipp_put_event(struct drm_exynos_ipp_cmd_node *c_node,
/* delete list */ /* delete list */
list_del(&e->base.link); list_del(&e->base.link);
kfree(e); kfree(e);
return; goto out_unlock;
} }
} }
out_unlock:
mutex_unlock(&c_node->event_lock);
return;
} }
static void ipp_handle_cmd_work(struct device *dev, static void ipp_handle_cmd_work(struct device *dev,
...@@ -887,7 +880,9 @@ static int ipp_queue_buf_with_run(struct device *dev, ...@@ -887,7 +880,9 @@ static int ipp_queue_buf_with_run(struct device *dev,
return 0; return 0;
} }
mutex_lock(&c_node->mem_lock);
if (!ipp_check_mem_list(c_node)) { if (!ipp_check_mem_list(c_node)) {
mutex_unlock(&c_node->mem_lock);
DRM_DEBUG_KMS("empty memory.\n"); DRM_DEBUG_KMS("empty memory.\n");
return 0; return 0;
} }
...@@ -904,10 +899,12 @@ static int ipp_queue_buf_with_run(struct device *dev, ...@@ -904,10 +899,12 @@ static int ipp_queue_buf_with_run(struct device *dev,
} else { } else {
ret = ipp_set_mem_node(ippdrv, c_node, m_node); ret = ipp_set_mem_node(ippdrv, c_node, m_node);
if (ret) { if (ret) {
mutex_unlock(&c_node->mem_lock);
DRM_ERROR("failed to set m node.\n"); DRM_ERROR("failed to set m node.\n");
return ret; return ret;
} }
} }
mutex_unlock(&c_node->mem_lock);
return 0; return 0;
} }
...@@ -918,15 +915,15 @@ static void ipp_clean_queue_buf(struct drm_device *drm_dev, ...@@ -918,15 +915,15 @@ static void ipp_clean_queue_buf(struct drm_device *drm_dev,
{ {
struct drm_exynos_ipp_mem_node *m_node, *tm_node; struct drm_exynos_ipp_mem_node *m_node, *tm_node;
if (!list_empty(&c_node->mem_list[qbuf->ops_id])) { /* delete list */
/* delete list */ mutex_lock(&c_node->mem_lock);
list_for_each_entry_safe(m_node, tm_node, list_for_each_entry_safe(m_node, tm_node,
&c_node->mem_list[qbuf->ops_id], list) { &c_node->mem_list[qbuf->ops_id], list) {
if (m_node->buf_id == qbuf->buf_id && if (m_node->buf_id == qbuf->buf_id &&
m_node->ops_id == qbuf->ops_id) m_node->ops_id == qbuf->ops_id)
ipp_put_mem_node(drm_dev, c_node, m_node); ipp_put_mem_node(drm_dev, c_node, m_node);
}
} }
mutex_unlock(&c_node->mem_lock);
} }
int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data, int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data,
...@@ -998,7 +995,7 @@ int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data, ...@@ -998,7 +995,7 @@ int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data,
} }
break; break;
case IPP_BUF_DEQUEUE: case IPP_BUF_DEQUEUE:
mutex_lock(&c_node->cmd_lock); mutex_lock(&c_node->lock);
/* put event for destination buffer */ /* put event for destination buffer */
if (qbuf->ops_id == EXYNOS_DRM_OPS_DST) if (qbuf->ops_id == EXYNOS_DRM_OPS_DST)
...@@ -1006,7 +1003,7 @@ int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data, ...@@ -1006,7 +1003,7 @@ int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data,
ipp_clean_queue_buf(drm_dev, c_node, qbuf); ipp_clean_queue_buf(drm_dev, c_node, qbuf);
mutex_unlock(&c_node->cmd_lock); mutex_unlock(&c_node->lock);
break; break;
default: default:
DRM_ERROR("invalid buffer control.\n"); DRM_ERROR("invalid buffer control.\n");
...@@ -1109,12 +1106,12 @@ int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, void *data, ...@@ -1109,12 +1106,12 @@ int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, void *data,
case IPP_CTRL_PLAY: case IPP_CTRL_PLAY:
if (pm_runtime_suspended(ippdrv->dev)) if (pm_runtime_suspended(ippdrv->dev))
pm_runtime_get_sync(ippdrv->dev); pm_runtime_get_sync(ippdrv->dev);
c_node->state = IPP_STATE_START; c_node->state = IPP_STATE_START;
cmd_work = c_node->start_work; cmd_work = c_node->start_work;
cmd_work->ctrl = cmd_ctrl->ctrl; cmd_work->ctrl = cmd_ctrl->ctrl;
ipp_handle_cmd_work(dev, ippdrv, cmd_work, c_node); ipp_handle_cmd_work(dev, ippdrv, cmd_work, c_node);
c_node->state = IPP_STATE_START;
break; break;
case IPP_CTRL_STOP: case IPP_CTRL_STOP:
cmd_work = c_node->stop_work; cmd_work = c_node->stop_work;
...@@ -1129,10 +1126,12 @@ int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, void *data, ...@@ -1129,10 +1126,12 @@ int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, void *data,
c_node->state = IPP_STATE_STOP; c_node->state = IPP_STATE_STOP;
ippdrv->dedicated = false; ippdrv->dedicated = false;
ipp_clean_cmd_node(c_node); mutex_lock(&ippdrv->cmd_lock);
ipp_clean_cmd_node(ctx, c_node);
if (list_empty(&ippdrv->cmd_list)) if (list_empty(&ippdrv->cmd_list))
pm_runtime_put_sync(ippdrv->dev); pm_runtime_put_sync(ippdrv->dev);
mutex_unlock(&ippdrv->cmd_lock);
break; break;
case IPP_CTRL_PAUSE: case IPP_CTRL_PAUSE:
cmd_work = c_node->stop_work; cmd_work = c_node->stop_work;
...@@ -1260,9 +1259,11 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv, ...@@ -1260,9 +1259,11 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv,
/* store command info in ippdrv */ /* store command info in ippdrv */
ippdrv->c_node = c_node; ippdrv->c_node = c_node;
mutex_lock(&c_node->mem_lock);
if (!ipp_check_mem_list(c_node)) { if (!ipp_check_mem_list(c_node)) {
DRM_DEBUG_KMS("empty memory.\n"); DRM_DEBUG_KMS("empty memory.\n");
return -ENOMEM; ret = -ENOMEM;
goto err_unlock;
} }
/* set current property in ippdrv */ /* set current property in ippdrv */
...@@ -1270,7 +1271,7 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv, ...@@ -1270,7 +1271,7 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv,
if (ret) { if (ret) {
DRM_ERROR("failed to set property.\n"); DRM_ERROR("failed to set property.\n");
ippdrv->c_node = NULL; ippdrv->c_node = NULL;
return ret; goto err_unlock;
} }
/* check command */ /* check command */
...@@ -1285,7 +1286,7 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv, ...@@ -1285,7 +1286,7 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv,
if (!m_node) { if (!m_node) {
DRM_ERROR("failed to get node.\n"); DRM_ERROR("failed to get node.\n");
ret = -EFAULT; ret = -EFAULT;
return ret; goto err_unlock;
} }
DRM_DEBUG_KMS("m_node[0x%x]\n", (int)m_node); DRM_DEBUG_KMS("m_node[0x%x]\n", (int)m_node);
...@@ -1293,7 +1294,7 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv, ...@@ -1293,7 +1294,7 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv,
ret = ipp_set_mem_node(ippdrv, c_node, m_node); ret = ipp_set_mem_node(ippdrv, c_node, m_node);
if (ret) { if (ret) {
DRM_ERROR("failed to set m node.\n"); DRM_ERROR("failed to set m node.\n");
return ret; goto err_unlock;
} }
} }
break; break;
...@@ -1305,7 +1306,7 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv, ...@@ -1305,7 +1306,7 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv,
ret = ipp_set_mem_node(ippdrv, c_node, m_node); ret = ipp_set_mem_node(ippdrv, c_node, m_node);
if (ret) { if (ret) {
DRM_ERROR("failed to set m node.\n"); DRM_ERROR("failed to set m node.\n");
return ret; goto err_unlock;
} }
} }
break; break;
...@@ -1317,14 +1318,16 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv, ...@@ -1317,14 +1318,16 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv,
ret = ipp_set_mem_node(ippdrv, c_node, m_node); ret = ipp_set_mem_node(ippdrv, c_node, m_node);
if (ret) { if (ret) {
DRM_ERROR("failed to set m node.\n"); DRM_ERROR("failed to set m node.\n");
return ret; goto err_unlock;
} }
} }
break; break;
default: default:
DRM_ERROR("invalid operations.\n"); DRM_ERROR("invalid operations.\n");
return -EINVAL; ret = -EINVAL;
goto err_unlock;
} }
mutex_unlock(&c_node->mem_lock);
DRM_DEBUG_KMS("cmd[%d]\n", property->cmd); DRM_DEBUG_KMS("cmd[%d]\n", property->cmd);
...@@ -1333,11 +1336,17 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv, ...@@ -1333,11 +1336,17 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv,
ret = ippdrv->start(ippdrv->dev, property->cmd); ret = ippdrv->start(ippdrv->dev, property->cmd);
if (ret) { if (ret) {
DRM_ERROR("failed to start ops.\n"); DRM_ERROR("failed to start ops.\n");
ippdrv->c_node = NULL;
return ret; return ret;
} }
} }
return 0; return 0;
err_unlock:
mutex_unlock(&c_node->mem_lock);
ippdrv->c_node = NULL;
return ret;
} }
static int ipp_stop_property(struct drm_device *drm_dev, static int ipp_stop_property(struct drm_device *drm_dev,
...@@ -1354,6 +1363,8 @@ static int ipp_stop_property(struct drm_device *drm_dev, ...@@ -1354,6 +1363,8 @@ static int ipp_stop_property(struct drm_device *drm_dev,
/* put event */ /* put event */
ipp_put_event(c_node, NULL); ipp_put_event(c_node, NULL);
mutex_lock(&c_node->mem_lock);
/* check command */ /* check command */
switch (property->cmd) { switch (property->cmd) {
case IPP_CMD_M2M: case IPP_CMD_M2M:
...@@ -1361,11 +1372,6 @@ static int ipp_stop_property(struct drm_device *drm_dev, ...@@ -1361,11 +1372,6 @@ static int ipp_stop_property(struct drm_device *drm_dev,
/* source/destination memory list */ /* source/destination memory list */
head = &c_node->mem_list[i]; head = &c_node->mem_list[i];
if (list_empty(head)) {
DRM_DEBUG_KMS("mem_list is empty.\n");
break;
}
list_for_each_entry_safe(m_node, tm_node, list_for_each_entry_safe(m_node, tm_node,
head, list) { head, list) {
ret = ipp_put_mem_node(drm_dev, c_node, ret = ipp_put_mem_node(drm_dev, c_node,
...@@ -1381,11 +1387,6 @@ static int ipp_stop_property(struct drm_device *drm_dev, ...@@ -1381,11 +1387,6 @@ static int ipp_stop_property(struct drm_device *drm_dev,
/* destination memory list */ /* destination memory list */
head = &c_node->mem_list[EXYNOS_DRM_OPS_DST]; head = &c_node->mem_list[EXYNOS_DRM_OPS_DST];
if (list_empty(head)) {
DRM_DEBUG_KMS("mem_list is empty.\n");
break;
}
list_for_each_entry_safe(m_node, tm_node, head, list) { list_for_each_entry_safe(m_node, tm_node, head, list) {
ret = ipp_put_mem_node(drm_dev, c_node, m_node); ret = ipp_put_mem_node(drm_dev, c_node, m_node);
if (ret) { if (ret) {
...@@ -1398,11 +1399,6 @@ static int ipp_stop_property(struct drm_device *drm_dev, ...@@ -1398,11 +1399,6 @@ static int ipp_stop_property(struct drm_device *drm_dev,
/* source memory list */ /* source memory list */
head = &c_node->mem_list[EXYNOS_DRM_OPS_SRC]; head = &c_node->mem_list[EXYNOS_DRM_OPS_SRC];
if (list_empty(head)) {
DRM_DEBUG_KMS("mem_list is empty.\n");
break;
}
list_for_each_entry_safe(m_node, tm_node, head, list) { list_for_each_entry_safe(m_node, tm_node, head, list) {
ret = ipp_put_mem_node(drm_dev, c_node, m_node); ret = ipp_put_mem_node(drm_dev, c_node, m_node);
if (ret) { if (ret) {
...@@ -1418,6 +1414,8 @@ static int ipp_stop_property(struct drm_device *drm_dev, ...@@ -1418,6 +1414,8 @@ static int ipp_stop_property(struct drm_device *drm_dev,
} }
err_clear: err_clear:
mutex_unlock(&c_node->mem_lock);
/* stop operations */ /* stop operations */
if (ippdrv->stop) if (ippdrv->stop)
ippdrv->stop(ippdrv->dev, property->cmd); ippdrv->stop(ippdrv->dev, property->cmd);
...@@ -1446,7 +1444,7 @@ void ipp_sched_cmd(struct work_struct *work) ...@@ -1446,7 +1444,7 @@ void ipp_sched_cmd(struct work_struct *work)
return; return;
} }
mutex_lock(&c_node->cmd_lock); mutex_lock(&c_node->lock);
property = &c_node->property; property = &c_node->property;
...@@ -1494,7 +1492,7 @@ void ipp_sched_cmd(struct work_struct *work) ...@@ -1494,7 +1492,7 @@ void ipp_sched_cmd(struct work_struct *work)
DRM_DEBUG_KMS("ctrl[%d] done.\n", cmd_work->ctrl); DRM_DEBUG_KMS("ctrl[%d] done.\n", cmd_work->ctrl);
err_unlock: err_unlock:
mutex_unlock(&c_node->cmd_lock); mutex_unlock(&c_node->lock);
} }
static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv, static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
...@@ -1524,14 +1522,18 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv, ...@@ -1524,14 +1522,18 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
return -EINVAL; return -EINVAL;
} }
mutex_lock(&c_node->event_lock);
if (list_empty(&c_node->event_list)) { if (list_empty(&c_node->event_list)) {
DRM_DEBUG_KMS("event list is empty.\n"); DRM_DEBUG_KMS("event list is empty.\n");
return 0; ret = 0;
goto err_event_unlock;
} }
mutex_lock(&c_node->mem_lock);
if (!ipp_check_mem_list(c_node)) { if (!ipp_check_mem_list(c_node)) {
DRM_DEBUG_KMS("empty memory.\n"); DRM_DEBUG_KMS("empty memory.\n");
return 0; ret = 0;
goto err_mem_unlock;
} }
/* check command */ /* check command */
...@@ -1545,7 +1547,8 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv, ...@@ -1545,7 +1547,8 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
struct drm_exynos_ipp_mem_node, list); struct drm_exynos_ipp_mem_node, list);
if (!m_node) { if (!m_node) {
DRM_ERROR("empty memory node.\n"); DRM_ERROR("empty memory node.\n");
return -ENOMEM; ret = -ENOMEM;
goto err_mem_unlock;
} }
tbuf_id[i] = m_node->buf_id; tbuf_id[i] = m_node->buf_id;
...@@ -1567,7 +1570,8 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv, ...@@ -1567,7 +1570,8 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
m_node = ipp_find_mem_node(c_node, &qbuf); m_node = ipp_find_mem_node(c_node, &qbuf);
if (!m_node) { if (!m_node) {
DRM_ERROR("empty memory node.\n"); DRM_ERROR("empty memory node.\n");
return -ENOMEM; ret = -ENOMEM;
goto err_mem_unlock;
} }
tbuf_id[EXYNOS_DRM_OPS_DST] = m_node->buf_id; tbuf_id[EXYNOS_DRM_OPS_DST] = m_node->buf_id;
...@@ -1584,7 +1588,8 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv, ...@@ -1584,7 +1588,8 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
struct drm_exynos_ipp_mem_node, list); struct drm_exynos_ipp_mem_node, list);
if (!m_node) { if (!m_node) {
DRM_ERROR("empty memory node.\n"); DRM_ERROR("empty memory node.\n");
return -ENOMEM; ret = -ENOMEM;
goto err_mem_unlock;
} }
tbuf_id[EXYNOS_DRM_OPS_SRC] = m_node->buf_id; tbuf_id[EXYNOS_DRM_OPS_SRC] = m_node->buf_id;
...@@ -1595,8 +1600,10 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv, ...@@ -1595,8 +1600,10 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
break; break;
default: default:
DRM_ERROR("invalid operations.\n"); DRM_ERROR("invalid operations.\n");
return -EINVAL; ret = -EINVAL;
goto err_mem_unlock;
} }
mutex_unlock(&c_node->mem_lock);
if (tbuf_id[EXYNOS_DRM_OPS_DST] != buf_id[EXYNOS_DRM_OPS_DST]) if (tbuf_id[EXYNOS_DRM_OPS_DST] != buf_id[EXYNOS_DRM_OPS_DST])
DRM_ERROR("failed to match buf_id[%d %d]prop_id[%d]\n", DRM_ERROR("failed to match buf_id[%d %d]prop_id[%d]\n",
...@@ -1611,11 +1618,6 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv, ...@@ -1611,11 +1618,6 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
e = list_first_entry(&c_node->event_list, e = list_first_entry(&c_node->event_list,
struct drm_exynos_ipp_send_event, base.link); struct drm_exynos_ipp_send_event, base.link);
if (!e) {
DRM_ERROR("empty event.\n");
return -EINVAL;
}
do_gettimeofday(&now); do_gettimeofday(&now);
DRM_DEBUG_KMS("tv_sec[%ld]tv_usec[%ld]\n", now.tv_sec, now.tv_usec); DRM_DEBUG_KMS("tv_sec[%ld]tv_usec[%ld]\n", now.tv_sec, now.tv_usec);
e->event.tv_sec = now.tv_sec; e->event.tv_sec = now.tv_sec;
...@@ -1630,11 +1632,18 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv, ...@@ -1630,11 +1632,18 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
list_move_tail(&e->base.link, &e->base.file_priv->event_list); list_move_tail(&e->base.link, &e->base.file_priv->event_list);
wake_up_interruptible(&e->base.file_priv->event_wait); wake_up_interruptible(&e->base.file_priv->event_wait);
spin_unlock_irqrestore(&drm_dev->event_lock, flags); spin_unlock_irqrestore(&drm_dev->event_lock, flags);
mutex_unlock(&c_node->event_lock);
DRM_DEBUG_KMS("done cmd[%d]prop_id[%d]buf_id[%d]\n", DRM_DEBUG_KMS("done cmd[%d]prop_id[%d]buf_id[%d]\n",
property->cmd, property->prop_id, tbuf_id[EXYNOS_DRM_OPS_DST]); property->cmd, property->prop_id, tbuf_id[EXYNOS_DRM_OPS_DST]);
return 0; return 0;
err_mem_unlock:
mutex_unlock(&c_node->mem_lock);
err_event_unlock:
mutex_unlock(&c_node->event_lock);
return ret;
} }
void ipp_sched_event(struct work_struct *work) void ipp_sched_event(struct work_struct *work)
...@@ -1676,8 +1685,6 @@ void ipp_sched_event(struct work_struct *work) ...@@ -1676,8 +1685,6 @@ void ipp_sched_event(struct work_struct *work)
goto err_completion; goto err_completion;
} }
mutex_lock(&c_node->event_lock);
ret = ipp_send_event(ippdrv, c_node, event_work->buf_id); ret = ipp_send_event(ippdrv, c_node, event_work->buf_id);
if (ret) { if (ret) {
DRM_ERROR("failed to send event.\n"); DRM_ERROR("failed to send event.\n");
...@@ -1687,8 +1694,6 @@ void ipp_sched_event(struct work_struct *work) ...@@ -1687,8 +1694,6 @@ void ipp_sched_event(struct work_struct *work)
err_completion: err_completion:
if (ipp_is_m2m_cmd(c_node->property.cmd)) if (ipp_is_m2m_cmd(c_node->property.cmd))
complete(&c_node->start_complete); complete(&c_node->start_complete);
mutex_unlock(&c_node->event_lock);
} }
static int ipp_subdrv_probe(struct drm_device *drm_dev, struct device *dev) static int ipp_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
...@@ -1699,23 +1704,21 @@ static int ipp_subdrv_probe(struct drm_device *drm_dev, struct device *dev) ...@@ -1699,23 +1704,21 @@ static int ipp_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
/* get ipp driver entry */ /* get ipp driver entry */
list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) { list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) {
u32 ipp_id;
ippdrv->drm_dev = drm_dev; ippdrv->drm_dev = drm_dev;
ret = ipp_create_id(&ctx->ipp_idr, &ctx->ipp_lock, ippdrv, ret = ipp_create_id(&ctx->ipp_idr, &ctx->ipp_lock, ippdrv,
&ippdrv->ipp_id); &ipp_id);
if (ret) { if (ret || ipp_id == 0) {
DRM_ERROR("failed to create id.\n"); DRM_ERROR("failed to create id.\n");
goto err_idr; goto err;
} }
DRM_DEBUG_KMS("count[%d]ippdrv[0x%x]ipp_id[%d]\n", DRM_DEBUG_KMS("count[%d]ippdrv[0x%x]ipp_id[%d]\n",
count++, (int)ippdrv, ippdrv->ipp_id); count++, (int)ippdrv, ipp_id);
if (ippdrv->ipp_id == 0) { ippdrv->prop_list.ipp_id = ipp_id;
DRM_ERROR("failed to get ipp_id[%d]\n",
ippdrv->ipp_id);
goto err_idr;
}
/* store parent device for node */ /* store parent device for node */
ippdrv->parent_dev = dev; ippdrv->parent_dev = dev;
...@@ -1724,39 +1727,46 @@ static int ipp_subdrv_probe(struct drm_device *drm_dev, struct device *dev) ...@@ -1724,39 +1727,46 @@ static int ipp_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
ippdrv->event_workq = ctx->event_workq; ippdrv->event_workq = ctx->event_workq;
ippdrv->sched_event = ipp_sched_event; ippdrv->sched_event = ipp_sched_event;
INIT_LIST_HEAD(&ippdrv->cmd_list); INIT_LIST_HEAD(&ippdrv->cmd_list);
mutex_init(&ippdrv->cmd_lock);
if (is_drm_iommu_supported(drm_dev)) { if (is_drm_iommu_supported(drm_dev)) {
ret = drm_iommu_attach_device(drm_dev, ippdrv->dev); ret = drm_iommu_attach_device(drm_dev, ippdrv->dev);
if (ret) { if (ret) {
DRM_ERROR("failed to activate iommu\n"); DRM_ERROR("failed to activate iommu\n");
goto err_iommu; goto err;
} }
} }
} }
return 0; return 0;
err_iommu: err:
/* get ipp driver entry */ /* get ipp driver entry */
list_for_each_entry_reverse(ippdrv, &exynos_drm_ippdrv_list, drv_list) list_for_each_entry_continue_reverse(ippdrv, &exynos_drm_ippdrv_list,
drv_list) {
if (is_drm_iommu_supported(drm_dev)) if (is_drm_iommu_supported(drm_dev))
drm_iommu_detach_device(drm_dev, ippdrv->dev); drm_iommu_detach_device(drm_dev, ippdrv->dev);
err_idr: ipp_remove_id(&ctx->ipp_idr, &ctx->ipp_lock,
idr_destroy(&ctx->ipp_idr); ippdrv->prop_list.ipp_id);
idr_destroy(&ctx->prop_idr); }
return ret; return ret;
} }
static void ipp_subdrv_remove(struct drm_device *drm_dev, struct device *dev) static void ipp_subdrv_remove(struct drm_device *drm_dev, struct device *dev)
{ {
struct exynos_drm_ippdrv *ippdrv; struct exynos_drm_ippdrv *ippdrv;
struct ipp_context *ctx = get_ipp_context(dev);
/* get ipp driver entry */ /* get ipp driver entry */
list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) { list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) {
if (is_drm_iommu_supported(drm_dev)) if (is_drm_iommu_supported(drm_dev))
drm_iommu_detach_device(drm_dev, ippdrv->dev); drm_iommu_detach_device(drm_dev, ippdrv->dev);
ipp_remove_id(&ctx->ipp_idr, &ctx->ipp_lock,
ippdrv->prop_list.ipp_id);
ippdrv->drm_dev = NULL; ippdrv->drm_dev = NULL;
exynos_drm_ippdrv_unregister(ippdrv); exynos_drm_ippdrv_unregister(ippdrv);
} }
...@@ -1787,20 +1797,14 @@ static void ipp_subdrv_close(struct drm_device *drm_dev, struct device *dev, ...@@ -1787,20 +1797,14 @@ static void ipp_subdrv_close(struct drm_device *drm_dev, struct device *dev,
struct drm_exynos_file_private *file_priv = file->driver_priv; struct drm_exynos_file_private *file_priv = file->driver_priv;
struct exynos_drm_ipp_private *priv = file_priv->ipp_priv; struct exynos_drm_ipp_private *priv = file_priv->ipp_priv;
struct exynos_drm_ippdrv *ippdrv = NULL; struct exynos_drm_ippdrv *ippdrv = NULL;
struct ipp_context *ctx = get_ipp_context(dev);
struct drm_exynos_ipp_cmd_node *c_node, *tc_node; struct drm_exynos_ipp_cmd_node *c_node, *tc_node;
int count = 0; int count = 0;
DRM_DEBUG_KMS("for priv[0x%x]\n", (int)priv); DRM_DEBUG_KMS("for priv[0x%x]\n", (int)priv);
if (list_empty(&exynos_drm_ippdrv_list)) {
DRM_DEBUG_KMS("ippdrv_list is empty.\n");
goto err_clear;
}
list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) { list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) {
if (list_empty(&ippdrv->cmd_list)) mutex_lock(&ippdrv->cmd_lock);
continue;
list_for_each_entry_safe(c_node, tc_node, list_for_each_entry_safe(c_node, tc_node,
&ippdrv->cmd_list, list) { &ippdrv->cmd_list, list) {
DRM_DEBUG_KMS("count[%d]ippdrv[0x%x]\n", DRM_DEBUG_KMS("count[%d]ippdrv[0x%x]\n",
...@@ -1820,14 +1824,14 @@ static void ipp_subdrv_close(struct drm_device *drm_dev, struct device *dev, ...@@ -1820,14 +1824,14 @@ static void ipp_subdrv_close(struct drm_device *drm_dev, struct device *dev,
} }
ippdrv->dedicated = false; ippdrv->dedicated = false;
ipp_clean_cmd_node(c_node); ipp_clean_cmd_node(ctx, c_node);
if (list_empty(&ippdrv->cmd_list)) if (list_empty(&ippdrv->cmd_list))
pm_runtime_put_sync(ippdrv->dev); pm_runtime_put_sync(ippdrv->dev);
} }
} }
mutex_unlock(&ippdrv->cmd_lock);
} }
err_clear:
kfree(priv); kfree(priv);
return; return;
} }
......
...@@ -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;
exynos_drm_manager_register(&vidi_manager); ret = exynos_drm_subdrv_register(subdrv);
exynos_drm_display_register(&vidi_display); if (ret < 0) {
dev_err(&pdev->dev, "failed to register drm vidi device\n");
return ret;
}
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; 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);
}
...@@ -33,13 +33,17 @@ ...@@ -33,13 +33,17 @@
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/i2c.h> #include <linux/of_address.h>
#include <linux/of_gpio.h> #include <linux/of_gpio.h>
#include <linux/hdmi.h> #include <linux/hdmi.h>
#include <linux/component.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <drm/exynos_drm.h> #include <drm/exynos_drm.h>
#include "exynos_drm_drv.h" #include "exynos_drm_drv.h"
#include "exynos_drm_crtc.h"
#include "exynos_mixer.h" #include "exynos_mixer.h"
#include <linux/gpio.h> #include <linux/gpio.h>
...@@ -48,6 +52,8 @@ ...@@ -48,6 +52,8 @@
#define get_hdmi_display(dev) platform_get_drvdata(to_platform_device(dev)) #define get_hdmi_display(dev) platform_get_drvdata(to_platform_device(dev))
#define ctx_from_connector(c) container_of(c, struct hdmi_context, connector) #define ctx_from_connector(c) container_of(c, struct hdmi_context, connector)
#define HOTPLUG_DEBOUNCE_MS 1100
/* AVI header and aspect ratio */ /* AVI header and aspect ratio */
#define HDMI_AVI_VERSION 0x02 #define HDMI_AVI_VERSION 0x02
#define HDMI_AVI_LENGTH 0x0D #define HDMI_AVI_LENGTH 0x0D
...@@ -66,6 +72,8 @@ enum hdmi_type { ...@@ -66,6 +72,8 @@ enum hdmi_type {
struct hdmi_driver_data { struct hdmi_driver_data {
unsigned int type; unsigned int type;
const struct hdmiphy_config *phy_confs;
unsigned int phy_conf_count;
unsigned int is_apb_phy:1; unsigned int is_apb_phy:1;
}; };
...@@ -74,7 +82,6 @@ struct hdmi_resources { ...@@ -74,7 +82,6 @@ struct hdmi_resources {
struct clk *sclk_hdmi; struct clk *sclk_hdmi;
struct clk *sclk_pixel; struct clk *sclk_pixel;
struct clk *sclk_hdmiphy; struct clk *sclk_hdmiphy;
struct clk *hdmiphy;
struct clk *mout_hdmi; struct clk *mout_hdmi;
struct regulator_bulk_data *regul_bulk; struct regulator_bulk_data *regul_bulk;
int regul_count; int regul_count;
...@@ -185,17 +192,23 @@ struct hdmi_context { ...@@ -185,17 +192,23 @@ struct hdmi_context {
void __iomem *regs; void __iomem *regs;
int irq; int irq;
struct delayed_work hotplug_work;
struct i2c_adapter *ddc_adpt; struct i2c_adapter *ddc_adpt;
struct i2c_client *hdmiphy_port; struct i2c_client *hdmiphy_port;
/* current hdmiphy conf regs */ /* current hdmiphy conf regs */
struct drm_display_mode current_mode;
struct hdmi_conf_regs mode_conf; struct hdmi_conf_regs mode_conf;
struct hdmi_resources res; struct hdmi_resources res;
int hpd_gpio; int hpd_gpio;
void __iomem *regs_hdmiphy;
const struct hdmiphy_config *phy_confs;
unsigned int phy_conf_count;
struct regmap *pmureg;
enum hdmi_type type; enum hdmi_type type;
}; };
...@@ -204,14 +217,6 @@ struct hdmiphy_config { ...@@ -204,14 +217,6 @@ struct hdmiphy_config {
u8 conf[32]; u8 conf[32];
}; };
struct hdmi_driver_data exynos4212_hdmi_driver_data = {
.type = HDMI_TYPE14,
};
struct hdmi_driver_data exynos5_hdmi_driver_data = {
.type = HDMI_TYPE14,
};
/* list of phy config settings */ /* list of phy config settings */
static const struct hdmiphy_config hdmiphy_v13_configs[] = { static const struct hdmiphy_config hdmiphy_v13_configs[] = {
{ {
...@@ -319,18 +324,18 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = { ...@@ -319,18 +324,18 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = {
{ {
.pixel_clock = 71000000, .pixel_clock = 71000000,
.conf = { .conf = {
0x01, 0x91, 0x1e, 0x15, 0x40, 0x3c, 0xce, 0x08, 0x01, 0xd1, 0x3b, 0x35, 0x40, 0x0c, 0x04, 0x08,
0x04, 0x20, 0xb2, 0xd8, 0x45, 0xa0, 0xac, 0x80, 0x85, 0xa0, 0x63, 0xd9, 0x45, 0xa0, 0xac, 0x80,
0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86, 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
0x54, 0xad, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80, 0x54, 0xad, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
}, },
}, },
{ {
.pixel_clock = 73250000, .pixel_clock = 73250000,
.conf = { .conf = {
0x01, 0xd1, 0x1f, 0x15, 0x40, 0x18, 0xe9, 0x08, 0x01, 0xd1, 0x3d, 0x35, 0x40, 0x18, 0x02, 0x08,
0x02, 0xa0, 0xb7, 0xd8, 0x45, 0xa0, 0xac, 0x80, 0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86, 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
0x54, 0xa8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80, 0x54, 0xa8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
}, },
}, },
...@@ -361,15 +366,6 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = { ...@@ -361,15 +366,6 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = {
0x54, 0x93, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80, 0x54, 0x93, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
}, },
}, },
{
.pixel_clock = 88750000,
.conf = {
0x01, 0x91, 0x25, 0x17, 0x40, 0x30, 0xfe, 0x08,
0x06, 0x20, 0xde, 0xd8, 0x45, 0xa0, 0xac, 0x80,
0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
0x54, 0x8a, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
},
},
{ {
.pixel_clock = 106500000, .pixel_clock = 106500000,
.conf = { .conf = {
...@@ -391,18 +387,18 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = { ...@@ -391,18 +387,18 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = {
{ {
.pixel_clock = 115500000, .pixel_clock = 115500000,
.conf = { .conf = {
0x01, 0xd1, 0x30, 0x1a, 0x40, 0x40, 0x10, 0x04, 0x01, 0xd1, 0x30, 0x12, 0x40, 0x40, 0x10, 0x08,
0x04, 0xa0, 0x21, 0xd9, 0x45, 0xa0, 0xac, 0x80, 0x80, 0x80, 0x21, 0xd9, 0x45, 0xa0, 0xac, 0x80,
0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86, 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
0x54, 0xaa, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80, 0x54, 0xaa, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
}, },
}, },
{ {
.pixel_clock = 119000000, .pixel_clock = 119000000,
.conf = { .conf = {
0x01, 0x91, 0x32, 0x14, 0x40, 0x60, 0xd8, 0x08, 0x01, 0xd1, 0x32, 0x1a, 0x40, 0x30, 0xd8, 0x08,
0x06, 0x20, 0x2a, 0xd9, 0x45, 0xa0, 0xac, 0x80, 0x04, 0xa0, 0x2a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86, 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
0x54, 0x9d, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80, 0x54, 0x9d, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
}, },
}, },
...@@ -426,6 +422,183 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = { ...@@ -426,6 +422,183 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = {
}, },
}; };
static const struct hdmiphy_config hdmiphy_5420_configs[] = {
{
.pixel_clock = 25200000,
.conf = {
0x01, 0x52, 0x3F, 0x55, 0x40, 0x01, 0x00, 0xC8,
0x82, 0xC8, 0xBD, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
0x06, 0x80, 0x01, 0x84, 0x05, 0x02, 0x24, 0x66,
0x54, 0xF4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
},
},
{
.pixel_clock = 27000000,
.conf = {
0x01, 0xD1, 0x22, 0x51, 0x40, 0x08, 0xFC, 0xE0,
0x98, 0xE8, 0xCB, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
0x06, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
0x54, 0xE4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
},
},
{
.pixel_clock = 27027000,
.conf = {
0x01, 0xD1, 0x2D, 0x72, 0x40, 0x64, 0x12, 0xC8,
0x43, 0xE8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
0x26, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
0x54, 0xE3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
},
},
{
.pixel_clock = 36000000,
.conf = {
0x01, 0x51, 0x2D, 0x55, 0x40, 0x40, 0x00, 0xC8,
0x02, 0xC8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
0x54, 0xAB, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
},
},
{
.pixel_clock = 40000000,
.conf = {
0x01, 0xD1, 0x21, 0x31, 0x40, 0x3C, 0x28, 0xC8,
0x87, 0xE8, 0xC8, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
0x54, 0x9A, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
},
},
{
.pixel_clock = 65000000,
.conf = {
0x01, 0xD1, 0x36, 0x34, 0x40, 0x0C, 0x04, 0xC8,
0x82, 0xE8, 0x45, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
0x54, 0xBD, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
},
},
{
.pixel_clock = 71000000,
.conf = {
0x01, 0xD1, 0x3B, 0x35, 0x40, 0x0C, 0x04, 0xC8,
0x85, 0xE8, 0x63, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
0x54, 0x57, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
},
},
{
.pixel_clock = 73250000,
.conf = {
0x01, 0xD1, 0x1F, 0x10, 0x40, 0x78, 0x8D, 0xC8,
0x81, 0xE8, 0xB7, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
0x56, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
0x54, 0xA8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
},
},
{
.pixel_clock = 74176000,
.conf = {
0x01, 0xD1, 0x1F, 0x10, 0x40, 0x5B, 0xEF, 0xC8,
0x81, 0xE8, 0xB9, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
0x56, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
0x54, 0xA6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
},
},
{
.pixel_clock = 74250000,
.conf = {
0x01, 0xD1, 0x1F, 0x10, 0x40, 0x40, 0xF8, 0x08,
0x81, 0xE8, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
0x26, 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x66,
0x54, 0xA5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
},
},
{
.pixel_clock = 83500000,
.conf = {
0x01, 0xD1, 0x23, 0x11, 0x40, 0x0C, 0xFB, 0xC8,
0x85, 0xE8, 0xD1, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
0x54, 0x4A, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
},
},
{
.pixel_clock = 88750000,
.conf = {
0x01, 0xD1, 0x25, 0x11, 0x40, 0x18, 0xFF, 0xC8,
0x83, 0xE8, 0xDE, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
0x54, 0x45, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
},
},
{
.pixel_clock = 106500000,
.conf = {
0x01, 0xD1, 0x2C, 0x12, 0x40, 0x0C, 0x09, 0xC8,
0x84, 0xE8, 0x0A, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
},
},
{
.pixel_clock = 108000000,
.conf = {
0x01, 0x51, 0x2D, 0x15, 0x40, 0x01, 0x00, 0xC8,
0x82, 0xC8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
0x54, 0xC7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
},
},
{
.pixel_clock = 115500000,
.conf = {
0x01, 0xD1, 0x30, 0x14, 0x40, 0x0C, 0x03, 0xC8,
0x88, 0xE8, 0x21, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
0x54, 0x6A, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
},
},
{
.pixel_clock = 146250000,
.conf = {
0x01, 0xD1, 0x3D, 0x15, 0x40, 0x18, 0xFD, 0xC8,
0x83, 0xE8, 0x6E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
0x54, 0x54, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
},
},
{
.pixel_clock = 148500000,
.conf = {
0x01, 0xD1, 0x1F, 0x00, 0x40, 0x40, 0xF8, 0x08,
0x81, 0xE8, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
0x26, 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x66,
0x54, 0x4B, 0x25, 0x03, 0x00, 0x80, 0x01, 0x80,
},
},
};
static struct hdmi_driver_data exynos5420_hdmi_driver_data = {
.type = HDMI_TYPE14,
.phy_confs = hdmiphy_5420_configs,
.phy_conf_count = ARRAY_SIZE(hdmiphy_5420_configs),
.is_apb_phy = 1,
};
static struct hdmi_driver_data exynos4212_hdmi_driver_data = {
.type = HDMI_TYPE14,
.phy_confs = hdmiphy_v14_configs,
.phy_conf_count = ARRAY_SIZE(hdmiphy_v14_configs),
.is_apb_phy = 0,
};
static struct hdmi_driver_data exynos5_hdmi_driver_data = {
.type = HDMI_TYPE14,
.phy_confs = hdmiphy_v13_configs,
.phy_conf_count = ARRAY_SIZE(hdmiphy_v13_configs),
.is_apb_phy = 0,
};
static inline u32 hdmi_reg_read(struct hdmi_context *hdata, u32 reg_id) static inline u32 hdmi_reg_read(struct hdmi_context *hdata, u32 reg_id)
{ {
return readl(hdata->regs + reg_id); return readl(hdata->regs + reg_id);
...@@ -445,6 +618,48 @@ static inline void hdmi_reg_writemask(struct hdmi_context *hdata, ...@@ -445,6 +618,48 @@ static inline void hdmi_reg_writemask(struct hdmi_context *hdata,
writel(value, hdata->regs + reg_id); writel(value, hdata->regs + reg_id);
} }
static int hdmiphy_reg_writeb(struct hdmi_context *hdata,
u32 reg_offset, u8 value)
{
if (hdata->hdmiphy_port) {
u8 buffer[2];
int ret;
buffer[0] = reg_offset;
buffer[1] = value;
ret = i2c_master_send(hdata->hdmiphy_port, buffer, 2);
if (ret == 2)
return 0;
return ret;
} else {
writeb(value, hdata->regs_hdmiphy + (reg_offset<<2));
return 0;
}
}
static int hdmiphy_reg_write_buf(struct hdmi_context *hdata,
u32 reg_offset, const u8 *buf, u32 len)
{
if ((reg_offset + len) > 32)
return -EINVAL;
if (hdata->hdmiphy_port) {
int ret;
ret = i2c_master_send(hdata->hdmiphy_port, buf, len);
if (ret == len)
return 0;
return ret;
} else {
int i;
for (i = 0; i < len; i++)
writeb(buf[i], hdata->regs_hdmiphy +
((reg_offset + i)<<2));
return 0;
}
}
static void hdmi_v13_regs_dump(struct hdmi_context *hdata, char *prefix) static void hdmi_v13_regs_dump(struct hdmi_context *hdata, char *prefix)
{ {
#define DUMPREG(reg_id) \ #define DUMPREG(reg_id) \
...@@ -809,6 +1024,8 @@ static enum drm_connector_status hdmi_detect(struct drm_connector *connector, ...@@ -809,6 +1024,8 @@ static enum drm_connector_status hdmi_detect(struct drm_connector *connector,
{ {
struct hdmi_context *hdata = ctx_from_connector(connector); struct hdmi_context *hdata = ctx_from_connector(connector);
hdata->hpd = gpio_get_value(hdata->hpd_gpio);
return hdata->hpd ? connector_status_connected : return hdata->hpd ? connector_status_connected :
connector_status_disconnected; connector_status_disconnected;
} }
...@@ -848,20 +1065,10 @@ static int hdmi_get_modes(struct drm_connector *connector) ...@@ -848,20 +1065,10 @@ static int hdmi_get_modes(struct drm_connector *connector)
static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock) static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
{ {
const struct hdmiphy_config *confs; int i;
int count, i;
if (hdata->type == HDMI_TYPE13) {
confs = hdmiphy_v13_configs;
count = ARRAY_SIZE(hdmiphy_v13_configs);
} else if (hdata->type == HDMI_TYPE14) {
confs = hdmiphy_v14_configs;
count = ARRAY_SIZE(hdmiphy_v14_configs);
} else
return -EINVAL;
for (i = 0; i < count; i++) for (i = 0; i < hdata->phy_conf_count; i++)
if (confs[i].pixel_clock == pixel_clock) if (hdata->phy_confs[i].pixel_clock == pixel_clock)
return i; return i;
DRM_DEBUG_KMS("Could not find phy config for %d\n", pixel_clock); DRM_DEBUG_KMS("Could not find phy config for %d\n", pixel_clock);
...@@ -928,16 +1135,6 @@ static int hdmi_create_connector(struct exynos_drm_display *display, ...@@ -928,16 +1135,6 @@ static int hdmi_create_connector(struct exynos_drm_display *display,
return 0; return 0;
} }
static int hdmi_initialize(struct exynos_drm_display *display,
struct drm_device *drm_dev)
{
struct hdmi_context *hdata = display->ctx;
hdata->drm_dev = drm_dev;
return 0;
}
static void hdmi_mode_fixup(struct exynos_drm_display *display, static void hdmi_mode_fixup(struct exynos_drm_display *display,
struct drm_connector *connector, struct drm_connector *connector,
const struct drm_display_mode *mode, const struct drm_display_mode *mode,
...@@ -1136,20 +1333,15 @@ static void hdmi_audio_control(struct hdmi_context *hdata, bool onoff) ...@@ -1136,20 +1333,15 @@ static void hdmi_audio_control(struct hdmi_context *hdata, bool onoff)
HDMI_ASP_EN : HDMI_ASP_DIS, HDMI_ASP_MASK); HDMI_ASP_EN : HDMI_ASP_DIS, HDMI_ASP_MASK);
} }
static void hdmi_conf_reset(struct hdmi_context *hdata) static void hdmi_start(struct hdmi_context *hdata, bool start)
{ {
u32 reg; u32 val = start ? HDMI_TG_EN : 0;
if (hdata->type == HDMI_TYPE13) if (hdata->current_mode.flags & DRM_MODE_FLAG_INTERLACE)
reg = HDMI_V13_CORE_RSTOUT; val |= HDMI_FIELD_EN;
else
reg = HDMI_CORE_RSTOUT;
/* resetting HDMI core */ hdmi_reg_writemask(hdata, HDMI_CON_0, val, HDMI_EN);
hdmi_reg_writemask(hdata, reg, 0, HDMI_CORE_SW_RSTOUT); hdmi_reg_writemask(hdata, HDMI_TG_CMD, val, HDMI_TG_EN | HDMI_FIELD_EN);
usleep_range(10000, 12000);
hdmi_reg_writemask(hdata, reg, ~0, HDMI_CORE_SW_RSTOUT);
usleep_range(10000, 12000);
} }
static void hdmi_conf_init(struct hdmi_context *hdata) static void hdmi_conf_init(struct hdmi_context *hdata)
...@@ -1163,6 +1355,8 @@ static void hdmi_conf_init(struct hdmi_context *hdata) ...@@ -1163,6 +1355,8 @@ static void hdmi_conf_init(struct hdmi_context *hdata)
/* choose HDMI mode */ /* choose HDMI mode */
hdmi_reg_writemask(hdata, HDMI_MODE_SEL, hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
HDMI_MODE_HDMI_EN, HDMI_MODE_MASK); HDMI_MODE_HDMI_EN, HDMI_MODE_MASK);
/* Apply Video preable and Guard band in HDMI mode only */
hdmi_reg_writeb(hdata, HDMI_CON_2, 0);
/* disable bluescreen */ /* disable bluescreen */
hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN); hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN);
...@@ -1286,12 +1480,7 @@ static void hdmi_v13_mode_apply(struct hdmi_context *hdata) ...@@ -1286,12 +1480,7 @@ static void hdmi_v13_mode_apply(struct hdmi_context *hdata)
clk_prepare_enable(hdata->res.sclk_hdmi); clk_prepare_enable(hdata->res.sclk_hdmi);
/* enable HDMI and timing generator */ /* enable HDMI and timing generator */
hdmi_reg_writemask(hdata, HDMI_CON_0, ~0, HDMI_EN); hdmi_start(hdata, true);
if (core->int_pro_mode[0])
hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN |
HDMI_FIELD_EN);
else
hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN);
} }
static void hdmi_v14_mode_apply(struct hdmi_context *hdata) static void hdmi_v14_mode_apply(struct hdmi_context *hdata)
...@@ -1453,12 +1642,7 @@ static void hdmi_v14_mode_apply(struct hdmi_context *hdata) ...@@ -1453,12 +1642,7 @@ static void hdmi_v14_mode_apply(struct hdmi_context *hdata)
clk_prepare_enable(hdata->res.sclk_hdmi); clk_prepare_enable(hdata->res.sclk_hdmi);
/* enable HDMI and timing generator */ /* enable HDMI and timing generator */
hdmi_reg_writemask(hdata, HDMI_CON_0, ~0, HDMI_EN); hdmi_start(hdata, true);
if (core->int_pro_mode[0])
hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN |
HDMI_FIELD_EN);
else
hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN);
} }
static void hdmi_mode_apply(struct hdmi_context *hdata) static void hdmi_mode_apply(struct hdmi_context *hdata)
...@@ -1499,32 +1683,51 @@ static void hdmiphy_conf_reset(struct hdmi_context *hdata) ...@@ -1499,32 +1683,51 @@ static void hdmiphy_conf_reset(struct hdmi_context *hdata)
static void hdmiphy_poweron(struct hdmi_context *hdata) static void hdmiphy_poweron(struct hdmi_context *hdata)
{ {
if (hdata->type == HDMI_TYPE14) if (hdata->type != HDMI_TYPE14)
hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, 0, return;
HDMI_PHY_POWER_OFF_EN);
DRM_DEBUG_KMS("\n");
/* For PHY Mode Setting */
hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
HDMI_PHY_ENABLE_MODE_SET);
/* Phy Power On */
hdmiphy_reg_writeb(hdata, HDMIPHY_POWER,
HDMI_PHY_POWER_ON);
/* For PHY Mode Setting */
hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
HDMI_PHY_DISABLE_MODE_SET);
/* PHY SW Reset */
hdmiphy_conf_reset(hdata);
} }
static void hdmiphy_poweroff(struct hdmi_context *hdata) static void hdmiphy_poweroff(struct hdmi_context *hdata)
{ {
if (hdata->type == HDMI_TYPE14) if (hdata->type != HDMI_TYPE14)
hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, ~0, return;
HDMI_PHY_POWER_OFF_EN);
DRM_DEBUG_KMS("\n");
/* PHY SW Reset */
hdmiphy_conf_reset(hdata);
/* For PHY Mode Setting */
hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
HDMI_PHY_ENABLE_MODE_SET);
/* PHY Power Off */
hdmiphy_reg_writeb(hdata, HDMIPHY_POWER,
HDMI_PHY_POWER_OFF);
/* For PHY Mode Setting */
hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
HDMI_PHY_DISABLE_MODE_SET);
} }
static void hdmiphy_conf_apply(struct hdmi_context *hdata) static void hdmiphy_conf_apply(struct hdmi_context *hdata)
{ {
const u8 *hdmiphy_data;
u8 buffer[32];
u8 operation[2];
u8 read_buffer[32] = {0, };
int ret; int ret;
int i; int i;
if (!hdata->hdmiphy_port) {
DRM_ERROR("hdmiphy is not attached\n");
return;
}
/* pixel clock */ /* pixel clock */
i = hdmi_find_phy_conf(hdata, hdata->mode_conf.pixel_clock); i = hdmi_find_phy_conf(hdata, hdata->mode_conf.pixel_clock);
if (i < 0) { if (i < 0) {
...@@ -1532,39 +1735,21 @@ static void hdmiphy_conf_apply(struct hdmi_context *hdata) ...@@ -1532,39 +1735,21 @@ static void hdmiphy_conf_apply(struct hdmi_context *hdata)
return; return;
} }
if (hdata->type == HDMI_TYPE13) ret = hdmiphy_reg_write_buf(hdata, 0, hdata->phy_confs[i].conf, 32);
hdmiphy_data = hdmiphy_v13_configs[i].conf; if (ret) {
else DRM_ERROR("failed to configure hdmiphy\n");
hdmiphy_data = hdmiphy_v14_configs[i].conf;
memcpy(buffer, hdmiphy_data, 32);
ret = i2c_master_send(hdata->hdmiphy_port, buffer, 32);
if (ret != 32) {
DRM_ERROR("failed to configure HDMIPHY via I2C\n");
return; return;
} }
usleep_range(10000, 12000); usleep_range(10000, 12000);
/* operation mode */ ret = hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
operation[0] = 0x1f; HDMI_PHY_DISABLE_MODE_SET);
operation[1] = 0x80; if (ret) {
ret = i2c_master_send(hdata->hdmiphy_port, operation, 2);
if (ret != 2) {
DRM_ERROR("failed to enable hdmiphy\n"); DRM_ERROR("failed to enable hdmiphy\n");
return; return;
} }
ret = i2c_master_recv(hdata->hdmiphy_port, read_buffer, 32);
if (ret < 0) {
DRM_ERROR("failed to read hdmiphy config\n");
return;
}
for (i = 0; i < ret; i++)
DRM_DEBUG_KMS("hdmiphy[0x%02x] write[0x%02x] - "
"recv [0x%02x]\n", i, buffer[i], read_buffer[i]);
} }
static void hdmi_conf_apply(struct hdmi_context *hdata) static void hdmi_conf_apply(struct hdmi_context *hdata)
...@@ -1573,7 +1758,7 @@ static void hdmi_conf_apply(struct hdmi_context *hdata) ...@@ -1573,7 +1758,7 @@ static void hdmi_conf_apply(struct hdmi_context *hdata)
hdmiphy_conf_apply(hdata); hdmiphy_conf_apply(hdata);
mutex_lock(&hdata->hdmi_mutex); mutex_lock(&hdata->hdmi_mutex);
hdmi_conf_reset(hdata); hdmi_start(hdata, false);
hdmi_conf_init(hdata); hdmi_conf_init(hdata);
mutex_unlock(&hdata->hdmi_mutex); mutex_unlock(&hdata->hdmi_mutex);
...@@ -1814,6 +1999,9 @@ static void hdmi_mode_set(struct exynos_drm_display *display, ...@@ -1814,6 +1999,9 @@ static void hdmi_mode_set(struct exynos_drm_display *display,
m->vrefresh, (m->flags & DRM_MODE_FLAG_INTERLACE) ? m->vrefresh, (m->flags & DRM_MODE_FLAG_INTERLACE) ?
"INTERLACED" : "PROGERESSIVE"); "INTERLACED" : "PROGERESSIVE");
/* preserve mode information for later use. */
drm_mode_copy(&hdata->current_mode, mode);
if (hdata->type == HDMI_TYPE13) if (hdata->type == HDMI_TYPE13)
hdmi_v13_mode_set(hdata, mode); hdmi_v13_mode_set(hdata, mode);
else else
...@@ -1854,7 +2042,10 @@ static void hdmi_poweron(struct exynos_drm_display *display) ...@@ -1854,7 +2042,10 @@ static void hdmi_poweron(struct exynos_drm_display *display)
if (regulator_bulk_enable(res->regul_count, res->regul_bulk)) if (regulator_bulk_enable(res->regul_count, res->regul_bulk))
DRM_DEBUG_KMS("failed to enable regulator bulk\n"); DRM_DEBUG_KMS("failed to enable regulator bulk\n");
clk_prepare_enable(res->hdmiphy); /* set pmu hdmiphy control bit to enable hdmiphy */
regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
PMU_HDMI_PHY_ENABLE_BIT, 1);
clk_prepare_enable(res->hdmi); clk_prepare_enable(res->hdmi);
clk_prepare_enable(res->sclk_hdmi); clk_prepare_enable(res->sclk_hdmi);
...@@ -1872,16 +2063,20 @@ static void hdmi_poweroff(struct exynos_drm_display *display) ...@@ -1872,16 +2063,20 @@ static void hdmi_poweroff(struct exynos_drm_display *display)
goto out; goto out;
mutex_unlock(&hdata->hdmi_mutex); mutex_unlock(&hdata->hdmi_mutex);
/* /* HDMI System Disable */
* The TV power domain needs any condition of hdmiphy to turn off and hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_EN);
* its reset state seems to meet the condition.
*/
hdmiphy_conf_reset(hdata);
hdmiphy_poweroff(hdata); hdmiphy_poweroff(hdata);
cancel_delayed_work(&hdata->hotplug_work);
clk_disable_unprepare(res->sclk_hdmi); clk_disable_unprepare(res->sclk_hdmi);
clk_disable_unprepare(res->hdmi); clk_disable_unprepare(res->hdmi);
clk_disable_unprepare(res->hdmiphy);
/* reset pmu hdmiphy control bit to disable hdmiphy */
regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
PMU_HDMI_PHY_ENABLE_BIT, 0);
regulator_bulk_disable(res->regul_count, res->regul_bulk); regulator_bulk_disable(res->regul_count, res->regul_bulk);
pm_runtime_put_sync(hdata->dev); pm_runtime_put_sync(hdata->dev);
...@@ -1913,7 +2108,6 @@ static void hdmi_dpms(struct exynos_drm_display *display, int mode) ...@@ -1913,7 +2108,6 @@ static void hdmi_dpms(struct exynos_drm_display *display, int mode)
} }
static struct exynos_drm_display_ops hdmi_display_ops = { static struct exynos_drm_display_ops hdmi_display_ops = {
.initialize = hdmi_initialize,
.create_connector = hdmi_create_connector, .create_connector = hdmi_create_connector,
.mode_fixup = hdmi_mode_fixup, .mode_fixup = hdmi_mode_fixup,
.mode_set = hdmi_mode_set, .mode_set = hdmi_mode_set,
...@@ -1926,9 +2120,11 @@ static struct exynos_drm_display hdmi_display = { ...@@ -1926,9 +2120,11 @@ static struct exynos_drm_display hdmi_display = {
.ops = &hdmi_display_ops, .ops = &hdmi_display_ops,
}; };
static irqreturn_t hdmi_irq_thread(int irq, void *arg) static void hdmi_hotplug_work_func(struct work_struct *work)
{ {
struct hdmi_context *hdata = arg; struct hdmi_context *hdata;
hdata = container_of(work, struct hdmi_context, hotplug_work.work);
mutex_lock(&hdata->hdmi_mutex); mutex_lock(&hdata->hdmi_mutex);
hdata->hpd = gpio_get_value(hdata->hpd_gpio); hdata->hpd = gpio_get_value(hdata->hpd_gpio);
...@@ -1936,6 +2132,14 @@ static irqreturn_t hdmi_irq_thread(int irq, void *arg) ...@@ -1936,6 +2132,14 @@ static irqreturn_t hdmi_irq_thread(int irq, void *arg)
if (hdata->drm_dev) if (hdata->drm_dev)
drm_helper_hpd_irq_event(hdata->drm_dev); drm_helper_hpd_irq_event(hdata->drm_dev);
}
static irqreturn_t hdmi_irq_thread(int irq, void *arg)
{
struct hdmi_context *hdata = arg;
mod_delayed_work(system_wq, &hdata->hotplug_work,
msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -1954,37 +2158,35 @@ static int hdmi_resources_init(struct hdmi_context *hdata) ...@@ -1954,37 +2158,35 @@ static int hdmi_resources_init(struct hdmi_context *hdata)
DRM_DEBUG_KMS("HDMI resource init\n"); DRM_DEBUG_KMS("HDMI resource init\n");
memset(res, 0, sizeof(*res));
/* get clocks, power */ /* get clocks, power */
res->hdmi = devm_clk_get(dev, "hdmi"); res->hdmi = devm_clk_get(dev, "hdmi");
if (IS_ERR(res->hdmi)) { if (IS_ERR(res->hdmi)) {
DRM_ERROR("failed to get clock 'hdmi'\n"); DRM_ERROR("failed to get clock 'hdmi'\n");
ret = PTR_ERR(res->hdmi);
goto fail; goto fail;
} }
res->sclk_hdmi = devm_clk_get(dev, "sclk_hdmi"); res->sclk_hdmi = devm_clk_get(dev, "sclk_hdmi");
if (IS_ERR(res->sclk_hdmi)) { if (IS_ERR(res->sclk_hdmi)) {
DRM_ERROR("failed to get clock 'sclk_hdmi'\n"); DRM_ERROR("failed to get clock 'sclk_hdmi'\n");
ret = PTR_ERR(res->sclk_hdmi);
goto fail; goto fail;
} }
res->sclk_pixel = devm_clk_get(dev, "sclk_pixel"); res->sclk_pixel = devm_clk_get(dev, "sclk_pixel");
if (IS_ERR(res->sclk_pixel)) { if (IS_ERR(res->sclk_pixel)) {
DRM_ERROR("failed to get clock 'sclk_pixel'\n"); DRM_ERROR("failed to get clock 'sclk_pixel'\n");
ret = PTR_ERR(res->sclk_pixel);
goto fail; goto fail;
} }
res->sclk_hdmiphy = devm_clk_get(dev, "sclk_hdmiphy"); res->sclk_hdmiphy = devm_clk_get(dev, "sclk_hdmiphy");
if (IS_ERR(res->sclk_hdmiphy)) { if (IS_ERR(res->sclk_hdmiphy)) {
DRM_ERROR("failed to get clock 'sclk_hdmiphy'\n"); DRM_ERROR("failed to get clock 'sclk_hdmiphy'\n");
goto fail; ret = PTR_ERR(res->sclk_hdmiphy);
}
res->hdmiphy = devm_clk_get(dev, "hdmiphy");
if (IS_ERR(res->hdmiphy)) {
DRM_ERROR("failed to get clock 'hdmiphy'\n");
goto fail; goto fail;
} }
res->mout_hdmi = devm_clk_get(dev, "mout_hdmi"); res->mout_hdmi = devm_clk_get(dev, "mout_hdmi");
if (IS_ERR(res->mout_hdmi)) { if (IS_ERR(res->mout_hdmi)) {
DRM_ERROR("failed to get clock 'mout_hdmi'\n"); DRM_ERROR("failed to get clock 'mout_hdmi'\n");
ret = PTR_ERR(res->mout_hdmi);
goto fail; goto fail;
} }
...@@ -1992,8 +2194,10 @@ static int hdmi_resources_init(struct hdmi_context *hdata) ...@@ -1992,8 +2194,10 @@ static int hdmi_resources_init(struct hdmi_context *hdata)
res->regul_bulk = devm_kzalloc(dev, ARRAY_SIZE(supply) * res->regul_bulk = devm_kzalloc(dev, ARRAY_SIZE(supply) *
sizeof(res->regul_bulk[0]), GFP_KERNEL); sizeof(res->regul_bulk[0]), GFP_KERNEL);
if (!res->regul_bulk) if (!res->regul_bulk) {
ret = -ENOMEM;
goto fail; goto fail;
}
for (i = 0; i < ARRAY_SIZE(supply); ++i) { for (i = 0; i < ARRAY_SIZE(supply); ++i) {
res->regul_bulk[i].supply = supply[i]; res->regul_bulk[i].supply = supply[i];
res->regul_bulk[i].consumer = NULL; res->regul_bulk[i].consumer = NULL;
...@@ -2001,14 +2205,14 @@ static int hdmi_resources_init(struct hdmi_context *hdata) ...@@ -2001,14 +2205,14 @@ static int hdmi_resources_init(struct hdmi_context *hdata)
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(supply), res->regul_bulk); ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(supply), res->regul_bulk);
if (ret) { if (ret) {
DRM_ERROR("failed to get regulators\n"); DRM_ERROR("failed to get regulators\n");
goto fail; return ret;
} }
res->regul_count = ARRAY_SIZE(supply); res->regul_count = ARRAY_SIZE(supply);
return 0; return ret;
fail: fail:
DRM_ERROR("HDMI resource init - failed\n"); DRM_ERROR("HDMI resource init - failed\n");
return -ENODEV; return ret;
} }
static struct s5p_hdmi_platform_data *drm_hdmi_dt_parse_pdata static struct s5p_hdmi_platform_data *drm_hdmi_dt_parse_pdata
...@@ -2042,43 +2246,106 @@ static struct of_device_id hdmi_match_types[] = { ...@@ -2042,43 +2246,106 @@ static struct of_device_id hdmi_match_types[] = {
}, { }, {
.compatible = "samsung,exynos4212-hdmi", .compatible = "samsung,exynos4212-hdmi",
.data = &exynos4212_hdmi_driver_data, .data = &exynos4212_hdmi_driver_data,
}, {
.compatible = "samsung,exynos5420-hdmi",
.data = &exynos5420_hdmi_driver_data,
}, { }, {
/* end node */ /* end node */
} }
}; };
static int hdmi_bind(struct device *dev, struct device *master, void *data)
{
struct drm_device *drm_dev = data;
struct hdmi_context *hdata;
hdata = hdmi_display.ctx;
hdata->drm_dev = drm_dev;
return exynos_drm_create_enc_conn(drm_dev, &hdmi_display);
}
static void hdmi_unbind(struct device *dev, struct device *master, void *data)
{
struct exynos_drm_display *display = get_hdmi_display(dev);
struct drm_encoder *encoder = display->encoder;
struct hdmi_context *hdata = display->ctx;
encoder->funcs->destroy(encoder);
drm_connector_cleanup(&hdata->connector);
}
static const struct component_ops hdmi_component_ops = {
.bind = hdmi_bind,
.unbind = hdmi_unbind,
};
static struct device_node *hdmi_legacy_ddc_dt_binding(struct device *dev)
{
const char *compatible_str = "samsung,exynos4210-hdmiddc";
struct device_node *np;
np = of_find_compatible_node(NULL, NULL, compatible_str);
if (np)
return of_get_next_parent(np);
return NULL;
}
static struct device_node *hdmi_legacy_phy_dt_binding(struct device *dev)
{
const char *compatible_str = "samsung,exynos4212-hdmiphy";
return of_find_compatible_node(NULL, NULL, compatible_str);
}
static int hdmi_probe(struct platform_device *pdev) static int hdmi_probe(struct platform_device *pdev)
{ {
struct device_node *ddc_node, *phy_node;
struct s5p_hdmi_platform_data *pdata;
struct hdmi_driver_data *drv_data;
const struct of_device_id *match;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct hdmi_context *hdata; struct hdmi_context *hdata;
struct s5p_hdmi_platform_data *pdata;
struct resource *res; struct resource *res;
const struct of_device_id *match;
struct device_node *ddc_node, *phy_node;
struct hdmi_driver_data *drv_data;
int ret; int ret;
if (!dev->of_node) ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
return -ENODEV; hdmi_display.type);
if (ret)
return ret;
if (!dev->of_node) {
ret = -ENODEV;
goto err_del_component;
}
pdata = drm_hdmi_dt_parse_pdata(dev); pdata = drm_hdmi_dt_parse_pdata(dev);
if (!pdata) if (!pdata) {
return -EINVAL; ret = -EINVAL;
goto err_del_component;
}
hdata = devm_kzalloc(dev, sizeof(struct hdmi_context), GFP_KERNEL); hdata = devm_kzalloc(dev, sizeof(struct hdmi_context), GFP_KERNEL);
if (!hdata) if (!hdata) {
return -ENOMEM; ret = -ENOMEM;
goto err_del_component;
}
mutex_init(&hdata->hdmi_mutex); mutex_init(&hdata->hdmi_mutex);
platform_set_drvdata(pdev, &hdmi_display); platform_set_drvdata(pdev, &hdmi_display);
match = of_match_node(hdmi_match_types, dev->of_node); match = of_match_node(hdmi_match_types, dev->of_node);
if (!match) if (!match) {
return -ENODEV; ret = -ENODEV;
goto err_del_component;
}
drv_data = (struct hdmi_driver_data *)match->data; drv_data = (struct hdmi_driver_data *)match->data;
hdata->type = drv_data->type; hdata->type = drv_data->type;
hdata->phy_confs = drv_data->phy_confs;
hdata->phy_conf_count = drv_data->phy_conf_count;
hdata->hpd_gpio = pdata->hpd_gpio; hdata->hpd_gpio = pdata->hpd_gpio;
hdata->dev = dev; hdata->dev = dev;
...@@ -2086,35 +2353,44 @@ static int hdmi_probe(struct platform_device *pdev) ...@@ -2086,35 +2353,44 @@ static int hdmi_probe(struct platform_device *pdev)
ret = hdmi_resources_init(hdata); ret = hdmi_resources_init(hdata);
if (ret) { if (ret) {
DRM_ERROR("hdmi_resources_init failed\n"); DRM_ERROR("hdmi_resources_init failed\n");
return -EINVAL; return ret;
} }
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
hdata->regs = devm_ioremap_resource(dev, res); hdata->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(hdata->regs)) if (IS_ERR(hdata->regs)) {
return PTR_ERR(hdata->regs); ret = PTR_ERR(hdata->regs);
goto err_del_component;
}
ret = devm_gpio_request(dev, hdata->hpd_gpio, "HPD"); ret = devm_gpio_request(dev, hdata->hpd_gpio, "HPD");
if (ret) { if (ret) {
DRM_ERROR("failed to request HPD gpio\n"); DRM_ERROR("failed to request HPD gpio\n");
return ret; goto err_del_component;
} }
ddc_node = hdmi_legacy_ddc_dt_binding(dev);
if (ddc_node)
goto out_get_ddc_adpt;
/* DDC i2c driver */ /* DDC i2c driver */
ddc_node = of_parse_phandle(dev->of_node, "ddc", 0); ddc_node = of_parse_phandle(dev->of_node, "ddc", 0);
if (!ddc_node) { if (!ddc_node) {
DRM_ERROR("Failed to find ddc node in device tree\n"); DRM_ERROR("Failed to find ddc node in device tree\n");
return -ENODEV; ret = -ENODEV;
goto err_del_component;
} }
out_get_ddc_adpt:
hdata->ddc_adpt = of_find_i2c_adapter_by_node(ddc_node); hdata->ddc_adpt = of_find_i2c_adapter_by_node(ddc_node);
if (!hdata->ddc_adpt) { if (!hdata->ddc_adpt) {
DRM_ERROR("Failed to get ddc i2c adapter by node\n"); DRM_ERROR("Failed to get ddc i2c adapter by node\n");
return -ENODEV; return -EPROBE_DEFER;
} }
/* Not support APB PHY yet. */ phy_node = hdmi_legacy_phy_dt_binding(dev);
if (drv_data->is_apb_phy) if (phy_node)
return -EPERM; goto out_get_phy_port;
/* hdmiphy i2c driver */ /* hdmiphy i2c driver */
phy_node = of_parse_phandle(dev->of_node, "phy", 0); phy_node = of_parse_phandle(dev->of_node, "phy", 0);
...@@ -2123,11 +2399,22 @@ static int hdmi_probe(struct platform_device *pdev) ...@@ -2123,11 +2399,22 @@ static int hdmi_probe(struct platform_device *pdev)
ret = -ENODEV; ret = -ENODEV;
goto err_ddc; goto err_ddc;
} }
hdata->hdmiphy_port = of_find_i2c_device_by_node(phy_node);
if (!hdata->hdmiphy_port) { out_get_phy_port:
DRM_ERROR("Failed to get hdmi phy i2c client from node\n"); if (drv_data->is_apb_phy) {
ret = -ENODEV; hdata->regs_hdmiphy = of_iomap(phy_node, 0);
goto err_ddc; if (!hdata->regs_hdmiphy) {
DRM_ERROR("failed to ioremap hdmi phy\n");
ret = -ENOMEM;
goto err_ddc;
}
} else {
hdata->hdmiphy_port = of_find_i2c_device_by_node(phy_node);
if (!hdata->hdmiphy_port) {
DRM_ERROR("Failed to get hdmi phy i2c client\n");
ret = -EPROBE_DEFER;
goto err_ddc;
}
} }
hdata->irq = gpio_to_irq(hdata->hpd_gpio); hdata->irq = gpio_to_irq(hdata->hpd_gpio);
...@@ -2139,6 +2426,8 @@ static int hdmi_probe(struct platform_device *pdev) ...@@ -2139,6 +2426,8 @@ static int hdmi_probe(struct platform_device *pdev)
hdata->hpd = gpio_get_value(hdata->hpd_gpio); hdata->hpd = gpio_get_value(hdata->hpd_gpio);
INIT_DELAYED_WORK(&hdata->hotplug_work, hdmi_hotplug_work_func);
ret = devm_request_threaded_irq(dev, hdata->irq, NULL, ret = devm_request_threaded_irq(dev, hdata->irq, NULL,
hdmi_irq_thread, IRQF_TRIGGER_RISING | hdmi_irq_thread, IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING | IRQF_ONESHOT, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
...@@ -2148,30 +2437,51 @@ static int hdmi_probe(struct platform_device *pdev) ...@@ -2148,30 +2437,51 @@ static int hdmi_probe(struct platform_device *pdev)
goto err_hdmiphy; goto err_hdmiphy;
} }
pm_runtime_enable(dev); hdata->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
"samsung,syscon-phandle");
if (IS_ERR(hdata->pmureg)) {
DRM_ERROR("syscon regmap lookup failed.\n");
ret = -EPROBE_DEFER;
goto err_hdmiphy;
}
pm_runtime_enable(dev);
hdmi_display.ctx = hdata; hdmi_display.ctx = hdata;
exynos_drm_display_register(&hdmi_display);
return 0; ret = component_add(&pdev->dev, &hdmi_component_ops);
if (ret)
goto err_disable_pm_runtime;
return ret;
err_disable_pm_runtime:
pm_runtime_disable(dev);
err_hdmiphy: err_hdmiphy:
put_device(&hdata->hdmiphy_port->dev); if (hdata->hdmiphy_port)
put_device(&hdata->hdmiphy_port->dev);
err_ddc: err_ddc:
put_device(&hdata->ddc_adpt->dev); put_device(&hdata->ddc_adpt->dev);
err_del_component:
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
return ret; return ret;
} }
static int hdmi_remove(struct platform_device *pdev) static int hdmi_remove(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct hdmi_context *hdata = hdmi_display.ctx;
struct exynos_drm_display *display = get_hdmi_display(dev);
struct hdmi_context *hdata = display->ctx; cancel_delayed_work_sync(&hdata->hotplug_work);
put_device(&hdata->hdmiphy_port->dev); put_device(&hdata->hdmiphy_port->dev);
put_device(&hdata->ddc_adpt->dev); put_device(&hdata->ddc_adpt->dev);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
component_del(&pdev->dev, &hdmi_component_ops);
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
return 0; return 0;
} }
......
/*
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Authors:
* Inki Dae <inki.dae@samsung.com>
* Seung-Woo Kim <sw0312.kim@samsung.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#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)
{
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);
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)
{ {
dev_info(&pdev->dev, "remove successful\n"); int ret;
pm_runtime_disable(&pdev->dev); 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