mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-30 10:36:39 +07:00
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 ...
This commit is contained in:
commit
b33a51e457
@ -62,6 +62,10 @@ Optional properties for dp-controller:
|
||||
-hsync-active-high:
|
||||
HSYNC polarity configuration.
|
||||
High if defined, Low if not defined
|
||||
-samsung,hpd-gpio:
|
||||
Hotplug detect GPIO.
|
||||
Indicates which GPIO should be used for hotplug
|
||||
detection
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -5,6 +5,7 @@ Required properties:
|
||||
1) "samsung,exynos5-hdmi" <DEPRECATED>
|
||||
2) "samsung,exynos4210-hdmi"
|
||||
3) "samsung,exynos4212-hdmi"
|
||||
4) "samsung,exynos5420-hdmi"
|
||||
- reg: physical base address of the hdmi and length of memory mapped
|
||||
region.
|
||||
- interrupts: interrupt number to the cpu.
|
||||
@ -27,6 +28,7 @@ Required properties:
|
||||
"hdmi", "sclk_hdmi", "sclk_pixel", "sclk_hdmiphy" and "mout_hdmi".
|
||||
- ddc: phandle to the hdmi ddc node
|
||||
- phy: phandle to the hdmi phy node
|
||||
- samsung,syscon-phandle: phandle for system controller node for PMU.
|
||||
|
||||
Example:
|
||||
|
||||
@ -37,4 +39,5 @@ Example:
|
||||
hpd-gpio = <&gpx3 7 1>;
|
||||
ddc = <&hdmi_ddc_node>;
|
||||
phy = <&hdmi_phy_node>;
|
||||
samsung,syscon-phandle = <&pmu_system_controller>;
|
||||
};
|
||||
|
@ -83,6 +83,8 @@ config DRM_KMS_CMA_HELPER
|
||||
|
||||
source "drivers/gpu/drm/i2c/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/bridge/Kconfig"
|
||||
|
||||
config DRM_TDFX
|
||||
tristate "3dfx Banshee/Voodoo3+"
|
||||
depends on DRM && PCI
|
||||
@ -199,5 +201,3 @@ source "drivers/gpu/drm/msm/Kconfig"
|
||||
source "drivers/gpu/drm/tegra/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/panel/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/bridge/Kconfig"
|
||||
|
@ -26,14 +26,14 @@ config DRM_EXYNOS_DMABUF
|
||||
|
||||
config DRM_EXYNOS_FIMD
|
||||
bool "Exynos DRM FIMD"
|
||||
depends on DRM_EXYNOS && !FB_S3C && !ARCH_MULTIPLATFORM
|
||||
depends on DRM_EXYNOS && !FB_S3C
|
||||
select FB_MODE_HELPERS
|
||||
help
|
||||
Choose this option if you want to use Exynos FIMD for DRM.
|
||||
|
||||
config DRM_EXYNOS_DPI
|
||||
bool "EXYNOS DRM parallel output support"
|
||||
depends on DRM_EXYNOS
|
||||
depends on DRM_EXYNOS_FIMD
|
||||
select DRM_PANEL
|
||||
default n
|
||||
help
|
||||
@ -41,7 +41,7 @@ config DRM_EXYNOS_DPI
|
||||
|
||||
config DRM_EXYNOS_DSI
|
||||
bool "EXYNOS DRM MIPI-DSI driver support"
|
||||
depends on DRM_EXYNOS
|
||||
depends on DRM_EXYNOS_FIMD
|
||||
select DRM_MIPI_DSI
|
||||
select DRM_PANEL
|
||||
default n
|
||||
@ -50,7 +50,7 @@ config DRM_EXYNOS_DSI
|
||||
|
||||
config DRM_EXYNOS_DP
|
||||
bool "EXYNOS DRM DP driver support"
|
||||
depends on DRM_EXYNOS && ARCH_EXYNOS
|
||||
depends on DRM_EXYNOS_FIMD && ARCH_EXYNOS && (DRM_PTN3460=n || DRM_PTN3460=y || DRM_PTN3460=DRM_EXYNOS)
|
||||
default DRM_EXYNOS
|
||||
help
|
||||
This enables support for DP device.
|
||||
|
@ -1,63 +0,0 @@
|
||||
/*
|
||||
* 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 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/component.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <video/of_display_timing.h>
|
||||
#include <video/of_videomode.h>
|
||||
@ -141,15 +144,15 @@ static int exynos_dp_read_edid(struct exynos_dp_device *dp)
|
||||
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);
|
||||
if (test_vector & DPCD_TEST_EDID_READ) {
|
||||
if (test_vector & DP_TEST_LINK_EDID_READ) {
|
||||
exynos_dp_write_byte_to_dpcd(dp,
|
||||
DPCD_ADDR_TEST_EDID_CHECKSUM,
|
||||
DP_TEST_EDID_CHECKSUM,
|
||||
edid[EDID_BLOCK_LENGTH + EDID_CHECKSUM]);
|
||||
exynos_dp_write_byte_to_dpcd(dp,
|
||||
DPCD_ADDR_TEST_RESPONSE,
|
||||
DPCD_TEST_EDID_CHECKSUM_WRITE);
|
||||
DP_TEST_RESPONSE,
|
||||
DP_TEST_EDID_CHECKSUM_WRITE);
|
||||
}
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
|
||||
exynos_dp_read_byte_from_dpcd(dp,
|
||||
DPCD_ADDR_TEST_REQUEST,
|
||||
DP_TEST_REQUEST,
|
||||
&test_vector);
|
||||
if (test_vector & DPCD_TEST_EDID_READ) {
|
||||
if (test_vector & DP_TEST_LINK_EDID_READ) {
|
||||
exynos_dp_write_byte_to_dpcd(dp,
|
||||
DPCD_ADDR_TEST_EDID_CHECKSUM,
|
||||
DP_TEST_EDID_CHECKSUM,
|
||||
edid[EDID_CHECKSUM]);
|
||||
exynos_dp_write_byte_to_dpcd(dp,
|
||||
DPCD_ADDR_TEST_RESPONSE,
|
||||
DPCD_TEST_EDID_CHECKSUM_WRITE);
|
||||
DP_TEST_RESPONSE,
|
||||
DP_TEST_EDID_CHECKSUM_WRITE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -193,8 +196,8 @@ static int exynos_dp_handle_edid(struct exynos_dp_device *dp)
|
||||
int i;
|
||||
int retval;
|
||||
|
||||
/* Read DPCD DPCD_ADDR_DPCD_REV~RECEIVE_PORT1_CAP_1 */
|
||||
retval = exynos_dp_read_bytes_from_dpcd(dp, DPCD_ADDR_DPCD_REV,
|
||||
/* Read DPCD DP_DPCD_REV~RECEIVE_PORT1_CAP_1 */
|
||||
retval = exynos_dp_read_bytes_from_dpcd(dp, DP_DPCD_REV,
|
||||
12, buf);
|
||||
if (retval)
|
||||
return retval;
|
||||
@ -214,14 +217,14 @@ static void exynos_dp_enable_rx_to_enhanced_mode(struct exynos_dp_device *dp,
|
||||
{
|
||||
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)
|
||||
exynos_dp_write_byte_to_dpcd(dp, DPCD_ADDR_LANE_COUNT_SET,
|
||||
DPCD_ENHANCED_FRAME_EN |
|
||||
exynos_dp_write_byte_to_dpcd(dp, DP_LANE_COUNT_SET,
|
||||
DP_LANE_COUNT_ENHANCED_FRAME_EN |
|
||||
DPCD_LANE_COUNT_SET(data));
|
||||
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));
|
||||
}
|
||||
|
||||
@ -230,7 +233,7 @@ static int exynos_dp_is_enhanced_mode_available(struct exynos_dp_device *dp)
|
||||
u8 data;
|
||||
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);
|
||||
|
||||
return retval;
|
||||
@ -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_write_byte_to_dpcd(dp,
|
||||
DPCD_ADDR_TRAINING_PATTERN_SET,
|
||||
DPCD_TRAINING_PATTERN_DISABLED);
|
||||
DP_TRAINING_PATTERN_SET,
|
||||
DP_TRAINING_PATTERN_DISABLE);
|
||||
}
|
||||
|
||||
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)
|
||||
/* Setup RX configuration */
|
||||
buf[0] = dp->link_train.link_rate;
|
||||
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);
|
||||
if (retval)
|
||||
return retval;
|
||||
@ -322,16 +325,16 @@ static int exynos_dp_link_start(struct exynos_dp_device *dp)
|
||||
|
||||
/* Set RX training pattern */
|
||||
retval = exynos_dp_write_byte_to_dpcd(dp,
|
||||
DPCD_ADDR_TRAINING_PATTERN_SET,
|
||||
DPCD_SCRAMBLING_DISABLED | DPCD_TRAINING_PATTERN_1);
|
||||
DP_TRAINING_PATTERN_SET,
|
||||
DP_LINK_SCRAMBLING_DISABLE | DP_TRAINING_PATTERN_1);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
for (lane = 0; lane < lane_count; lane++)
|
||||
buf[lane] = DPCD_PRE_EMPHASIS_PATTERN2_LEVEL0 |
|
||||
DPCD_VOLTAGE_SWING_PATTERN1_LEVEL0;
|
||||
buf[lane] = DP_TRAIN_PRE_EMPHASIS_0 |
|
||||
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);
|
||||
|
||||
return retval;
|
||||
@ -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++) {
|
||||
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 0;
|
||||
@ -364,13 +367,13 @@ static int exynos_dp_channel_eq_ok(u8 link_status[2], u8 link_align,
|
||||
int lane;
|
||||
u8 lane_status;
|
||||
|
||||
if ((link_align & DPCD_INTERLANE_ALIGN_DONE) == 0)
|
||||
if ((link_align & DP_INTERLANE_ALIGN_DONE) == 0)
|
||||
return -EINVAL;
|
||||
|
||||
for (lane = 0; lane < lane_count; lane++) {
|
||||
lane_status = exynos_dp_get_lane_status(link_status, lane);
|
||||
lane_status &= DPCD_CHANNEL_EQ_BITS;
|
||||
if (lane_status != DPCD_CHANNEL_EQ_BITS)
|
||||
lane_status &= DP_CHANNEL_EQ_BITS;
|
||||
if (lane_status != DP_CHANNEL_EQ_BITS)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -468,9 +471,9 @@ static void exynos_dp_get_adjust_training_lane(struct exynos_dp_device *dp,
|
||||
DPCD_PRE_EMPHASIS_SET(pre_emphasis);
|
||||
|
||||
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)
|
||||
training_lane |= DPCD_MAX_PRE_EMPHASIS_REACHED;
|
||||
training_lane |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
|
||||
|
||||
dp->link_train.training_lane[lane] = training_lane;
|
||||
}
|
||||
@ -487,12 +490,12 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp)
|
||||
lane_count = dp->link_train.lane_count;
|
||||
|
||||
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)
|
||||
return retval;
|
||||
|
||||
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)
|
||||
return retval;
|
||||
|
||||
@ -501,9 +504,9 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp)
|
||||
exynos_dp_set_training_pattern(dp, TRAINING_PTN2);
|
||||
|
||||
retval = exynos_dp_write_byte_to_dpcd(dp,
|
||||
DPCD_ADDR_TRAINING_PATTERN_SET,
|
||||
DPCD_SCRAMBLING_DISABLED |
|
||||
DPCD_TRAINING_PATTERN_2);
|
||||
DP_TRAINING_PATTERN_SET,
|
||||
DP_LINK_SCRAMBLING_DISABLE |
|
||||
DP_TRAINING_PATTERN_2);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
@ -543,7 +546,7 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp)
|
||||
dp->link_train.training_lane[lane], lane);
|
||||
|
||||
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);
|
||||
if (retval)
|
||||
return retval;
|
||||
@ -562,7 +565,7 @@ static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp)
|
||||
lane_count = dp->link_train.lane_count;
|
||||
|
||||
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)
|
||||
return retval;
|
||||
|
||||
@ -572,12 +575,12 @@ static int exynos_dp_process_equalizer_training(struct exynos_dp_device *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)
|
||||
return retval;
|
||||
|
||||
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)
|
||||
return retval;
|
||||
|
||||
@ -619,7 +622,7 @@ static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp)
|
||||
exynos_dp_set_lane_link_training(dp,
|
||||
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);
|
||||
|
||||
return retval;
|
||||
@ -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
|
||||
* 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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
* 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);
|
||||
}
|
||||
|
||||
@ -819,20 +822,20 @@ static void exynos_dp_enable_scramble(struct exynos_dp_device *dp, bool enable)
|
||||
exynos_dp_enable_scrambling(dp);
|
||||
|
||||
exynos_dp_read_byte_from_dpcd(dp,
|
||||
DPCD_ADDR_TRAINING_PATTERN_SET,
|
||||
DP_TRAINING_PATTERN_SET,
|
||||
&data);
|
||||
exynos_dp_write_byte_to_dpcd(dp,
|
||||
DPCD_ADDR_TRAINING_PATTERN_SET,
|
||||
(u8)(data & ~DPCD_SCRAMBLING_DISABLED));
|
||||
DP_TRAINING_PATTERN_SET,
|
||||
(u8)(data & ~DP_LINK_SCRAMBLING_DISABLE));
|
||||
} else {
|
||||
exynos_dp_disable_scrambling(dp);
|
||||
|
||||
exynos_dp_read_byte_from_dpcd(dp,
|
||||
DPCD_ADDR_TRAINING_PATTERN_SET,
|
||||
DP_TRAINING_PATTERN_SET,
|
||||
&data);
|
||||
exynos_dp_write_byte_to_dpcd(dp,
|
||||
DPCD_ADDR_TRAINING_PATTERN_SET,
|
||||
(u8)(data | DPCD_SCRAMBLING_DISABLED));
|
||||
DP_TRAINING_PATTERN_SET,
|
||||
(u8)(data | DP_LINK_SCRAMBLING_DISABLE));
|
||||
}
|
||||
}
|
||||
|
||||
@ -962,16 +965,6 @@ static struct drm_connector_helper_funcs exynos_dp_connector_helper_funcs = {
|
||||
.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)
|
||||
{
|
||||
bridge->client = NULL;
|
||||
@ -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 = {
|
||||
.initialize = exynos_dp_initialize,
|
||||
.create_connector = exynos_dp_create_connector,
|
||||
.dpms = exynos_dp_dpms,
|
||||
};
|
||||
@ -1116,10 +1108,8 @@ static struct video_info *exynos_dp_dt_parse_pdata(struct device *dev)
|
||||
|
||||
dp_video_config = devm_kzalloc(dev,
|
||||
sizeof(*dp_video_config), GFP_KERNEL);
|
||||
if (!dp_video_config) {
|
||||
dev_err(dev, "memory allocation for video config failed\n");
|
||||
if (!dp_video_config)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
dp_video_config->h_sync_polarity =
|
||||
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)
|
||||
dp_phy_node = of_find_node_by_name(dp_phy_node, "dptx-phy");
|
||||
if (!dp_phy_node) {
|
||||
dp->phy = devm_phy_get(dp->dev, "dp");
|
||||
if (IS_ERR(dp->phy))
|
||||
return PTR_ERR(dp->phy);
|
||||
else
|
||||
return 0;
|
||||
return PTR_ERR_OR_ZERO(dp->phy);
|
||||
}
|
||||
|
||||
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)
|
||||
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 exynos_dp_device *dp;
|
||||
unsigned int irq_flags;
|
||||
|
||||
int ret = 0;
|
||||
|
||||
dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device),
|
||||
GFP_KERNEL);
|
||||
if (!dp) {
|
||||
dev_err(&pdev->dev, "no memory for device data\n");
|
||||
if (!dp)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dp->dev = &pdev->dev;
|
||||
dp->dpms_mode = DRM_MODE_DPMS_OFF;
|
||||
@ -1266,7 +1254,30 @@ static int exynos_dp_probe(struct platform_device *pdev)
|
||||
if (IS_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) {
|
||||
dev_err(&pdev->dev, "failed to get irq\n");
|
||||
return -ENODEV;
|
||||
@ -1278,28 +1289,61 @@ static int exynos_dp_probe(struct platform_device *pdev)
|
||||
|
||||
exynos_dp_init_dp(dp);
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, dp->irq, exynos_dp_irq_handler, 0,
|
||||
"exynos-dp", dp);
|
||||
ret = devm_request_irq(&pdev->dev, dp->irq, exynos_dp_irq_handler,
|
||||
irq_flags, "exynos-dp", dp);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to request irq\n");
|
||||
return ret;
|
||||
}
|
||||
disable_irq(dp->irq);
|
||||
|
||||
dp->drm_dev = drm_dev;
|
||||
exynos_dp_display.ctx = dp;
|
||||
|
||||
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 void exynos_dp_unbind(struct device *dev, struct device *master,
|
||||
void *data)
|
||||
{
|
||||
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);
|
||||
|
||||
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)
|
||||
{
|
||||
struct exynos_drm_display *display = platform_get_drvdata(pdev);
|
||||
|
||||
exynos_dp_dpms(display, DRM_MODE_DPMS_OFF);
|
||||
exynos_drm_display_unregister(&exynos_dp_display);
|
||||
component_del(&pdev->dev, &exynos_dp_ops);
|
||||
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -14,6 +14,7 @@
|
||||
#define _EXYNOS_DP_CORE_H
|
||||
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_dp_helper.h>
|
||||
#include <drm/exynos_drm.h>
|
||||
|
||||
#define DP_TIMEOUT_LOOP_COUNT 100
|
||||
@ -159,6 +160,7 @@ struct exynos_dp_device {
|
||||
struct work_struct hotplug_work;
|
||||
struct phy *phy;
|
||||
int dpms_mode;
|
||||
int hpd_gpio;
|
||||
|
||||
struct exynos_drm_panel_info panel;
|
||||
};
|
||||
@ -261,69 +263,17 @@ void exynos_dp_disable_scrambling(struct exynos_dp_device *dp);
|
||||
#define EDID_EXTENSION_FLAG 0x7e
|
||||
#define EDID_CHECKSUM 0x7f
|
||||
|
||||
/* Definition for DPCD Register */
|
||||
#define DPCD_ADDR_DPCD_REV 0x0000
|
||||
#define DPCD_ADDR_MAX_LINK_RATE 0x0001
|
||||
#define DPCD_ADDR_MAX_LANE_COUNT 0x0002
|
||||
#define DPCD_ADDR_LINK_BW_SET 0x0100
|
||||
#define DPCD_ADDR_LANE_COUNT_SET 0x0101
|
||||
#define DPCD_ADDR_TRAINING_PATTERN_SET 0x0102
|
||||
#define DPCD_ADDR_TRAINING_LANE0_SET 0x0103
|
||||
#define DPCD_ADDR_LANE0_1_STATUS 0x0202
|
||||
#define DPCD_ADDR_LANE_ALIGN_STATUS_UPDATED 0x0204
|
||||
#define DPCD_ADDR_ADJUST_REQUEST_LANE0_1 0x0206
|
||||
#define DPCD_ADDR_ADJUST_REQUEST_LANE2_3 0x0207
|
||||
#define DPCD_ADDR_TEST_REQUEST 0x0218
|
||||
#define DPCD_ADDR_TEST_RESPONSE 0x0260
|
||||
#define DPCD_ADDR_TEST_EDID_CHECKSUM 0x0261
|
||||
#define DPCD_ADDR_SINK_POWER_STATE 0x0600
|
||||
|
||||
/* DPCD_ADDR_MAX_LANE_COUNT */
|
||||
/* DP_MAX_LANE_COUNT */
|
||||
#define DPCD_ENHANCED_FRAME_CAP(x) (((x) >> 7) & 0x1)
|
||||
#define DPCD_MAX_LANE_COUNT(x) ((x) & 0x1f)
|
||||
|
||||
/* DPCD_ADDR_LANE_COUNT_SET */
|
||||
#define DPCD_ENHANCED_FRAME_EN (0x1 << 7)
|
||||
/* DP_LANE_COUNT_SET */
|
||||
#define DPCD_LANE_COUNT_SET(x) ((x) & 0x1f)
|
||||
|
||||
/* DPCD_ADDR_TRAINING_PATTERN_SET */
|
||||
#define DPCD_SCRAMBLING_DISABLED (0x1 << 5)
|
||||
#define DPCD_SCRAMBLING_ENABLED (0x0 << 5)
|
||||
#define DPCD_TRAINING_PATTERN_2 (0x2 << 0)
|
||||
#define DPCD_TRAINING_PATTERN_1 (0x1 << 0)
|
||||
#define DPCD_TRAINING_PATTERN_DISABLED (0x0 << 0)
|
||||
|
||||
/* DPCD_ADDR_TRAINING_LANE0_SET */
|
||||
#define DPCD_MAX_PRE_EMPHASIS_REACHED (0x1 << 5)
|
||||
/* DP_TRAINING_LANE0_SET */
|
||||
#define DPCD_PRE_EMPHASIS_SET(x) (((x) & 0x3) << 3)
|
||||
#define DPCD_PRE_EMPHASIS_GET(x) (((x) >> 3) & 0x3)
|
||||
#define DPCD_PRE_EMPHASIS_PATTERN2_LEVEL0 (0x0 << 3)
|
||||
#define DPCD_MAX_SWING_REACHED (0x1 << 2)
|
||||
#define DPCD_VOLTAGE_SWING_SET(x) (((x) & 0x3) << 0)
|
||||
#define DPCD_VOLTAGE_SWING_GET(x) (((x) >> 0) & 0x3)
|
||||
#define DPCD_VOLTAGE_SWING_PATTERN1_LEVEL0 (0x0 << 0)
|
||||
|
||||
/* DPCD_ADDR_LANE0_1_STATUS */
|
||||
#define DPCD_LANE_SYMBOL_LOCKED (0x1 << 2)
|
||||
#define DPCD_LANE_CHANNEL_EQ_DONE (0x1 << 1)
|
||||
#define DPCD_LANE_CR_DONE (0x1 << 0)
|
||||
#define DPCD_CHANNEL_EQ_BITS (DPCD_LANE_CR_DONE| \
|
||||
DPCD_LANE_CHANNEL_EQ_DONE|\
|
||||
DPCD_LANE_SYMBOL_LOCKED)
|
||||
|
||||
/* DPCD_ADDR_LANE_ALIGN__STATUS_UPDATED */
|
||||
#define DPCD_LINK_STATUS_UPDATED (0x1 << 7)
|
||||
#define DPCD_DOWNSTREAM_PORT_STATUS_CHANGED (0x1 << 6)
|
||||
#define DPCD_INTERLANE_ALIGN_DONE (0x1 << 0)
|
||||
|
||||
/* DPCD_ADDR_TEST_REQUEST */
|
||||
#define DPCD_TEST_EDID_READ (0x1 << 2)
|
||||
|
||||
/* DPCD_ADDR_TEST_RESPONSE */
|
||||
#define DPCD_TEST_EDID_CHECKSUM_WRITE (0x1 << 2)
|
||||
|
||||
/* DPCD_ADDR_SINK_POWER_STATE */
|
||||
#define DPCD_SET_POWER_STATE_D0 (0x1 << 0)
|
||||
#define DPCD_SET_POWER_STATE_D4 (0x2 << 0)
|
||||
|
||||
#endif /* _EXYNOS_DP_CORE_H */
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio.h>
|
||||
|
||||
#include "exynos_dp_core.h"
|
||||
#include "exynos_dp_reg.h"
|
||||
@ -326,6 +327,9 @@ void exynos_dp_clear_hotplug_interrupts(struct exynos_dp_device *dp)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
if (gpio_is_valid(dp->hpd_gpio))
|
||||
return;
|
||||
|
||||
reg = HOTPLUG_CHG | HPD_LOST | PLUG;
|
||||
writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4);
|
||||
|
||||
@ -337,6 +341,9 @@ void exynos_dp_init_hpd(struct exynos_dp_device *dp)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
if (gpio_is_valid(dp->hpd_gpio))
|
||||
return;
|
||||
|
||||
exynos_dp_clear_hotplug_interrupts(dp);
|
||||
|
||||
reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3);
|
||||
@ -348,19 +355,27 @@ enum dp_irq_type exynos_dp_get_irq_type(struct exynos_dp_device *dp)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
/* Parse hotplug interrupt status register */
|
||||
reg = readl(dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4);
|
||||
if (gpio_is_valid(dp->hpd_gpio)) {
|
||||
reg = gpio_get_value(dp->hpd_gpio);
|
||||
if (reg)
|
||||
return DP_IRQ_TYPE_HP_CABLE_IN;
|
||||
else
|
||||
return DP_IRQ_TYPE_HP_CABLE_OUT;
|
||||
} else {
|
||||
/* Parse hotplug interrupt status register */
|
||||
reg = readl(dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4);
|
||||
|
||||
if (reg & PLUG)
|
||||
return DP_IRQ_TYPE_HP_CABLE_IN;
|
||||
if (reg & PLUG)
|
||||
return DP_IRQ_TYPE_HP_CABLE_IN;
|
||||
|
||||
if (reg & HPD_LOST)
|
||||
return DP_IRQ_TYPE_HP_CABLE_OUT;
|
||||
if (reg & HPD_LOST)
|
||||
return DP_IRQ_TYPE_HP_CABLE_OUT;
|
||||
|
||||
if (reg & HOTPLUG_CHG)
|
||||
return DP_IRQ_TYPE_HP_CHANGE;
|
||||
if (reg & HOTPLUG_CHG)
|
||||
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)
|
||||
@ -386,7 +401,7 @@ void exynos_dp_init_aux(struct exynos_dp_device *dp)
|
||||
/* Disable AUX transaction H/W retry */
|
||||
reg = AUX_BIT_PERIOD_EXPECTED_DELAY(3) | AUX_HW_RETRY_COUNT_SEL(0)|
|
||||
AUX_HW_RETRY_INTERVAL_600_MICROSECONDS;
|
||||
writel(reg, dp->reg_base + EXYNOS_DP_AUX_HW_RETRY_CTL) ;
|
||||
writel(reg, dp->reg_base + EXYNOS_DP_AUX_HW_RETRY_CTL);
|
||||
|
||||
/* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */
|
||||
reg = DEFER_CTRL_EN | DEFER_COUNT(1);
|
||||
@ -402,9 +417,14 @@ int exynos_dp_get_plug_in_status(struct exynos_dp_device *dp)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3);
|
||||
if (reg & HPD_STATUS)
|
||||
return 0;
|
||||
if (gpio_is_valid(dp->hpd_gpio)) {
|
||||
if (gpio_get_value(dp->hpd_gpio))
|
||||
return 0;
|
||||
} else {
|
||||
reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3);
|
||||
if (reg & HPD_STATUS)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -19,21 +19,19 @@
|
||||
#include "exynos_drm_fbdev.h"
|
||||
|
||||
static LIST_HEAD(exynos_drm_subdrv_list);
|
||||
static LIST_HEAD(exynos_drm_manager_list);
|
||||
static LIST_HEAD(exynos_drm_display_list);
|
||||
|
||||
static int exynos_drm_create_enc_conn(struct drm_device *dev,
|
||||
int exynos_drm_create_enc_conn(struct drm_device *dev,
|
||||
struct exynos_drm_display *display)
|
||||
{
|
||||
struct drm_encoder *encoder;
|
||||
struct exynos_drm_manager *manager;
|
||||
int ret;
|
||||
unsigned long possible_crtcs = 0;
|
||||
|
||||
/* Find possible crtcs for this display */
|
||||
list_for_each_entry(manager, &exynos_drm_manager_list, list)
|
||||
if (manager->type == display->type)
|
||||
possible_crtcs |= 1 << manager->pipe;
|
||||
ret = exynos_drm_crtc_get_pipe_from_type(dev, display->type);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
possible_crtcs |= 1 << ret;
|
||||
|
||||
/* create and initialize a encoder for this sub driver. */
|
||||
encoder = exynos_drm_encoder_create(dev, display, possible_crtcs);
|
||||
@ -57,196 +55,6 @@ static int exynos_drm_create_enc_conn(struct drm_device *dev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int exynos_drm_subdrv_probe(struct drm_device *dev,
|
||||
struct exynos_drm_subdrv *subdrv)
|
||||
{
|
||||
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;
|
||||
int ret, pipe = 0;
|
||||
|
||||
list_for_each_entry(manager, &exynos_drm_manager_list, list) {
|
||||
if (manager->ops->initialize) {
|
||||
ret = manager->ops->initialize(manager, dev, pipe);
|
||||
if (ret) {
|
||||
DRM_ERROR("Mgr init [%d] failed with %d\n",
|
||||
manager->type, ret);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
manager->drm_dev = dev;
|
||||
manager->pipe = pipe++;
|
||||
|
||||
ret = exynos_drm_crtc_create(manager);
|
||||
if (ret) {
|
||||
DRM_ERROR("CRTC create [%d] failed with %d\n",
|
||||
manager->type, ret);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
err:
|
||||
list_for_each_entry_safe(manager, n, &exynos_drm_manager_list, list) {
|
||||
if (pipe-- > 0)
|
||||
exynos_drm_manager_unregister(manager);
|
||||
else
|
||||
list_del(&manager->list);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void exynos_drm_remove_managers(struct drm_device *dev)
|
||||
{
|
||||
struct exynos_drm_manager *manager, *n;
|
||||
|
||||
list_for_each_entry_safe(manager, n, &exynos_drm_manager_list, list)
|
||||
exynos_drm_manager_unregister(manager);
|
||||
}
|
||||
|
||||
int exynos_drm_initialize_displays(struct drm_device *dev)
|
||||
{
|
||||
struct exynos_drm_display *display, *n;
|
||||
int ret, initialized = 0;
|
||||
|
||||
list_for_each_entry(display, &exynos_drm_display_list, list) {
|
||||
if (display->ops->initialize) {
|
||||
ret = display->ops->initialize(display, dev);
|
||||
if (ret) {
|
||||
DRM_ERROR("Display init [%d] failed with %d\n",
|
||||
display->type, ret);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
initialized++;
|
||||
|
||||
ret = exynos_drm_create_enc_conn(dev, display);
|
||||
if (ret) {
|
||||
DRM_ERROR("Encoder create [%d] failed with %d\n",
|
||||
display->type, ret);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
err:
|
||||
list_for_each_entry_safe(display, n, &exynos_drm_display_list, list) {
|
||||
if (initialized-- > 0)
|
||||
exynos_drm_display_unregister(display);
|
||||
else
|
||||
list_del(&display->list);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void exynos_drm_remove_displays(struct drm_device *dev)
|
||||
{
|
||||
struct exynos_drm_display *display, *n;
|
||||
|
||||
list_for_each_entry_safe(display, n, &exynos_drm_display_list, list)
|
||||
exynos_drm_display_unregister(display);
|
||||
}
|
||||
|
||||
int exynos_drm_device_register(struct drm_device *dev)
|
||||
{
|
||||
struct exynos_drm_subdrv *subdrv, *n;
|
||||
int err;
|
||||
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
|
||||
list_for_each_entry_safe(subdrv, n, &exynos_drm_subdrv_list, list) {
|
||||
err = exynos_drm_subdrv_probe(dev, subdrv);
|
||||
if (err) {
|
||||
DRM_DEBUG("exynos drm subdrv probe failed.\n");
|
||||
list_del(&subdrv->list);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(exynos_drm_device_register);
|
||||
|
||||
int exynos_drm_device_unregister(struct drm_device *dev)
|
||||
{
|
||||
struct exynos_drm_subdrv *subdrv;
|
||||
|
||||
if (!dev) {
|
||||
WARN(1, "Unexpected drm device unregister!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) {
|
||||
exynos_drm_subdrv_remove(dev, subdrv);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(exynos_drm_device_unregister);
|
||||
|
||||
int exynos_drm_manager_register(struct exynos_drm_manager *manager)
|
||||
{
|
||||
BUG_ON(!manager->ops);
|
||||
list_add_tail(&manager->list, &exynos_drm_manager_list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exynos_drm_manager_unregister(struct exynos_drm_manager *manager)
|
||||
{
|
||||
if (manager->ops->remove)
|
||||
manager->ops->remove(manager);
|
||||
|
||||
list_del(&manager->list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exynos_drm_display_register(struct exynos_drm_display *display)
|
||||
{
|
||||
BUG_ON(!display->ops);
|
||||
list_add_tail(&display->list, &exynos_drm_display_list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exynos_drm_display_unregister(struct exynos_drm_display *display)
|
||||
{
|
||||
if (display->ops->remove)
|
||||
display->ops->remove(display);
|
||||
|
||||
list_del(&display->list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv)
|
||||
{
|
||||
if (!subdrv)
|
||||
@ -269,6 +77,54 @@ int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *subdrv)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(exynos_drm_subdrv_unregister);
|
||||
|
||||
int exynos_drm_device_subdrv_probe(struct drm_device *dev)
|
||||
{
|
||||
struct exynos_drm_subdrv *subdrv, *n;
|
||||
int err;
|
||||
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
|
||||
list_for_each_entry_safe(subdrv, n, &exynos_drm_subdrv_list, list) {
|
||||
if (subdrv->probe) {
|
||||
subdrv->drm_dev = dev;
|
||||
|
||||
/*
|
||||
* this probe callback would be called by sub driver
|
||||
* after setting of all resources to this sub driver,
|
||||
* such as clock, irq and register map are done.
|
||||
*/
|
||||
err = subdrv->probe(dev, subdrv->dev);
|
||||
if (err) {
|
||||
DRM_DEBUG("exynos drm subdrv probe failed.\n");
|
||||
list_del(&subdrv->list);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(exynos_drm_device_subdrv_probe);
|
||||
|
||||
int exynos_drm_device_subdrv_remove(struct drm_device *dev)
|
||||
{
|
||||
struct exynos_drm_subdrv *subdrv;
|
||||
|
||||
if (!dev) {
|
||||
WARN(1, "Unexpected drm device unregister!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) {
|
||||
if (subdrv->remove)
|
||||
subdrv->remove(dev, subdrv->dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(exynos_drm_device_subdrv_remove);
|
||||
|
||||
int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file)
|
||||
{
|
||||
struct exynos_drm_subdrv *subdrv;
|
||||
|
@ -368,6 +368,7 @@ int exynos_drm_crtc_create(struct exynos_drm_manager *manager)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
manager->crtc = &exynos_crtc->drm_crtc;
|
||||
crtc = &exynos_crtc->drm_crtc;
|
||||
|
||||
private->crtc[manager->pipe] = crtc;
|
||||
@ -491,3 +492,19 @@ void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb)
|
||||
manager->ops->wait_for_vblank(manager);
|
||||
}
|
||||
}
|
||||
|
||||
int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev,
|
||||
unsigned int out_type)
|
||||
{
|
||||
struct drm_crtc *crtc;
|
||||
|
||||
list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) {
|
||||
struct exynos_drm_crtc *exynos_crtc;
|
||||
|
||||
exynos_crtc = to_exynos_crtc(crtc);
|
||||
if (exynos_crtc->manager->type == out_type)
|
||||
return exynos_crtc->manager->pipe;
|
||||
}
|
||||
|
||||
return -EPERM;
|
||||
}
|
||||
|
@ -32,4 +32,8 @@ void exynos_drm_crtc_plane_commit(struct drm_crtc *crtc, int zpos);
|
||||
void exynos_drm_crtc_plane_enable(struct drm_crtc *crtc, int zpos);
|
||||
void exynos_drm_crtc_plane_disable(struct drm_crtc *crtc, int zpos);
|
||||
|
||||
/* This function gets pipe value to crtc device matched with out_type. */
|
||||
int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev,
|
||||
unsigned int out_type);
|
||||
|
||||
#endif
|
||||
|
@ -40,20 +40,10 @@ exynos_dpi_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
struct exynos_dpi *ctx = connector_to_dpi(connector);
|
||||
|
||||
/* panels supported only by boot-loader are always connected */
|
||||
if (!ctx->panel_node)
|
||||
return connector_status_connected;
|
||||
if (!ctx->panel->connector)
|
||||
drm_panel_attach(ctx->panel, &ctx->connector);
|
||||
|
||||
if (!ctx->panel) {
|
||||
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;
|
||||
return connector_status_connected;
|
||||
}
|
||||
|
||||
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,
|
||||
|
||||
ctx->encoder = encoder;
|
||||
|
||||
if (ctx->panel_node)
|
||||
connector->polled = DRM_CONNECTOR_POLL_CONNECT;
|
||||
else
|
||||
connector->polled = DRM_CONNECTOR_POLL_HPD;
|
||||
connector->polled = DRM_CONNECTOR_POLL_HPD;
|
||||
|
||||
ret = drm_connector_init(encoder->dev, connector,
|
||||
&exynos_dpi_connector_funcs,
|
||||
@ -287,8 +274,10 @@ static int exynos_dpi_parse_dt(struct exynos_dpi *ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = of_get_videomode(dn, vm, 0);
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
devm_kfree(dev, vm);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ctx->vm = vm;
|
||||
|
||||
@ -301,32 +290,58 @@ static int exynos_dpi_parse_dt(struct exynos_dpi *ctx)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exynos_dpi_probe(struct device *dev)
|
||||
struct exynos_drm_display *exynos_dpi_probe(struct device *dev)
|
||||
{
|
||||
struct exynos_dpi *ctx;
|
||||
int ret;
|
||||
|
||||
ret = exynos_drm_component_add(dev,
|
||||
EXYNOS_DEVICE_TYPE_CONNECTOR,
|
||||
exynos_dpi_display.type);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
goto err_del_component;
|
||||
|
||||
ctx->dev = dev;
|
||||
exynos_dpi_display.ctx = ctx;
|
||||
ctx->dpms_mode = DRM_MODE_DPMS_OFF;
|
||||
|
||||
ret = exynos_dpi_parse_dt(ctx);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret < 0) {
|
||||
devm_kfree(dev, ctx);
|
||||
goto err_del_component;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct drm_encoder *encoder = exynos_dpi_display.encoder;
|
||||
struct exynos_dpi *ctx = exynos_dpi_display.ctx;
|
||||
|
||||
exynos_dpi_dpms(&exynos_dpi_display, DRM_MODE_DPMS_OFF);
|
||||
exynos_drm_display_unregister(&exynos_dpi_display);
|
||||
encoder->funcs->destroy(encoder);
|
||||
drm_connector_cleanup(&ctx->connector);
|
||||
|
||||
exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
|
||||
#include <linux/anon_inodes.h>
|
||||
#include <linux/component.h>
|
||||
|
||||
#include <drm/exynos_drm.h>
|
||||
|
||||
@ -40,9 +41,19 @@
|
||||
|
||||
#define VBLANK_OFF_DELAY 50000
|
||||
|
||||
/* platform device pointer for eynos drm device. */
|
||||
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)
|
||||
{
|
||||
struct exynos_drm_private *private;
|
||||
@ -73,38 +84,21 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
|
||||
|
||||
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++) {
|
||||
struct drm_plane *plane;
|
||||
unsigned long possible_crtcs = (1 << MAX_CRTC) - 1;
|
||||
|
||||
plane = exynos_plane_init(dev, possible_crtcs, false);
|
||||
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 */
|
||||
drm_kms_helper_poll_init(dev);
|
||||
|
||||
ret = drm_vblank_init(dev, MAX_CRTC);
|
||||
if (ret)
|
||||
goto err_display_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;
|
||||
goto err_mode_config_cleanup;
|
||||
|
||||
/* setup possible_clones. */
|
||||
exynos_drm_encoder_setup(dev);
|
||||
@ -113,17 +107,25 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
|
||||
|
||||
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 */
|
||||
drm_helper_hpd_irq_event(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_vblank:
|
||||
err_unbind_all:
|
||||
component_unbind_all(dev->dev, dev);
|
||||
err_cleanup_vblank:
|
||||
drm_vblank_cleanup(dev);
|
||||
err_display_cleanup:
|
||||
exynos_drm_remove_displays(dev);
|
||||
err_manager_cleanup:
|
||||
exynos_drm_remove_managers(dev);
|
||||
err_mode_config_cleanup:
|
||||
drm_mode_config_cleanup(dev);
|
||||
drm_release_iommu_mapping(dev);
|
||||
@ -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)
|
||||
{
|
||||
exynos_drm_device_subdrv_remove(dev);
|
||||
|
||||
exynos_drm_fbdev_fini(dev);
|
||||
exynos_drm_device_unregister(dev);
|
||||
drm_vblank_cleanup(dev);
|
||||
drm_kms_helper_poll_fini(dev);
|
||||
exynos_drm_remove_displays(dev);
|
||||
exynos_drm_remove_managers(dev);
|
||||
drm_mode_config_cleanup(dev);
|
||||
|
||||
drm_release_iommu_mapping(dev);
|
||||
kfree(dev->dev_private);
|
||||
|
||||
component_unbind_all(dev->dev, dev);
|
||||
dev->dev_private = NULL;
|
||||
|
||||
return 0;
|
||||
@ -183,9 +185,9 @@ static int exynos_drm_resume(struct drm_device *dev)
|
||||
if (connector->funcs->dpms)
|
||||
connector->funcs->dpms(connector, connector->dpms);
|
||||
}
|
||||
drm_modeset_unlock_all(dev);
|
||||
|
||||
drm_helper_resume_force_mode(dev);
|
||||
drm_modeset_unlock_all(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -323,8 +325,7 @@ static const struct file_operations exynos_drm_driver_fops = {
|
||||
};
|
||||
|
||||
static struct drm_driver exynos_drm_driver = {
|
||||
.driver_features = DRIVER_HAVE_IRQ | DRIVER_MODESET |
|
||||
DRIVER_GEM | DRIVER_PRIME,
|
||||
.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
|
||||
.load = exynos_drm_load,
|
||||
.unload = exynos_drm_unload,
|
||||
.suspend = exynos_drm_suspend,
|
||||
@ -355,27 +356,6 @@ static struct drm_driver exynos_drm_driver = {
|
||||
.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
|
||||
static int exynos_drm_sys_suspend(struct device *dev)
|
||||
{
|
||||
@ -400,237 +380,409 @@ static int exynos_drm_sys_resume(struct device *dev)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
static int exynos_drm_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct drm_device *drm_dev = dev_get_drvdata(dev);
|
||||
pm_message_t message;
|
||||
|
||||
if (pm_runtime_suspended(dev))
|
||||
return 0;
|
||||
|
||||
message.event = PM_EVENT_SUSPEND;
|
||||
return exynos_drm_suspend(drm_dev, message);
|
||||
}
|
||||
|
||||
static int exynos_drm_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct drm_device *drm_dev = dev_get_drvdata(dev);
|
||||
|
||||
if (!pm_runtime_suspended(dev))
|
||||
return 0;
|
||||
|
||||
return exynos_drm_resume(drm_dev);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops exynos_drm_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(exynos_drm_sys_suspend, exynos_drm_sys_resume)
|
||||
SET_RUNTIME_PM_OPS(exynos_drm_runtime_suspend,
|
||||
exynos_drm_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
int exynos_drm_component_add(struct device *dev,
|
||||
enum exynos_drm_device_type dev_type,
|
||||
enum exynos_drm_output_type out_type)
|
||||
{
|
||||
struct component_dev *cdev;
|
||||
|
||||
if (dev_type != EXYNOS_DEVICE_TYPE_CRTC &&
|
||||
dev_type != EXYNOS_DEVICE_TYPE_CONNECTOR) {
|
||||
DRM_ERROR("invalid device type.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&drm_component_lock);
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
void exynos_drm_component_del(struct device *dev,
|
||||
enum exynos_drm_device_type dev_type)
|
||||
{
|
||||
struct component_dev *cdev, *next;
|
||||
|
||||
mutex_lock(&drm_component_lock);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static int compare_of(struct device *dev, void *data)
|
||||
{
|
||||
return dev == (struct device *)data;
|
||||
}
|
||||
|
||||
static int exynos_drm_add_components(struct device *dev, struct master *m)
|
||||
{
|
||||
struct component_dev *cdev;
|
||||
unsigned int attach_cnt = 0;
|
||||
|
||||
mutex_lock(&drm_component_lock);
|
||||
|
||||
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 exynos_drm_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
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
|
||||
ret = platform_driver_register(&dp_driver);
|
||||
if (ret < 0)
|
||||
goto err_unregister_fimd_drv;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_DSI
|
||||
ret = platform_driver_register(&dsi_driver);
|
||||
if (ret < 0)
|
||||
goto err_unregister_dp_drv;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_HDMI
|
||||
ret = platform_driver_register(&mixer_driver);
|
||||
if (ret < 0)
|
||||
goto err_unregister_dsi_drv;
|
||||
ret = platform_driver_register(&hdmi_driver);
|
||||
if (ret < 0)
|
||||
goto err_unregister_mixer_drv;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_G2D
|
||||
ret = platform_driver_register(&g2d_driver);
|
||||
if (ret < 0)
|
||||
goto err_unregister_hdmi_drv;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_FIMC
|
||||
ret = platform_driver_register(&fimc_driver);
|
||||
if (ret < 0)
|
||||
goto err_unregister_g2d_drv;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_ROTATOR
|
||||
ret = platform_driver_register(&rotator_driver);
|
||||
if (ret < 0)
|
||||
goto err_unregister_fimc_drv;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_GSC
|
||||
ret = platform_driver_register(&gsc_driver);
|
||||
if (ret < 0)
|
||||
goto err_unregister_rotator_drv;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_IPP
|
||||
ret = platform_driver_register(&ipp_driver);
|
||||
if (ret < 0)
|
||||
goto err_unregister_gsc_drv;
|
||||
|
||||
ret = exynos_platform_device_ipp_register();
|
||||
if (ret < 0)
|
||||
goto err_unregister_ipp_drv;
|
||||
#endif
|
||||
|
||||
ret = component_master_add(&pdev->dev, &exynos_drm_ops);
|
||||
if (ret < 0)
|
||||
DRM_DEBUG_KMS("re-tried by last sub driver probed later.\n");
|
||||
|
||||
return 0;
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_IPP
|
||||
err_unregister_ipp_drv:
|
||||
platform_driver_unregister(&ipp_driver);
|
||||
err_unregister_gsc_drv:
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_GSC
|
||||
platform_driver_unregister(&gsc_driver);
|
||||
err_unregister_rotator_drv:
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_ROTATOR
|
||||
platform_driver_unregister(&rotator_driver);
|
||||
err_unregister_fimc_drv:
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_FIMC
|
||||
platform_driver_unregister(&fimc_driver);
|
||||
err_unregister_g2d_drv:
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_G2D
|
||||
platform_driver_unregister(&g2d_driver);
|
||||
err_unregister_hdmi_drv:
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_HDMI
|
||||
platform_driver_unregister(&hdmi_driver);
|
||||
err_unregister_mixer_drv:
|
||||
platform_driver_unregister(&mixer_driver);
|
||||
err_unregister_dsi_drv:
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_DSI
|
||||
platform_driver_unregister(&dsi_driver);
|
||||
err_unregister_dp_drv:
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_DP
|
||||
platform_driver_unregister(&dp_driver);
|
||||
err_unregister_fimd_drv:
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_FIMD
|
||||
platform_driver_unregister(&fimd_driver);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int exynos_drm_platform_remove(struct platform_device *pdev)
|
||||
{
|
||||
#ifdef CONFIG_DRM_EXYNOS_IPP
|
||||
exynos_platform_device_ipp_unregister();
|
||||
platform_driver_unregister(&ipp_driver);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_GSC
|
||||
platform_driver_unregister(&gsc_driver);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_ROTATOR
|
||||
platform_driver_unregister(&rotator_driver);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_FIMC
|
||||
platform_driver_unregister(&fimc_driver);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_G2D
|
||||
platform_driver_unregister(&g2d_driver);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_HDMI
|
||||
platform_driver_unregister(&mixer_driver);
|
||||
platform_driver_unregister(&hdmi_driver);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_FIMD
|
||||
platform_driver_unregister(&fimd_driver);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_DSI
|
||||
platform_driver_unregister(&dsi_driver);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_DP
|
||||
platform_driver_unregister(&dp_driver);
|
||||
#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 = {
|
||||
.probe = exynos_drm_platform_probe,
|
||||
.remove = exynos_drm_platform_remove,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "exynos-drm",
|
||||
.pm = &exynos_drm_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init exynos_drm_init(void)
|
||||
static int exynos_drm_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_DP
|
||||
ret = platform_driver_register(&dp_driver);
|
||||
if (ret < 0)
|
||||
goto out_dp;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_DSI
|
||||
ret = platform_driver_register(&dsi_driver);
|
||||
if (ret < 0)
|
||||
goto out_dsi;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_FIMD
|
||||
ret = platform_driver_register(&fimd_driver);
|
||||
if (ret < 0)
|
||||
goto out_fimd;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_HDMI
|
||||
ret = platform_driver_register(&hdmi_driver);
|
||||
if (ret < 0)
|
||||
goto out_hdmi;
|
||||
ret = platform_driver_register(&mixer_driver);
|
||||
if (ret < 0)
|
||||
goto out_mixer;
|
||||
#endif
|
||||
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 = platform_driver_register(&vidi_driver);
|
||||
ret = exynos_drm_probe_vidi();
|
||||
if (ret < 0)
|
||||
goto out_vidi;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_G2D
|
||||
ret = platform_driver_register(&g2d_driver);
|
||||
if (ret < 0)
|
||||
goto out_g2d;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_FIMC
|
||||
ret = platform_driver_register(&fimc_driver);
|
||||
if (ret < 0)
|
||||
goto out_fimc;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_ROTATOR
|
||||
ret = platform_driver_register(&rotator_driver);
|
||||
if (ret < 0)
|
||||
goto out_rotator;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_GSC
|
||||
ret = platform_driver_register(&gsc_driver);
|
||||
if (ret < 0)
|
||||
goto out_gsc;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_IPP
|
||||
ret = platform_driver_register(&ipp_driver);
|
||||
if (ret < 0)
|
||||
goto out_ipp;
|
||||
|
||||
ret = exynos_platform_device_ipp_register();
|
||||
if (ret < 0)
|
||||
goto out_ipp_dev;
|
||||
goto err_unregister_pd;
|
||||
#endif
|
||||
|
||||
ret = platform_driver_register(&exynos_drm_platform_driver);
|
||||
if (ret < 0)
|
||||
goto out_drm;
|
||||
|
||||
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;
|
||||
}
|
||||
if (ret)
|
||||
goto err_remove_vidi;
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
platform_driver_unregister(&exynos_drm_platform_driver);
|
||||
|
||||
out_drm:
|
||||
#ifdef CONFIG_DRM_EXYNOS_IPP
|
||||
exynos_platform_device_ipp_unregister();
|
||||
out_ipp_dev:
|
||||
platform_driver_unregister(&ipp_driver);
|
||||
out_ipp:
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_GSC
|
||||
platform_driver_unregister(&gsc_driver);
|
||||
out_gsc:
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_ROTATOR
|
||||
platform_driver_unregister(&rotator_driver);
|
||||
out_rotator:
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_FIMC
|
||||
platform_driver_unregister(&fimc_driver);
|
||||
out_fimc:
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_G2D
|
||||
platform_driver_unregister(&g2d_driver);
|
||||
out_g2d:
|
||||
#endif
|
||||
err_unregister_pd:
|
||||
platform_device_unregister(exynos_drm_pdev);
|
||||
|
||||
err_remove_vidi:
|
||||
#ifdef CONFIG_DRM_EXYNOS_VIDI
|
||||
platform_driver_unregister(&vidi_driver);
|
||||
out_vidi:
|
||||
exynos_drm_remove_vidi();
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_HDMI
|
||||
platform_driver_unregister(&mixer_driver);
|
||||
out_mixer:
|
||||
platform_driver_unregister(&hdmi_driver);
|
||||
out_hdmi:
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_FIMD
|
||||
platform_driver_unregister(&fimd_driver);
|
||||
out_fimd:
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_DSI
|
||||
platform_driver_unregister(&dsi_driver);
|
||||
out_dsi:
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_DP
|
||||
platform_driver_unregister(&dp_driver);
|
||||
out_dp:
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit exynos_drm_exit(void)
|
||||
static void exynos_drm_exit(void)
|
||||
{
|
||||
platform_device_unregister(exynos_drm_pdev);
|
||||
|
||||
platform_driver_unregister(&exynos_drm_platform_driver);
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_IPP
|
||||
exynos_platform_device_ipp_unregister();
|
||||
platform_driver_unregister(&ipp_driver);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_GSC
|
||||
platform_driver_unregister(&gsc_driver);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_ROTATOR
|
||||
platform_driver_unregister(&rotator_driver);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_FIMC
|
||||
platform_driver_unregister(&fimc_driver);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_G2D
|
||||
platform_driver_unregister(&g2d_driver);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_HDMI
|
||||
platform_driver_unregister(&mixer_driver);
|
||||
platform_driver_unregister(&hdmi_driver);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_VIDI
|
||||
platform_driver_unregister(&vidi_driver);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_FIMD
|
||||
platform_driver_unregister(&fimd_driver);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_DSI
|
||||
platform_driver_unregister(&dsi_driver);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_DP
|
||||
platform_driver_unregister(&dp_driver);
|
||||
exynos_drm_remove_vidi();
|
||||
#endif
|
||||
platform_device_unregister(exynos_drm_pdev);
|
||||
platform_driver_unregister(&exynos_drm_platform_driver);
|
||||
}
|
||||
|
||||
module_init(exynos_drm_init);
|
||||
|
@ -42,6 +42,13 @@ struct drm_connector;
|
||||
|
||||
extern unsigned int drm_vblank_offdelay;
|
||||
|
||||
/* This enumerates device type. */
|
||||
enum exynos_drm_device_type {
|
||||
EXYNOS_DEVICE_TYPE_NONE,
|
||||
EXYNOS_DEVICE_TYPE_CRTC,
|
||||
EXYNOS_DEVICE_TYPE_CONNECTOR,
|
||||
};
|
||||
|
||||
/* this enumerates display type. */
|
||||
enum exynos_drm_output_type {
|
||||
EXYNOS_DISPLAY_TYPE_NONE,
|
||||
@ -122,7 +129,6 @@ struct exynos_drm_overlay {
|
||||
* Exynos DRM Display Structure.
|
||||
* - this structure is common to analog tv, digital tv and lcd panel.
|
||||
*
|
||||
* @initialize: initializes the display with drm_dev
|
||||
* @remove: cleans up the display for removal
|
||||
* @mode_fixup: fix mode data comparing to hw specific display mode.
|
||||
* @mode_set: convert drm_display_mode to hw specific display mode and
|
||||
@ -133,8 +139,6 @@ struct exynos_drm_overlay {
|
||||
*/
|
||||
struct exynos_drm_display;
|
||||
struct exynos_drm_display_ops {
|
||||
int (*initialize)(struct exynos_drm_display *display,
|
||||
struct drm_device *drm_dev);
|
||||
int (*create_connector)(struct exynos_drm_display *display,
|
||||
struct drm_encoder *encoder);
|
||||
void (*remove)(struct exynos_drm_display *display);
|
||||
@ -172,8 +176,6 @@ struct exynos_drm_display {
|
||||
/*
|
||||
* Exynos drm manager ops
|
||||
*
|
||||
* @initialize: initializes the manager with drm_dev
|
||||
* @remove: cleans up the manager for removal
|
||||
* @dpms: control device power.
|
||||
* @mode_fixup: fix mode data before applying it
|
||||
* @mode_set: set the given mode to the manager
|
||||
@ -189,9 +191,6 @@ struct exynos_drm_display {
|
||||
*/
|
||||
struct exynos_drm_manager;
|
||||
struct exynos_drm_manager_ops {
|
||||
int (*initialize)(struct exynos_drm_manager *mgr,
|
||||
struct drm_device *drm_dev, int pipe);
|
||||
void (*remove)(struct exynos_drm_manager *mgr);
|
||||
void (*dpms)(struct exynos_drm_manager *mgr, int mode);
|
||||
bool (*mode_fixup)(struct exynos_drm_manager *mgr,
|
||||
const struct drm_display_mode *mode,
|
||||
@ -215,6 +214,7 @@ struct exynos_drm_manager_ops {
|
||||
* @list: the list entry for this manager
|
||||
* @type: one of EXYNOS_DISPLAY_TYPE_LCD and HDMI.
|
||||
* @drm_dev: pointer to the drm device
|
||||
* @crtc: crtc object.
|
||||
* @pipe: the pipe number for this crtc/manager
|
||||
* @ops: pointer to callbacks for exynos drm specific functionality
|
||||
* @ctx: A pointer to the manager's implementation specific context
|
||||
@ -223,6 +223,7 @@ struct exynos_drm_manager {
|
||||
struct list_head list;
|
||||
enum exynos_drm_output_type type;
|
||||
struct drm_device *drm_dev;
|
||||
struct drm_crtc *crtc;
|
||||
int pipe;
|
||||
struct exynos_drm_manager_ops *ops;
|
||||
void *ctx;
|
||||
@ -254,6 +255,7 @@ struct drm_exynos_file_private {
|
||||
* otherwise default one.
|
||||
* @da_space_size: size of device address space.
|
||||
* if 0 then default value is used for it.
|
||||
* @pipe: the pipe number for this crtc/manager.
|
||||
*/
|
||||
struct exynos_drm_private {
|
||||
struct drm_fb_helper *fb_helper;
|
||||
@ -271,6 +273,8 @@ struct exynos_drm_private {
|
||||
|
||||
unsigned long da_start;
|
||||
unsigned long da_space_size;
|
||||
|
||||
unsigned int pipe;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -281,11 +285,11 @@ struct exynos_drm_private {
|
||||
* @drm_dev: pointer to drm_device and this pointer would be set
|
||||
* when sub driver calls exynos_drm_subdrv_register().
|
||||
* @manager: subdrv has its own manager to control a hardware appropriately
|
||||
* and we can access a hardware drawing on this manager.
|
||||
* and we can access a hardware drawing on this manager.
|
||||
* @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
|
||||
* by probe callback.
|
||||
* by probe callback.
|
||||
* @open: this would be called with drm device file open.
|
||||
* @close: this would be called with drm device file close.
|
||||
*/
|
||||
@ -302,39 +306,14 @@ struct exynos_drm_subdrv {
|
||||
struct drm_file *file);
|
||||
};
|
||||
|
||||
/*
|
||||
* this function calls a probe callback registered to sub driver list and
|
||||
* create its own encoder and connector and then set drm_device object
|
||||
* to global one.
|
||||
*/
|
||||
int exynos_drm_device_register(struct drm_device *dev);
|
||||
/*
|
||||
* this function calls a remove callback registered to sub driver list and
|
||||
* destroy its own encoder and connetor.
|
||||
*/
|
||||
int exynos_drm_device_unregister(struct drm_device *dev);
|
||||
|
||||
int exynos_drm_initialize_managers(struct drm_device *dev);
|
||||
void exynos_drm_remove_managers(struct drm_device *dev);
|
||||
int exynos_drm_initialize_displays(struct drm_device *dev);
|
||||
void exynos_drm_remove_displays(struct drm_device *dev);
|
||||
|
||||
int exynos_drm_manager_register(struct exynos_drm_manager *manager);
|
||||
int exynos_drm_manager_unregister(struct exynos_drm_manager *manager);
|
||||
int exynos_drm_display_register(struct exynos_drm_display *display);
|
||||
int exynos_drm_display_unregister(struct exynos_drm_display *display);
|
||||
|
||||
/*
|
||||
* this function would be called by sub drivers such as display controller
|
||||
* or hdmi driver to register this sub driver object to exynos drm driver
|
||||
* and when a sub driver is registered to exynos drm driver a probe callback
|
||||
* of the sub driver is called and creates its own encoder and connector.
|
||||
*/
|
||||
/* This function would be called by non kms drivers such as g2d and ipp. */
|
||||
int exynos_drm_subdrv_register(struct exynos_drm_subdrv *drm_subdrv);
|
||||
|
||||
/* this function removes subdrv list from exynos drm driver */
|
||||
int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *drm_subdrv);
|
||||
|
||||
int exynos_drm_device_subdrv_probe(struct drm_device *dev);
|
||||
int exynos_drm_device_subdrv_remove(struct drm_device *dev);
|
||||
int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file);
|
||||
void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file);
|
||||
|
||||
@ -360,18 +339,40 @@ int exynos_platform_device_ipp_register(void);
|
||||
void exynos_platform_device_ipp_unregister(void);
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_DPI
|
||||
int exynos_dpi_probe(struct device *dev);
|
||||
struct exynos_drm_display * exynos_dpi_probe(struct device *dev);
|
||||
int exynos_dpi_remove(struct device *dev);
|
||||
#else
|
||||
static inline int exynos_dpi_probe(struct device *dev) { return 0; }
|
||||
static inline struct exynos_drm_display *
|
||||
exynos_dpi_probe(struct device *dev) { return 0; }
|
||||
static inline int exynos_dpi_remove(struct device *dev) { return 0; }
|
||||
#endif
|
||||
|
||||
/*
|
||||
* this function registers exynos drm vidi platform device/driver.
|
||||
*/
|
||||
int exynos_drm_probe_vidi(void);
|
||||
|
||||
/*
|
||||
* this function unregister exynos drm vidi platform device/driver.
|
||||
*/
|
||||
void exynos_drm_remove_vidi(void);
|
||||
|
||||
/* This function creates a encoder and a connector, and initializes them. */
|
||||
int exynos_drm_create_enc_conn(struct drm_device *dev,
|
||||
struct exynos_drm_display *display);
|
||||
|
||||
int exynos_drm_component_add(struct device *dev,
|
||||
enum exynos_drm_device_type dev_type,
|
||||
enum exynos_drm_output_type out_type);
|
||||
|
||||
void exynos_drm_component_del(struct device *dev,
|
||||
enum exynos_drm_device_type dev_type);
|
||||
|
||||
extern struct platform_driver fimd_driver;
|
||||
extern struct platform_driver dp_driver;
|
||||
extern struct platform_driver dsi_driver;
|
||||
extern struct platform_driver fimd_driver;
|
||||
extern struct platform_driver hdmi_driver;
|
||||
extern struct platform_driver mixer_driver;
|
||||
extern struct platform_driver hdmi_driver;
|
||||
extern struct platform_driver exynos_drm_common_hdmi_driver;
|
||||
extern struct platform_driver vidi_driver;
|
||||
extern struct platform_driver g2d_driver;
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <linux/irq.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/component.h>
|
||||
|
||||
#include <video/mipi_display.h>
|
||||
#include <video/videomode.h>
|
||||
@ -1378,16 +1379,60 @@ static int exynos_dsi_parse_dt(struct exynos_dsi *dsi)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int exynos_dsi_bind(struct device *dev, struct device *master,
|
||||
void *data)
|
||||
{
|
||||
struct drm_device *drm_dev = data;
|
||||
struct exynos_dsi *dsi;
|
||||
int ret;
|
||||
|
||||
ret = exynos_drm_create_enc_conn(drm_dev, &exynos_dsi_display);
|
||||
if (ret) {
|
||||
DRM_ERROR("Encoder create [%d] failed with %d\n",
|
||||
exynos_dsi_display.type, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dsi = exynos_dsi_display.ctx;
|
||||
|
||||
return mipi_dsi_host_register(&dsi->dsi_host);
|
||||
}
|
||||
|
||||
static void exynos_dsi_unbind(struct device *dev, struct device *master,
|
||||
void *data)
|
||||
{
|
||||
struct exynos_dsi *dsi = exynos_dsi_display.ctx;
|
||||
struct drm_encoder *encoder = dsi->encoder;
|
||||
|
||||
exynos_dsi_dpms(&exynos_dsi_display, DRM_MODE_DPMS_OFF);
|
||||
|
||||
mipi_dsi_host_unregister(&dsi->dsi_host);
|
||||
|
||||
encoder->funcs->destroy(encoder);
|
||||
drm_connector_cleanup(&dsi->connector);
|
||||
}
|
||||
|
||||
static const struct component_ops exynos_dsi_component_ops = {
|
||||
.bind = exynos_dsi_bind,
|
||||
.unbind = exynos_dsi_unbind,
|
||||
};
|
||||
|
||||
static int exynos_dsi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct exynos_dsi *dsi;
|
||||
int ret;
|
||||
|
||||
ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
|
||||
exynos_dsi_display.type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL);
|
||||
if (!dsi) {
|
||||
dev_err(&pdev->dev, "failed to allocate dsi object.\n");
|
||||
return -ENOMEM;
|
||||
ret = -ENOMEM;
|
||||
goto err_del_component;
|
||||
}
|
||||
|
||||
init_completion(&dsi->completed);
|
||||
@ -1401,7 +1446,7 @@ static int exynos_dsi_probe(struct platform_device *pdev)
|
||||
|
||||
ret = exynos_dsi_parse_dt(dsi);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto err_del_component;
|
||||
|
||||
dsi->supplies[0].supply = "vddcore";
|
||||
dsi->supplies[1].supply = "vddio";
|
||||
@ -1415,32 +1460,37 @@ static int exynos_dsi_probe(struct platform_device *pdev)
|
||||
dsi->pll_clk = devm_clk_get(&pdev->dev, "pll_clk");
|
||||
if (IS_ERR(dsi->pll_clk)) {
|
||||
dev_info(&pdev->dev, "failed to get dsi pll input clock\n");
|
||||
return -EPROBE_DEFER;
|
||||
ret = PTR_ERR(dsi->pll_clk);
|
||||
goto err_del_component;
|
||||
}
|
||||
|
||||
dsi->bus_clk = devm_clk_get(&pdev->dev, "bus_clk");
|
||||
if (IS_ERR(dsi->bus_clk)) {
|
||||
dev_info(&pdev->dev, "failed to get dsi bus clock\n");
|
||||
return -EPROBE_DEFER;
|
||||
ret = PTR_ERR(dsi->bus_clk);
|
||||
goto err_del_component;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
dsi->reg_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(dsi->reg_base)) {
|
||||
dev_err(&pdev->dev, "failed to remap io region\n");
|
||||
return PTR_ERR(dsi->reg_base);
|
||||
ret = PTR_ERR(dsi->reg_base);
|
||||
goto err_del_component;
|
||||
}
|
||||
|
||||
dsi->phy = devm_phy_get(&pdev->dev, "dsim");
|
||||
if (IS_ERR(dsi->phy)) {
|
||||
dev_info(&pdev->dev, "failed to get dsim phy\n");
|
||||
return -EPROBE_DEFER;
|
||||
ret = PTR_ERR(dsi->phy);
|
||||
goto err_del_component;
|
||||
}
|
||||
|
||||
dsi->irq = platform_get_irq(pdev, 0);
|
||||
if (dsi->irq < 0) {
|
||||
dev_err(&pdev->dev, "failed to request dsi irq resource\n");
|
||||
return dsi->irq;
|
||||
ret = dsi->irq;
|
||||
goto err_del_component;
|
||||
}
|
||||
|
||||
irq_set_status_flags(dsi->irq, IRQ_NOAUTOEN);
|
||||
@ -1449,59 +1499,32 @@ static int exynos_dsi_probe(struct platform_device *pdev)
|
||||
dev_name(&pdev->dev), dsi);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to request dsi irq\n");
|
||||
return ret;
|
||||
goto err_del_component;
|
||||
}
|
||||
|
||||
exynos_dsi_display.ctx = dsi;
|
||||
|
||||
platform_set_drvdata(pdev, &exynos_dsi_display);
|
||||
exynos_drm_display_register(&exynos_dsi_display);
|
||||
|
||||
return mipi_dsi_host_register(&dsi->dsi_host);
|
||||
ret = component_add(&pdev->dev, &exynos_dsi_component_ops);
|
||||
if (ret)
|
||||
goto err_del_component;
|
||||
|
||||
return ret;
|
||||
|
||||
err_del_component:
|
||||
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
|
||||
return ret;
|
||||
}
|
||||
|
||||
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);
|
||||
component_del(&pdev->dev, &exynos_dsi_component_ops);
|
||||
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if CONFIG_PM_SLEEP
|
||||
static int exynos_dsi_resume(struct device *dev)
|
||||
{
|
||||
struct exynos_dsi *dsi = exynos_dsi_display.ctx;
|
||||
|
||||
if (dsi->state & DSIM_STATE_ENABLED) {
|
||||
dsi->state &= ~DSIM_STATE_ENABLED;
|
||||
exynos_dsi_enable(dsi);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exynos_dsi_suspend(struct device *dev)
|
||||
{
|
||||
struct exynos_dsi *dsi = exynos_dsi_display.ctx;
|
||||
|
||||
if (dsi->state & DSIM_STATE_ENABLED) {
|
||||
exynos_dsi_disable(dsi);
|
||||
dsi->state |= DSIM_STATE_ENABLED;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops exynos_dsi_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(exynos_dsi_suspend, exynos_dsi_resume)
|
||||
};
|
||||
|
||||
static struct of_device_id exynos_dsi_of_match[] = {
|
||||
{ .compatible = "samsung,exynos4210-mipi-dsi" },
|
||||
{ }
|
||||
@ -1513,7 +1536,6 @@ struct platform_driver dsi_driver = {
|
||||
.driver = {
|
||||
.name = "exynos-dsi",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &exynos_dsi_pm_ops,
|
||||
.of_match_table = exynos_dsi_of_match,
|
||||
},
|
||||
};
|
||||
|
@ -121,16 +121,8 @@ static int exynos_drm_fbdev_update(struct drm_fb_helper *helper,
|
||||
offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3);
|
||||
offset += fbi->var.yoffset * fb->pitches[0];
|
||||
|
||||
dev->mode_config.fb_base = (resource_size_t)buffer->dma_addr;
|
||||
fbi->screen_base = buffer->kvaddr + offset;
|
||||
if (is_drm_iommu_supported(dev))
|
||||
fbi->fix.smem_start = (unsigned long)
|
||||
(page_to_phys(sg_page(buffer->sgt->sgl)) + offset);
|
||||
else
|
||||
fbi->fix.smem_start = (unsigned long)buffer->dma_addr;
|
||||
|
||||
fbi->screen_size = size;
|
||||
fbi->fix.smem_len = size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -237,7 +229,7 @@ static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
|
||||
.fb_probe = exynos_drm_fbdev_create,
|
||||
};
|
||||
|
||||
bool exynos_drm_fbdev_is_anything_connected(struct drm_device *dev)
|
||||
static bool exynos_drm_fbdev_is_anything_connected(struct drm_device *dev)
|
||||
{
|
||||
struct drm_connector *connector;
|
||||
bool ret = false;
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/exynos_drm.h>
|
||||
@ -57,7 +58,6 @@
|
||||
#define FIMC_SHFACTOR 10
|
||||
#define FIMC_BUF_STOP 1
|
||||
#define FIMC_BUF_START 2
|
||||
#define FIMC_REG_SZ 32
|
||||
#define FIMC_WIDTH_ITU_709 1280
|
||||
#define FIMC_REFRESH_MAX 60
|
||||
#define FIMC_REFRESH_MIN 12
|
||||
@ -69,9 +69,6 @@
|
||||
#define get_fimc_context(dev) platform_get_drvdata(to_platform_device(dev))
|
||||
#define get_ctx_from_ippdrv(ippdrv) container_of(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 {
|
||||
FIMC_WB_NONE,
|
||||
FIMC_WB_A,
|
||||
@ -161,7 +158,7 @@ struct fimc_context {
|
||||
struct exynos_drm_ippdrv ippdrv;
|
||||
struct resource *regs_res;
|
||||
void __iomem *regs;
|
||||
struct mutex lock;
|
||||
spinlock_t lock;
|
||||
struct clk *clocks[FIMC_CLKS_MAX];
|
||||
u32 clk_frequency;
|
||||
struct regmap *sysreg;
|
||||
@ -172,39 +169,53 @@ struct fimc_context {
|
||||
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)
|
||||
{
|
||||
u32 cfg;
|
||||
|
||||
/* stop dma operation */
|
||||
cfg = fimc_read(EXYNOS_CISTATUS);
|
||||
if (EXYNOS_CISTATUS_GET_ENVID_STATUS(cfg)) {
|
||||
cfg = fimc_read(EXYNOS_MSCTRL);
|
||||
cfg &= ~EXYNOS_MSCTRL_ENVID;
|
||||
fimc_write(cfg, EXYNOS_MSCTRL);
|
||||
}
|
||||
cfg = fimc_read(ctx, EXYNOS_CISTATUS);
|
||||
if (EXYNOS_CISTATUS_GET_ENVID_STATUS(cfg))
|
||||
fimc_clear_bits(ctx, EXYNOS_MSCTRL, EXYNOS_MSCTRL_ENVID);
|
||||
|
||||
cfg = fimc_read(EXYNOS_CISRCFMT);
|
||||
cfg |= EXYNOS_CISRCFMT_ITU601_8BIT;
|
||||
fimc_write(cfg, EXYNOS_CISRCFMT);
|
||||
fimc_set_bits(ctx, EXYNOS_CISRCFMT, EXYNOS_CISRCFMT_ITU601_8BIT);
|
||||
|
||||
/* disable image capture */
|
||||
cfg = fimc_read(EXYNOS_CIIMGCPT);
|
||||
cfg &= ~(EXYNOS_CIIMGCPT_IMGCPTEN_SC | EXYNOS_CIIMGCPT_IMGCPTEN);
|
||||
fimc_write(cfg, EXYNOS_CIIMGCPT);
|
||||
fimc_clear_bits(ctx, EXYNOS_CIIMGCPT,
|
||||
EXYNOS_CIIMGCPT_IMGCPTEN_SC | EXYNOS_CIIMGCPT_IMGCPTEN);
|
||||
|
||||
/* s/w reset */
|
||||
cfg = fimc_read(EXYNOS_CIGCTRL);
|
||||
cfg |= (EXYNOS_CIGCTRL_SWRST);
|
||||
fimc_write(cfg, EXYNOS_CIGCTRL);
|
||||
fimc_set_bits(ctx, EXYNOS_CIGCTRL, EXYNOS_CIGCTRL_SWRST);
|
||||
|
||||
/* s/w reset complete */
|
||||
cfg = fimc_read(EXYNOS_CIGCTRL);
|
||||
cfg &= ~EXYNOS_CIGCTRL_SWRST;
|
||||
fimc_write(cfg, EXYNOS_CIGCTRL);
|
||||
fimc_clear_bits(ctx, EXYNOS_CIGCTRL, EXYNOS_CIGCTRL_SWRST);
|
||||
|
||||
/* reset sequence */
|
||||
fimc_write(0x0, EXYNOS_CIFCNTSEQ);
|
||||
fimc_write(ctx, 0x0, EXYNOS_CIFCNTSEQ);
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
DRM_DEBUG_KMS("wb[%d]\n", wb);
|
||||
|
||||
cfg = fimc_read(EXYNOS_CIGCTRL);
|
||||
cfg = fimc_read(ctx, EXYNOS_CIGCTRL);
|
||||
cfg &= ~(EXYNOS_CIGCTRL_TESTPATTERN_MASK |
|
||||
EXYNOS_CIGCTRL_SELCAM_ITU_MASK |
|
||||
EXYNOS_CIGCTRL_SELCAM_MIPI_MASK |
|
||||
@ -246,7 +257,7 @@ static void fimc_set_type_ctrl(struct fimc_context *ctx, enum fimc_wb wb)
|
||||
break;
|
||||
}
|
||||
|
||||
fimc_write(cfg, EXYNOS_CIGCTRL);
|
||||
fimc_write(ctx, cfg, EXYNOS_CIGCTRL);
|
||||
}
|
||||
|
||||
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",
|
||||
pol->inv_href, pol->inv_hsync);
|
||||
|
||||
cfg = fimc_read(EXYNOS_CIGCTRL);
|
||||
cfg = fimc_read(ctx, EXYNOS_CIGCTRL);
|
||||
cfg &= ~(EXYNOS_CIGCTRL_INVPOLPCLK | EXYNOS_CIGCTRL_INVPOLVSYNC |
|
||||
EXYNOS_CIGCTRL_INVPOLHREF | EXYNOS_CIGCTRL_INVPOLHSYNC);
|
||||
|
||||
@ -272,7 +283,7 @@ static void fimc_set_polarity(struct fimc_context *ctx,
|
||||
if (pol->inv_hsync)
|
||||
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)
|
||||
@ -281,70 +292,54 @@ static void fimc_handle_jpeg(struct fimc_context *ctx, bool enable)
|
||||
|
||||
DRM_DEBUG_KMS("enable[%d]\n", enable);
|
||||
|
||||
cfg = fimc_read(EXYNOS_CIGCTRL);
|
||||
cfg = fimc_read(ctx, EXYNOS_CIGCTRL);
|
||||
if (enable)
|
||||
cfg |= EXYNOS_CIGCTRL_CAM_JPEG;
|
||||
else
|
||||
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,
|
||||
bool overflow, bool level)
|
||||
static void fimc_mask_irq(struct fimc_context *ctx, bool enable)
|
||||
{
|
||||
u32 cfg;
|
||||
|
||||
DRM_DEBUG_KMS("enable[%d]overflow[%d]level[%d]\n",
|
||||
enable, overflow, level);
|
||||
DRM_DEBUG_KMS("enable[%d]\n", enable);
|
||||
|
||||
cfg = fimc_read(EXYNOS_CIGCTRL);
|
||||
cfg = fimc_read(ctx, EXYNOS_CIGCTRL);
|
||||
if (enable) {
|
||||
cfg &= ~(EXYNOS_CIGCTRL_IRQ_OVFEN | EXYNOS_CIGCTRL_IRQ_LEVEL);
|
||||
cfg |= EXYNOS_CIGCTRL_IRQ_ENABLE;
|
||||
if (overflow)
|
||||
cfg |= EXYNOS_CIGCTRL_IRQ_OVFEN;
|
||||
if (level)
|
||||
cfg |= EXYNOS_CIGCTRL_IRQ_LEVEL;
|
||||
cfg &= ~EXYNOS_CIGCTRL_IRQ_OVFEN;
|
||||
cfg |= EXYNOS_CIGCTRL_IRQ_ENABLE | EXYNOS_CIGCTRL_IRQ_LEVEL;
|
||||
} else
|
||||
cfg &= ~(EXYNOS_CIGCTRL_IRQ_OVFEN | EXYNOS_CIGCTRL_IRQ_ENABLE);
|
||||
|
||||
fimc_write(cfg, EXYNOS_CIGCTRL);
|
||||
cfg &= ~EXYNOS_CIGCTRL_IRQ_ENABLE;
|
||||
fimc_write(ctx, cfg, EXYNOS_CIGCTRL);
|
||||
}
|
||||
|
||||
static void fimc_clear_irq(struct fimc_context *ctx)
|
||||
{
|
||||
u32 cfg;
|
||||
|
||||
cfg = fimc_read(EXYNOS_CIGCTRL);
|
||||
cfg |= EXYNOS_CIGCTRL_IRQ_CLR;
|
||||
fimc_write(cfg, EXYNOS_CIGCTRL);
|
||||
fimc_set_bits(ctx, EXYNOS_CIGCTRL, EXYNOS_CIGCTRL_IRQ_CLR);
|
||||
}
|
||||
|
||||
static bool fimc_check_ovf(struct fimc_context *ctx)
|
||||
{
|
||||
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 |
|
||||
EXYNOS_CISTATUS_OVFICR;
|
||||
|
||||
DRM_DEBUG_KMS("flag[0x%x]\n", flag);
|
||||
|
||||
if (status & flag) {
|
||||
cfg = fimc_read(EXYNOS_CIWDOFST);
|
||||
cfg |= (EXYNOS_CIWDOFST_CLROVFIY | EXYNOS_CIWDOFST_CLROVFICB |
|
||||
fimc_set_bits(ctx, EXYNOS_CIWDOFST,
|
||||
EXYNOS_CIWDOFST_CLROVFIY | EXYNOS_CIWDOFST_CLROVFICB |
|
||||
EXYNOS_CIWDOFST_CLROVFICR);
|
||||
|
||||
fimc_write(cfg, EXYNOS_CIWDOFST);
|
||||
|
||||
cfg = fimc_read(EXYNOS_CIWDOFST);
|
||||
cfg &= ~(EXYNOS_CIWDOFST_CLROVFIY | EXYNOS_CIWDOFST_CLROVFICB |
|
||||
fimc_clear_bits(ctx, EXYNOS_CIWDOFST,
|
||||
EXYNOS_CIWDOFST_CLROVFIY | EXYNOS_CIWDOFST_CLROVFICB |
|
||||
EXYNOS_CIWDOFST_CLROVFICR);
|
||||
|
||||
fimc_write(cfg, EXYNOS_CIWDOFST);
|
||||
|
||||
dev_err(ippdrv->dev, "occurred overflow at %d, status 0x%x.\n",
|
||||
ctx->id, status);
|
||||
return true;
|
||||
@ -357,7 +352,7 @@ static bool fimc_check_frame_end(struct fimc_context *ctx)
|
||||
{
|
||||
u32 cfg;
|
||||
|
||||
cfg = fimc_read(EXYNOS_CISTATUS);
|
||||
cfg = fimc_read(ctx, EXYNOS_CISTATUS);
|
||||
|
||||
DRM_DEBUG_KMS("cfg[0x%x]\n", cfg);
|
||||
|
||||
@ -365,7 +360,7 @@ static bool fimc_check_frame_end(struct fimc_context *ctx)
|
||||
return false;
|
||||
|
||||
cfg &= ~(EXYNOS_CISTATUS_FRAMEEND);
|
||||
fimc_write(cfg, EXYNOS_CISTATUS);
|
||||
fimc_write(ctx, cfg, EXYNOS_CISTATUS);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -375,7 +370,7 @@ static int fimc_get_buf_id(struct fimc_context *ctx)
|
||||
u32 cfg;
|
||||
int frame_cnt, buf_id;
|
||||
|
||||
cfg = fimc_read(EXYNOS_CISTATUS2);
|
||||
cfg = fimc_read(ctx, EXYNOS_CISTATUS2);
|
||||
frame_cnt = EXYNOS_CISTATUS2_GET_FRAMECOUNT_BEFORE(cfg);
|
||||
|
||||
if (frame_cnt == 0)
|
||||
@ -402,13 +397,13 @@ static void fimc_handle_lastend(struct fimc_context *ctx, bool enable)
|
||||
|
||||
DRM_DEBUG_KMS("enable[%d]\n", enable);
|
||||
|
||||
cfg = fimc_read(EXYNOS_CIOCTRL);
|
||||
cfg = fimc_read(ctx, EXYNOS_CIOCTRL);
|
||||
if (enable)
|
||||
cfg |= EXYNOS_CIOCTRL_LASTENDEN;
|
||||
else
|
||||
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)
|
||||
DRM_DEBUG_KMS("fmt[0x%x]\n", fmt);
|
||||
|
||||
/* RGB */
|
||||
cfg = fimc_read(EXYNOS_CISCCTRL);
|
||||
cfg = fimc_read(ctx, EXYNOS_CISCCTRL);
|
||||
cfg &= ~EXYNOS_CISCCTRL_INRGB_FMT_RGB_MASK;
|
||||
|
||||
switch (fmt) {
|
||||
case DRM_FORMAT_RGB565:
|
||||
cfg |= EXYNOS_CISCCTRL_INRGB_FMT_RGB565;
|
||||
fimc_write(cfg, EXYNOS_CISCCTRL);
|
||||
fimc_write(ctx, cfg, EXYNOS_CISCCTRL);
|
||||
return 0;
|
||||
case DRM_FORMAT_RGB888:
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
cfg |= EXYNOS_CISCCTRL_INRGB_FMT_RGB888;
|
||||
fimc_write(cfg, EXYNOS_CISCCTRL);
|
||||
fimc_write(ctx, cfg, EXYNOS_CISCCTRL);
|
||||
return 0;
|
||||
default:
|
||||
/* bypass */
|
||||
@ -439,7 +434,7 @@ static int fimc_src_set_fmt_order(struct fimc_context *ctx, u32 fmt)
|
||||
}
|
||||
|
||||
/* YUV */
|
||||
cfg = fimc_read(EXYNOS_MSCTRL);
|
||||
cfg = fimc_read(ctx, EXYNOS_MSCTRL);
|
||||
cfg &= ~(EXYNOS_MSCTRL_ORDER2P_SHIFT_MASK |
|
||||
EXYNOS_MSCTRL_C_INT_IN_2PLANE |
|
||||
EXYNOS_MSCTRL_ORDER422_YCBYCR);
|
||||
@ -479,7 +474,7 @@ static int fimc_src_set_fmt_order(struct fimc_context *ctx, u32 fmt)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fimc_write(cfg, EXYNOS_MSCTRL);
|
||||
fimc_write(ctx, cfg, EXYNOS_MSCTRL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -492,7 +487,7 @@ static int fimc_src_set_fmt(struct device *dev, u32 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;
|
||||
|
||||
switch (fmt) {
|
||||
@ -527,9 +522,9 @@ static int fimc_src_set_fmt(struct device *dev, u32 fmt)
|
||||
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;
|
||||
|
||||
if (fmt == DRM_FORMAT_NV12MT)
|
||||
@ -537,7 +532,7 @@ static int fimc_src_set_fmt(struct device *dev, u32 fmt)
|
||||
else
|
||||
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);
|
||||
}
|
||||
@ -552,11 +547,11 @@ static int fimc_src_set_transf(struct device *dev,
|
||||
|
||||
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 |
|
||||
EXYNOS_MSCTRL_FLIP_Y_MIRROR);
|
||||
|
||||
cfg2 = fimc_read(EXYNOS_CITRGFMT);
|
||||
cfg2 = fimc_read(ctx, EXYNOS_CITRGFMT);
|
||||
cfg2 &= ~EXYNOS_CITRGFMT_INROT90_CLOCKWISE;
|
||||
|
||||
switch (degree) {
|
||||
@ -595,8 +590,8 @@ static int fimc_src_set_transf(struct device *dev,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fimc_write(cfg1, EXYNOS_MSCTRL);
|
||||
fimc_write(cfg2, EXYNOS_CITRGFMT);
|
||||
fimc_write(ctx, cfg1, EXYNOS_MSCTRL);
|
||||
fimc_write(ctx, cfg2, EXYNOS_CITRGFMT);
|
||||
*swap = (cfg2 & EXYNOS_CITRGFMT_INROT90_CLOCKWISE) ? 1 : 0;
|
||||
|
||||
return 0;
|
||||
@ -621,17 +616,17 @@ static int fimc_set_window(struct fimc_context *ctx,
|
||||
* set window offset 1, 2 size
|
||||
* check figure 43-21 in user manual
|
||||
*/
|
||||
cfg = fimc_read(EXYNOS_CIWDOFST);
|
||||
cfg = fimc_read(ctx, EXYNOS_CIWDOFST);
|
||||
cfg &= ~(EXYNOS_CIWDOFST_WINHOROFST_MASK |
|
||||
EXYNOS_CIWDOFST_WINVEROFST_MASK);
|
||||
cfg |= (EXYNOS_CIWDOFST_WINHOROFST(h1) |
|
||||
EXYNOS_CIWDOFST_WINVEROFST(v1));
|
||||
cfg |= EXYNOS_CIWDOFST_WINOFSEN;
|
||||
fimc_write(cfg, EXYNOS_CIWDOFST);
|
||||
fimc_write(ctx, cfg, EXYNOS_CIWDOFST);
|
||||
|
||||
cfg = (EXYNOS_CIWDOFST2_WINHOROFST2(h2) |
|
||||
EXYNOS_CIWDOFST2_WINVEROFST2(v2));
|
||||
fimc_write(cfg, EXYNOS_CIWDOFST2);
|
||||
fimc_write(ctx, cfg, EXYNOS_CIWDOFST2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -651,7 +646,7 @@ static int fimc_src_set_size(struct device *dev, int swap,
|
||||
cfg = (EXYNOS_ORGISIZE_HORIZONTAL(img_sz.hsize) |
|
||||
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);
|
||||
|
||||
@ -663,12 +658,12 @@ static int fimc_src_set_size(struct device *dev, int swap,
|
||||
}
|
||||
|
||||
/* set input DMA image size */
|
||||
cfg = fimc_read(EXYNOS_CIREAL_ISIZE);
|
||||
cfg = fimc_read(ctx, EXYNOS_CIREAL_ISIZE);
|
||||
cfg &= ~(EXYNOS_CIREAL_ISIZE_HEIGHT_MASK |
|
||||
EXYNOS_CIREAL_ISIZE_WIDTH_MASK);
|
||||
cfg |= (EXYNOS_CIREAL_ISIZE_WIDTH(img_pos.w) |
|
||||
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
|
||||
@ -677,18 +672,18 @@ static int fimc_src_set_size(struct device *dev, int swap,
|
||||
cfg = (EXYNOS_CISRCFMT_ITU601_8BIT |
|
||||
EXYNOS_CISRCFMT_SOURCEHSIZE(img_sz.hsize) |
|
||||
EXYNOS_CISRCFMT_SOURCEVSIZE(img_sz.vsize));
|
||||
fimc_write(cfg, EXYNOS_CISRCFMT);
|
||||
fimc_write(ctx, cfg, EXYNOS_CISRCFMT);
|
||||
|
||||
/* offset Y(RGB), Cb, Cr */
|
||||
cfg = (EXYNOS_CIIYOFF_HORIZONTAL(img_pos.x) |
|
||||
EXYNOS_CIIYOFF_VERTICAL(img_pos.y));
|
||||
fimc_write(cfg, EXYNOS_CIIYOFF);
|
||||
fimc_write(ctx, cfg, EXYNOS_CIIYOFF);
|
||||
cfg = (EXYNOS_CIICBOFF_HORIZONTAL(img_pos.x) |
|
||||
EXYNOS_CIICBOFF_VERTICAL(img_pos.y));
|
||||
fimc_write(cfg, EXYNOS_CIICBOFF);
|
||||
fimc_write(ctx, cfg, EXYNOS_CIICBOFF);
|
||||
cfg = (EXYNOS_CIICROFF_HORIZONTAL(img_pos.x) |
|
||||
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);
|
||||
}
|
||||
@ -722,25 +717,25 @@ static int fimc_src_set_addr(struct device *dev,
|
||||
switch (buf_type) {
|
||||
case IPP_BUF_ENQUEUE:
|
||||
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));
|
||||
|
||||
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));
|
||||
fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB],
|
||||
fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CB],
|
||||
EXYNOS_CIICRSA(buf_id));
|
||||
} else {
|
||||
fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB],
|
||||
fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CB],
|
||||
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));
|
||||
}
|
||||
break;
|
||||
case IPP_BUF_DEQUEUE:
|
||||
fimc_write(0x0, EXYNOS_CIIYSA(buf_id));
|
||||
fimc_write(0x0, EXYNOS_CIICBSA(buf_id));
|
||||
fimc_write(0x0, EXYNOS_CIICRSA(buf_id));
|
||||
fimc_write(ctx, 0x0, EXYNOS_CIIYSA(buf_id));
|
||||
fimc_write(ctx, 0x0, EXYNOS_CIICBSA(buf_id));
|
||||
fimc_write(ctx, 0x0, EXYNOS_CIICRSA(buf_id));
|
||||
break;
|
||||
default:
|
||||
/* bypass */
|
||||
@ -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);
|
||||
|
||||
/* RGB */
|
||||
cfg = fimc_read(EXYNOS_CISCCTRL);
|
||||
cfg = fimc_read(ctx, EXYNOS_CISCCTRL);
|
||||
cfg &= ~EXYNOS_CISCCTRL_OUTRGB_FMT_RGB_MASK;
|
||||
|
||||
switch (fmt) {
|
||||
case DRM_FORMAT_RGB565:
|
||||
cfg |= EXYNOS_CISCCTRL_OUTRGB_FMT_RGB565;
|
||||
fimc_write(cfg, EXYNOS_CISCCTRL);
|
||||
fimc_write(ctx, cfg, EXYNOS_CISCCTRL);
|
||||
return 0;
|
||||
case DRM_FORMAT_RGB888:
|
||||
cfg |= EXYNOS_CISCCTRL_OUTRGB_FMT_RGB888;
|
||||
fimc_write(cfg, EXYNOS_CISCCTRL);
|
||||
fimc_write(ctx, cfg, EXYNOS_CISCCTRL);
|
||||
return 0;
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
cfg |= (EXYNOS_CISCCTRL_OUTRGB_FMT_RGB888 |
|
||||
EXYNOS_CISCCTRL_EXTRGB_EXTENSION);
|
||||
fimc_write(cfg, EXYNOS_CISCCTRL);
|
||||
fimc_write(ctx, cfg, EXYNOS_CISCCTRL);
|
||||
break;
|
||||
default:
|
||||
/* bypass */
|
||||
@ -788,7 +783,7 @@ static int fimc_dst_set_fmt_order(struct fimc_context *ctx, u32 fmt)
|
||||
}
|
||||
|
||||
/* YUV */
|
||||
cfg = fimc_read(EXYNOS_CIOCTRL);
|
||||
cfg = fimc_read(ctx, EXYNOS_CIOCTRL);
|
||||
cfg &= ~(EXYNOS_CIOCTRL_ORDER2P_MASK |
|
||||
EXYNOS_CIOCTRL_ORDER422_MASK |
|
||||
EXYNOS_CIOCTRL_YCBCR_PLANE_MASK);
|
||||
@ -830,7 +825,7 @@ static int fimc_dst_set_fmt_order(struct fimc_context *ctx, u32 fmt)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fimc_write(cfg, EXYNOS_CIOCTRL);
|
||||
fimc_write(ctx, cfg, EXYNOS_CIOCTRL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -843,16 +838,16 @@ static int fimc_dst_set_fmt(struct device *dev, u32 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) {
|
||||
cfg |= EXYNOS_CIEXTEN_YUV444_OUT;
|
||||
fimc_write(cfg, EXYNOS_CIEXTEN);
|
||||
fimc_write(ctx, cfg, EXYNOS_CIEXTEN);
|
||||
} else {
|
||||
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;
|
||||
|
||||
switch (fmt) {
|
||||
@ -885,10 +880,10 @@ static int fimc_dst_set_fmt(struct device *dev, u32 fmt)
|
||||
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;
|
||||
|
||||
if (fmt == DRM_FORMAT_NV12MT)
|
||||
@ -896,7 +891,7 @@ static int fimc_dst_set_fmt(struct device *dev, u32 fmt)
|
||||
else
|
||||
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);
|
||||
}
|
||||
@ -911,7 +906,7 @@ static int fimc_dst_set_transf(struct device *dev,
|
||||
|
||||
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_OUTROT90_CLOCKWISE;
|
||||
|
||||
@ -951,53 +946,23 @@ static int fimc_dst_set_transf(struct device *dev,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fimc_write(cfg, EXYNOS_CITRGFMT);
|
||||
fimc_write(ctx, cfg, EXYNOS_CITRGFMT);
|
||||
*swap = (cfg & EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE) ? 1 : 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,
|
||||
struct drm_exynos_pos *src, struct drm_exynos_pos *dst)
|
||||
{
|
||||
struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
|
||||
u32 cfg, cfg_ext, shfactor;
|
||||
u32 pre_dst_width, pre_dst_height;
|
||||
u32 pre_hratio, hfactor, pre_vratio, vfactor;
|
||||
u32 hfactor, vfactor;
|
||||
int ret = 0;
|
||||
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) {
|
||||
src_w = src->h;
|
||||
src_h = src->w;
|
||||
@ -1014,24 +979,24 @@ static int fimc_set_prescaler(struct fimc_context *ctx, struct fimc_scaler *sc,
|
||||
dst_h = dst->h;
|
||||
}
|
||||
|
||||
ret = fimc_get_ratio_shift(src_w, dst_w, &pre_hratio, &hfactor);
|
||||
if (ret) {
|
||||
/* fimc_ippdrv_check_property assures that dividers are not null */
|
||||
hfactor = fls(src_w / dst_w / 2);
|
||||
if (hfactor > FIMC_SHFACTOR / 2) {
|
||||
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);
|
||||
if (ret) {
|
||||
vfactor = fls(src_h / dst_h / 2);
|
||||
if (vfactor > FIMC_SHFACTOR / 2) {
|
||||
dev_err(ippdrv->dev, "failed to get ratio vertical.\n");
|
||||
return ret;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pre_dst_width = src_w / pre_hratio;
|
||||
pre_dst_height = src_h / pre_vratio;
|
||||
pre_dst_width = src_w >> hfactor;
|
||||
pre_dst_height = src_h >> vfactor;
|
||||
DRM_DEBUG_KMS("pre_dst_width[%d]pre_dst_height[%d]\n",
|
||||
pre_dst_width, pre_dst_height);
|
||||
DRM_DEBUG_KMS("pre_hratio[%d]hfactor[%d]pre_vratio[%d]vfactor[%d]\n",
|
||||
pre_hratio, hfactor, pre_vratio, vfactor);
|
||||
DRM_DEBUG_KMS("hfactor[%d]vfactor[%d]\n", hfactor, vfactor);
|
||||
|
||||
sc->hratio = (src_w << 14) / (dst_w << hfactor);
|
||||
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,
|
||||
DRM_DEBUG_KMS("shfactor[%d]\n", shfactor);
|
||||
|
||||
cfg = (EXYNOS_CISCPRERATIO_SHFACTOR(shfactor) |
|
||||
EXYNOS_CISCPRERATIO_PREHORRATIO(pre_hratio) |
|
||||
EXYNOS_CISCPRERATIO_PREVERRATIO(pre_vratio));
|
||||
fimc_write(cfg, EXYNOS_CISCPRERATIO);
|
||||
EXYNOS_CISCPRERATIO_PREHORRATIO(1 << hfactor) |
|
||||
EXYNOS_CISCPRERATIO_PREVERRATIO(1 << vfactor));
|
||||
fimc_write(ctx, cfg, EXYNOS_CISCPRERATIO);
|
||||
|
||||
cfg = (EXYNOS_CISCPREDST_PREDSTWIDTH(pre_dst_width) |
|
||||
EXYNOS_CISCPREDST_PREDSTHEIGHT(pre_dst_height));
|
||||
fimc_write(cfg, EXYNOS_CISCPREDST);
|
||||
fimc_write(ctx, cfg, EXYNOS_CISCPREDST);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -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",
|
||||
sc->hratio, sc->vratio);
|
||||
|
||||
cfg = fimc_read(EXYNOS_CISCCTRL);
|
||||
cfg = fimc_read(ctx, EXYNOS_CISCCTRL);
|
||||
cfg &= ~(EXYNOS_CISCCTRL_SCALERBYPASS |
|
||||
EXYNOS_CISCCTRL_SCALEUP_H | EXYNOS_CISCCTRL_SCALEUP_V |
|
||||
EXYNOS_CISCCTRL_MAIN_V_RATIO_MASK |
|
||||
@ -1084,14 +1049,14 @@ static void fimc_set_scaler(struct fimc_context *ctx, struct fimc_scaler *sc)
|
||||
|
||||
cfg |= (EXYNOS_CISCCTRL_MAINHORRATIO((sc->hratio >> 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_MAINVERRATIO_EXT_MASK;
|
||||
cfg_ext |= (EXYNOS_CIEXTEN_MAINHORRATIO_EXT(sc->hratio) |
|
||||
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,
|
||||
@ -1109,12 +1074,12 @@ static int fimc_dst_set_size(struct device *dev, int swap,
|
||||
cfg = (EXYNOS_ORGOSIZE_HORIZONTAL(img_sz.hsize) |
|
||||
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);
|
||||
|
||||
/* CSC ITU */
|
||||
cfg = fimc_read(EXYNOS_CIGCTRL);
|
||||
cfg = fimc_read(ctx, EXYNOS_CIGCTRL);
|
||||
cfg &= ~EXYNOS_CIGCTRL_CSC_MASK;
|
||||
|
||||
if (sz->hsize >= FIMC_WIDTH_ITU_709)
|
||||
@ -1122,7 +1087,7 @@ static int fimc_dst_set_size(struct device *dev, int swap,
|
||||
else
|
||||
cfg |= EXYNOS_CIGCTRL_CSC_ITU601;
|
||||
|
||||
fimc_write(cfg, EXYNOS_CIGCTRL);
|
||||
fimc_write(ctx, cfg, EXYNOS_CIGCTRL);
|
||||
|
||||
if (swap) {
|
||||
img_pos.w = pos->h;
|
||||
@ -1132,41 +1097,38 @@ static int fimc_dst_set_size(struct device *dev, int swap,
|
||||
}
|
||||
|
||||
/* target image size */
|
||||
cfg = fimc_read(EXYNOS_CITRGFMT);
|
||||
cfg = fimc_read(ctx, EXYNOS_CITRGFMT);
|
||||
cfg &= ~(EXYNOS_CITRGFMT_TARGETH_MASK |
|
||||
EXYNOS_CITRGFMT_TARGETV_MASK);
|
||||
cfg |= (EXYNOS_CITRGFMT_TARGETHSIZE(img_pos.w) |
|
||||
EXYNOS_CITRGFMT_TARGETVSIZE(img_pos.h));
|
||||
fimc_write(cfg, EXYNOS_CITRGFMT);
|
||||
fimc_write(ctx, cfg, EXYNOS_CITRGFMT);
|
||||
|
||||
/* target area */
|
||||
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 */
|
||||
cfg = (EXYNOS_CIOYOFF_HORIZONTAL(img_pos.x) |
|
||||
EXYNOS_CIOYOFF_VERTICAL(img_pos.y));
|
||||
fimc_write(cfg, EXYNOS_CIOYOFF);
|
||||
fimc_write(ctx, cfg, EXYNOS_CIOYOFF);
|
||||
cfg = (EXYNOS_CIOCBOFF_HORIZONTAL(img_pos.x) |
|
||||
EXYNOS_CIOCBOFF_VERTICAL(img_pos.y));
|
||||
fimc_write(cfg, EXYNOS_CIOCBOFF);
|
||||
fimc_write(ctx, cfg, EXYNOS_CIOCBOFF);
|
||||
cfg = (EXYNOS_CIOCROFF_HORIZONTAL(img_pos.x) |
|
||||
EXYNOS_CIOCROFF_VERTICAL(img_pos.y));
|
||||
fimc_write(cfg, EXYNOS_CIOCROFF);
|
||||
fimc_write(ctx, cfg, EXYNOS_CIOCROFF);
|
||||
|
||||
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 mask = 0x00000001;
|
||||
u32 cfg, buf_num;
|
||||
|
||||
cfg = fimc_read(EXYNOS_CIFCNTSEQ);
|
||||
cfg = fimc_read(ctx, EXYNOS_CIFCNTSEQ);
|
||||
|
||||
for (i = 0; i < FIMC_REG_SZ; i++)
|
||||
if (cfg & (mask << i))
|
||||
buf_num++;
|
||||
buf_num = hweight32(cfg);
|
||||
|
||||
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,
|
||||
u32 cfg;
|
||||
u32 mask = 0x00000001 << buf_id;
|
||||
int ret = 0;
|
||||
unsigned long flags;
|
||||
|
||||
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 */
|
||||
cfg = fimc_read(EXYNOS_CIFCNTSEQ);
|
||||
cfg = fimc_read(ctx, EXYNOS_CIFCNTSEQ);
|
||||
|
||||
switch (buf_type) {
|
||||
case IPP_BUF_ENQUEUE:
|
||||
@ -1205,20 +1168,20 @@ static int fimc_dst_set_buf_seq(struct fimc_context *ctx, u32 buf_id,
|
||||
/* sequence id */
|
||||
cfg &= ~mask;
|
||||
cfg |= (enable << buf_id);
|
||||
fimc_write(cfg, EXYNOS_CIFCNTSEQ);
|
||||
fimc_write(ctx, cfg, EXYNOS_CIFCNTSEQ);
|
||||
|
||||
/* interrupt enable */
|
||||
if (buf_type == IPP_BUF_ENQUEUE &&
|
||||
fimc_dst_get_buf_seq(ctx) >= FIMC_BUF_START)
|
||||
fimc_handle_irq(ctx, true, false, true);
|
||||
fimc_dst_get_buf_count(ctx) >= FIMC_BUF_START)
|
||||
fimc_mask_irq(ctx, true);
|
||||
|
||||
/* interrupt disable */
|
||||
if (buf_type == IPP_BUF_DEQUEUE &&
|
||||
fimc_dst_get_buf_seq(ctx) <= FIMC_BUF_STOP)
|
||||
fimc_handle_irq(ctx, false, false, true);
|
||||
fimc_dst_get_buf_count(ctx) <= FIMC_BUF_STOP)
|
||||
fimc_mask_irq(ctx, false);
|
||||
|
||||
err_unlock:
|
||||
mutex_unlock(&ctx->lock);
|
||||
spin_unlock_irqrestore(&ctx->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1252,25 +1215,25 @@ static int fimc_dst_set_addr(struct device *dev,
|
||||
case IPP_BUF_ENQUEUE:
|
||||
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));
|
||||
|
||||
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));
|
||||
fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB],
|
||||
fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CB],
|
||||
EXYNOS_CIOCRSA(buf_id));
|
||||
} else {
|
||||
fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB],
|
||||
fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CB],
|
||||
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));
|
||||
}
|
||||
break;
|
||||
case IPP_BUF_DEQUEUE:
|
||||
fimc_write(0x0, EXYNOS_CIOYSA(buf_id));
|
||||
fimc_write(0x0, EXYNOS_CIOCBSA(buf_id));
|
||||
fimc_write(0x0, EXYNOS_CIOCRSA(buf_id));
|
||||
fimc_write(ctx, 0x0, EXYNOS_CIOYSA(buf_id));
|
||||
fimc_write(ctx, 0x0, EXYNOS_CIOCBSA(buf_id));
|
||||
fimc_write(ctx, 0x0, EXYNOS_CIOCRSA(buf_id));
|
||||
break;
|
||||
default:
|
||||
/* bypass */
|
||||
@ -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)
|
||||
{
|
||||
struct drm_exynos_ipp_prop_list *prop_list;
|
||||
|
||||
prop_list = devm_kzalloc(ippdrv->dev, sizeof(*prop_list), GFP_KERNEL);
|
||||
if (!prop_list)
|
||||
return -ENOMEM;
|
||||
struct drm_exynos_ipp_prop_list *prop_list = &ippdrv->prop_list;
|
||||
|
||||
prop_list->version = 1;
|
||||
prop_list->writeback = 1;
|
||||
@ -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.vsize = FIMC_SCALE_MIN;
|
||||
|
||||
ippdrv->prop_list = prop_list;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1395,7 +1352,7 @@ static int fimc_ippdrv_check_property(struct device *dev,
|
||||
{
|
||||
struct fimc_context *ctx = get_fimc_context(dev);
|
||||
struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
|
||||
struct drm_exynos_ipp_prop_list *pp = ippdrv->prop_list;
|
||||
struct drm_exynos_ipp_prop_list *pp = &ippdrv->prop_list;
|
||||
struct drm_exynos_ipp_config *config;
|
||||
struct drm_exynos_pos *pos;
|
||||
struct drm_exynos_sz *sz;
|
||||
@ -1508,15 +1465,15 @@ static void fimc_clear_addr(struct fimc_context *ctx)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < FIMC_MAX_SRC; i++) {
|
||||
fimc_write(0, EXYNOS_CIIYSA(i));
|
||||
fimc_write(0, EXYNOS_CIICBSA(i));
|
||||
fimc_write(0, EXYNOS_CIICRSA(i));
|
||||
fimc_write(ctx, 0, EXYNOS_CIIYSA(i));
|
||||
fimc_write(ctx, 0, EXYNOS_CIICBSA(i));
|
||||
fimc_write(ctx, 0, EXYNOS_CIICRSA(i));
|
||||
}
|
||||
|
||||
for (i = 0; i < FIMC_MAX_DST; i++) {
|
||||
fimc_write(0, EXYNOS_CIOYSA(i));
|
||||
fimc_write(0, EXYNOS_CIOCBSA(i));
|
||||
fimc_write(0, EXYNOS_CIOCRSA(i));
|
||||
fimc_write(ctx, 0, EXYNOS_CIOYSA(i));
|
||||
fimc_write(ctx, 0, EXYNOS_CIOCBSA(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)
|
||||
|
||||
property = &c_node->property;
|
||||
|
||||
fimc_handle_irq(ctx, true, false, true);
|
||||
fimc_mask_irq(ctx, true);
|
||||
|
||||
for_each_ipp_ops(i) {
|
||||
config = &property->config[i];
|
||||
@ -1582,10 +1539,10 @@ static int fimc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd)
|
||||
fimc_handle_lastend(ctx, false);
|
||||
|
||||
/* setup dma */
|
||||
cfg0 = fimc_read(EXYNOS_MSCTRL);
|
||||
cfg0 = fimc_read(ctx, EXYNOS_MSCTRL);
|
||||
cfg0 &= ~EXYNOS_MSCTRL_INPUT_MASK;
|
||||
cfg0 |= EXYNOS_MSCTRL_INPUT_MEMORY;
|
||||
fimc_write(cfg0, EXYNOS_MSCTRL);
|
||||
fimc_write(ctx, cfg0, EXYNOS_MSCTRL);
|
||||
break;
|
||||
case IPP_CMD_WB:
|
||||
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)
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
/* Scaler */
|
||||
cfg1 = fimc_read(EXYNOS_CISCCTRL);
|
||||
cfg1 = fimc_read(ctx, EXYNOS_CISCCTRL);
|
||||
cfg1 &= ~EXYNOS_CISCCTRL_SCAN_MASK;
|
||||
cfg1 |= (EXYNOS_CISCCTRL_PROGRESSIVE |
|
||||
EXYNOS_CISCCTRL_SCALERSTART);
|
||||
|
||||
fimc_write(cfg1, EXYNOS_CISCCTRL);
|
||||
fimc_write(ctx, cfg1, EXYNOS_CISCCTRL);
|
||||
|
||||
/* Enable image capture*/
|
||||
cfg0 |= EXYNOS_CIIMGCPT_IMGCPTEN;
|
||||
fimc_write(cfg0, EXYNOS_CIIMGCPT);
|
||||
fimc_write(ctx, cfg0, EXYNOS_CIIMGCPT);
|
||||
|
||||
/* Disable frame end irq */
|
||||
cfg0 = fimc_read(EXYNOS_CIGCTRL);
|
||||
cfg0 &= ~EXYNOS_CIGCTRL_IRQ_END_DISABLE;
|
||||
fimc_write(cfg0, EXYNOS_CIGCTRL);
|
||||
fimc_clear_bits(ctx, EXYNOS_CIGCTRL, EXYNOS_CIGCTRL_IRQ_END_DISABLE);
|
||||
|
||||
cfg0 = fimc_read(EXYNOS_CIOCTRL);
|
||||
cfg0 &= ~EXYNOS_CIOCTRL_WEAVE_MASK;
|
||||
fimc_write(cfg0, EXYNOS_CIOCTRL);
|
||||
fimc_clear_bits(ctx, EXYNOS_CIOCTRL, EXYNOS_CIOCTRL_WEAVE_MASK);
|
||||
|
||||
if (cmd == IPP_CMD_M2M) {
|
||||
cfg0 = fimc_read(EXYNOS_MSCTRL);
|
||||
cfg0 |= EXYNOS_MSCTRL_ENVID;
|
||||
fimc_write(cfg0, EXYNOS_MSCTRL);
|
||||
fimc_set_bits(ctx, EXYNOS_MSCTRL, EXYNOS_MSCTRL_ENVID);
|
||||
|
||||
cfg0 = fimc_read(EXYNOS_MSCTRL);
|
||||
cfg0 |= EXYNOS_MSCTRL_ENVID;
|
||||
fimc_write(cfg0, EXYNOS_MSCTRL);
|
||||
fimc_set_bits(ctx, EXYNOS_MSCTRL, EXYNOS_MSCTRL_ENVID);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1661,10 +1610,10 @@ static void fimc_ippdrv_stop(struct device *dev, enum drm_exynos_ipp_cmd cmd)
|
||||
switch (cmd) {
|
||||
case IPP_CMD_M2M:
|
||||
/* Source clear */
|
||||
cfg = fimc_read(EXYNOS_MSCTRL);
|
||||
cfg = fimc_read(ctx, EXYNOS_MSCTRL);
|
||||
cfg &= ~EXYNOS_MSCTRL_INPUT_MASK;
|
||||
cfg &= ~EXYNOS_MSCTRL_ENVID;
|
||||
fimc_write(cfg, EXYNOS_MSCTRL);
|
||||
fimc_write(ctx, cfg, EXYNOS_MSCTRL);
|
||||
break;
|
||||
case IPP_CMD_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)
|
||||
break;
|
||||
}
|
||||
|
||||
fimc_handle_irq(ctx, false, false, true);
|
||||
fimc_mask_irq(ctx, false);
|
||||
|
||||
/* reset sequence */
|
||||
fimc_write(0x0, EXYNOS_CIFCNTSEQ);
|
||||
fimc_write(ctx, 0x0, EXYNOS_CIFCNTSEQ);
|
||||
|
||||
/* Scaler disable */
|
||||
cfg = fimc_read(EXYNOS_CISCCTRL);
|
||||
cfg &= ~EXYNOS_CISCCTRL_SCALERSTART;
|
||||
fimc_write(cfg, EXYNOS_CISCCTRL);
|
||||
fimc_clear_bits(ctx, EXYNOS_CISCCTRL, EXYNOS_CISCCTRL_SCALERSTART);
|
||||
|
||||
/* Disable image capture */
|
||||
cfg = fimc_read(EXYNOS_CIIMGCPT);
|
||||
cfg &= ~(EXYNOS_CIIMGCPT_IMGCPTEN_SC | EXYNOS_CIIMGCPT_IMGCPTEN);
|
||||
fimc_write(cfg, EXYNOS_CIIMGCPT);
|
||||
fimc_clear_bits(ctx, EXYNOS_CIIMGCPT,
|
||||
EXYNOS_CIIMGCPT_IMGCPTEN_SC | EXYNOS_CIIMGCPT_IMGCPTEN);
|
||||
|
||||
/* Enable frame end irq */
|
||||
cfg = fimc_read(EXYNOS_CIGCTRL);
|
||||
cfg |= EXYNOS_CIGCTRL_IRQ_END_DISABLE;
|
||||
fimc_write(cfg, EXYNOS_CIGCTRL);
|
||||
fimc_set_bits(ctx, EXYNOS_CIGCTRL, EXYNOS_CIGCTRL_IRQ_END_DISABLE);
|
||||
}
|
||||
|
||||
static void fimc_put_clocks(struct fimc_context *ctx)
|
||||
@ -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);
|
||||
|
||||
mutex_init(&ctx->lock);
|
||||
spin_lock_init(&ctx->lock);
|
||||
platform_set_drvdata(pdev, ctx);
|
||||
|
||||
pm_runtime_set_active(dev);
|
||||
@ -1879,7 +1823,6 @@ static int fimc_remove(struct platform_device *pdev)
|
||||
struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
|
||||
|
||||
exynos_drm_ippdrv_unregister(ippdrv);
|
||||
mutex_destroy(&ctx->lock);
|
||||
|
||||
fimc_put_clocks(ctx);
|
||||
pm_runtime_set_suspended(dev);
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/component.h>
|
||||
|
||||
#include <video/of_display_timing.h>
|
||||
#include <video/of_videomode.h>
|
||||
@ -38,6 +39,7 @@
|
||||
*/
|
||||
|
||||
#define FIMD_DEFAULT_FRAMERATE 60
|
||||
#define MIN_FB_WIDTH_FOR_16WORD_BURST 128
|
||||
|
||||
/* position control register for hardware window 0, 2 ~ 4.*/
|
||||
#define VIDOSD_A(win) (VIDOSD_BASE + 0x00 + (win) * 16)
|
||||
@ -122,6 +124,7 @@ struct fimd_context {
|
||||
|
||||
struct exynos_drm_panel_info panel;
|
||||
struct fimd_driver_data *driver_data;
|
||||
struct exynos_drm_display *display;
|
||||
};
|
||||
|
||||
static const struct of_device_id fimd_driver_dt_match[] = {
|
||||
@ -143,13 +146,57 @@ static inline struct fimd_driver_data *drm_fimd_get_driver_data(
|
||||
return (struct fimd_driver_data *)of_id->data;
|
||||
}
|
||||
|
||||
static int fimd_mgr_initialize(struct exynos_drm_manager *mgr,
|
||||
struct drm_device *drm_dev, int pipe)
|
||||
static void fimd_wait_for_vblank(struct exynos_drm_manager *mgr)
|
||||
{
|
||||
struct fimd_context *ctx = mgr->ctx;
|
||||
|
||||
ctx->drm_dev = drm_dev;
|
||||
ctx->pipe = pipe;
|
||||
if (ctx->suspended)
|
||||
return;
|
||||
|
||||
atomic_set(&ctx->wait_vsync_event, 1);
|
||||
|
||||
/*
|
||||
* wait for FIMD to signal VSYNC interrupt or return after
|
||||
* timeout which is set to 50ms (refresh rate of 20).
|
||||
*/
|
||||
if (!wait_event_timeout(ctx->wait_vsync_queue,
|
||||
!atomic_read(&ctx->wait_vsync_event),
|
||||
HZ/20))
|
||||
DRM_DEBUG_KMS("vblank wait timed out.\n");
|
||||
}
|
||||
|
||||
|
||||
static void fimd_clear_channel(struct exynos_drm_manager *mgr)
|
||||
{
|
||||
struct fimd_context *ctx = mgr->ctx;
|
||||
int win, ch_enabled = 0;
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
/* Check if any channel is enabled. */
|
||||
for (win = 0; win < WINDOWS_NR; win++) {
|
||||
u32 val = readl(ctx->regs + SHADOWCON);
|
||||
if (val & SHADOWCON_CHx_ENABLE(win)) {
|
||||
val &= ~SHADOWCON_CHx_ENABLE(win);
|
||||
writel(val, ctx->regs + SHADOWCON);
|
||||
ch_enabled = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait for vsync, as disable channel takes effect at next vsync */
|
||||
if (ch_enabled)
|
||||
fimd_wait_for_vblank(mgr);
|
||||
}
|
||||
|
||||
static int fimd_mgr_initialize(struct exynos_drm_manager *mgr,
|
||||
struct drm_device *drm_dev)
|
||||
{
|
||||
struct fimd_context *ctx = mgr->ctx;
|
||||
struct exynos_drm_private *priv;
|
||||
priv = drm_dev->dev_private;
|
||||
|
||||
mgr->drm_dev = ctx->drm_dev = drm_dev;
|
||||
mgr->pipe = ctx->pipe = priv->pipe++;
|
||||
|
||||
/*
|
||||
* enable drm irq mode.
|
||||
@ -169,8 +216,14 @@ static int fimd_mgr_initialize(struct exynos_drm_manager *mgr,
|
||||
drm_dev->vblank_disable_allowed = true;
|
||||
|
||||
/* attach this sub driver to iommu mapping if supported. */
|
||||
if (is_drm_iommu_supported(ctx->drm_dev))
|
||||
if (is_drm_iommu_supported(ctx->drm_dev)) {
|
||||
/*
|
||||
* If any channel is already active, iommu will throw
|
||||
* a PAGE FAULT when enabled. So clear any channel if enabled.
|
||||
*/
|
||||
fimd_clear_channel(mgr);
|
||||
drm_iommu_attach_device(ctx->drm_dev, ctx->dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -324,25 +377,6 @@ static void fimd_disable_vblank(struct exynos_drm_manager *mgr)
|
||||
}
|
||||
}
|
||||
|
||||
static void fimd_wait_for_vblank(struct exynos_drm_manager *mgr)
|
||||
{
|
||||
struct fimd_context *ctx = mgr->ctx;
|
||||
|
||||
if (ctx->suspended)
|
||||
return;
|
||||
|
||||
atomic_set(&ctx->wait_vsync_event, 1);
|
||||
|
||||
/*
|
||||
* wait for FIMD to signal VSYNC interrupt or return after
|
||||
* timeout which is set to 50ms (refresh rate of 20).
|
||||
*/
|
||||
if (!wait_event_timeout(ctx->wait_vsync_queue,
|
||||
!atomic_read(&ctx->wait_vsync_event),
|
||||
HZ/20))
|
||||
DRM_DEBUG_KMS("vblank wait timed out.\n");
|
||||
}
|
||||
|
||||
static void fimd_win_mode_set(struct exynos_drm_manager *mgr,
|
||||
struct exynos_drm_overlay *overlay)
|
||||
{
|
||||
@ -446,6 +480,19 @@ static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win)
|
||||
|
||||
DRM_DEBUG_KMS("bpp = %d\n", win_data->bpp);
|
||||
|
||||
/*
|
||||
* In case of exynos, setting dma-burst to 16Word causes permanent
|
||||
* tearing for very small buffers, e.g. cursor buffer. Burst Mode
|
||||
* switching which is based on overlay size is not recommended as
|
||||
* overlay size varies alot towards the end of the screen and rapid
|
||||
* movement causes unstable DMA which results into iommu crash/tear.
|
||||
*/
|
||||
|
||||
if (win_data->fb_width < MIN_FB_WIDTH_FOR_16WORD_BURST) {
|
||||
val &= ~WINCONx_BURSTLEN_MASK;
|
||||
val |= WINCONx_BURSTLEN_4WORD;
|
||||
}
|
||||
|
||||
writel(val, ctx->regs + WINCON(win));
|
||||
}
|
||||
|
||||
@ -656,19 +703,6 @@ static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos)
|
||||
win_data->enabled = false;
|
||||
}
|
||||
|
||||
static void fimd_clear_win(struct fimd_context *ctx, int win)
|
||||
{
|
||||
writel(0, ctx->regs + WINCON(win));
|
||||
writel(0, ctx->regs + VIDOSD_A(win));
|
||||
writel(0, ctx->regs + VIDOSD_B(win));
|
||||
writel(0, ctx->regs + VIDOSD_C(win));
|
||||
|
||||
if (win == 1 || win == 2)
|
||||
writel(0, ctx->regs + VIDOSD_D(win));
|
||||
|
||||
fimd_shadow_protect_win(ctx, win, false);
|
||||
}
|
||||
|
||||
static void fimd_window_suspend(struct exynos_drm_manager *mgr)
|
||||
{
|
||||
struct fimd_context *ctx = mgr->ctx;
|
||||
@ -803,8 +837,6 @@ static void fimd_dpms(struct exynos_drm_manager *mgr, int mode)
|
||||
}
|
||||
|
||||
static struct exynos_drm_manager_ops fimd_manager_ops = {
|
||||
.initialize = fimd_mgr_initialize,
|
||||
.remove = fimd_mgr_remove,
|
||||
.dpms = fimd_dpms,
|
||||
.mode_fixup = fimd_mode_fixup,
|
||||
.mode_set = fimd_mode_set,
|
||||
@ -849,20 +881,64 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int fimd_bind(struct device *dev, struct device *master, void *data)
|
||||
{
|
||||
struct fimd_context *ctx = fimd_manager.ctx;
|
||||
struct drm_device *drm_dev = data;
|
||||
|
||||
fimd_mgr_initialize(&fimd_manager, drm_dev);
|
||||
exynos_drm_crtc_create(&fimd_manager);
|
||||
if (ctx->display)
|
||||
exynos_drm_create_enc_conn(drm_dev, ctx->display);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static void fimd_unbind(struct device *dev, struct device *master,
|
||||
void *data)
|
||||
{
|
||||
struct exynos_drm_manager *mgr = dev_get_drvdata(dev);
|
||||
struct fimd_context *ctx = fimd_manager.ctx;
|
||||
struct drm_crtc *crtc = mgr->crtc;
|
||||
|
||||
fimd_dpms(mgr, DRM_MODE_DPMS_OFF);
|
||||
|
||||
if (ctx->display)
|
||||
exynos_dpi_remove(dev);
|
||||
|
||||
fimd_mgr_remove(mgr);
|
||||
|
||||
crtc->funcs->destroy(crtc);
|
||||
}
|
||||
|
||||
static const struct component_ops fimd_component_ops = {
|
||||
.bind = fimd_bind,
|
||||
.unbind = fimd_unbind,
|
||||
};
|
||||
|
||||
static int fimd_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct fimd_context *ctx;
|
||||
struct resource *res;
|
||||
int win;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (!dev->of_node)
|
||||
return -ENODEV;
|
||||
ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC,
|
||||
fimd_manager.type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!dev->of_node) {
|
||||
ret = -ENODEV;
|
||||
goto err_del_component;
|
||||
}
|
||||
|
||||
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
if (!ctx) {
|
||||
ret = -ENOMEM;
|
||||
goto err_del_component;
|
||||
}
|
||||
|
||||
ctx->dev = dev;
|
||||
ctx->suspended = true;
|
||||
@ -875,32 +951,37 @@ static int fimd_probe(struct platform_device *pdev)
|
||||
ctx->bus_clk = devm_clk_get(dev, "fimd");
|
||||
if (IS_ERR(ctx->bus_clk)) {
|
||||
dev_err(dev, "failed to get bus clock\n");
|
||||
return PTR_ERR(ctx->bus_clk);
|
||||
ret = PTR_ERR(ctx->bus_clk);
|
||||
goto err_del_component;
|
||||
}
|
||||
|
||||
ctx->lcd_clk = devm_clk_get(dev, "sclk_fimd");
|
||||
if (IS_ERR(ctx->lcd_clk)) {
|
||||
dev_err(dev, "failed to get lcd clock\n");
|
||||
return PTR_ERR(ctx->lcd_clk);
|
||||
ret = PTR_ERR(ctx->lcd_clk);
|
||||
goto err_del_component;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
ctx->regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(ctx->regs))
|
||||
return PTR_ERR(ctx->regs);
|
||||
if (IS_ERR(ctx->regs)) {
|
||||
ret = PTR_ERR(ctx->regs);
|
||||
goto err_del_component;
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "vsync");
|
||||
if (!res) {
|
||||
dev_err(dev, "irq request failed.\n");
|
||||
return -ENXIO;
|
||||
ret = -ENXIO;
|
||||
goto err_del_component;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(dev, res->start, fimd_irq_handler,
|
||||
0, "drm_fimd", ctx);
|
||||
if (ret) {
|
||||
dev_err(dev, "irq request failed.\n");
|
||||
return ret;
|
||||
goto err_del_component;
|
||||
}
|
||||
|
||||
ctx->driver_data = drm_fimd_get_driver_data(pdev);
|
||||
@ -910,30 +991,34 @@ static int fimd_probe(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, &fimd_manager);
|
||||
|
||||
fimd_manager.ctx = ctx;
|
||||
exynos_drm_manager_register(&fimd_manager);
|
||||
|
||||
exynos_dpi_probe(ctx->dev);
|
||||
ctx->display = exynos_dpi_probe(dev);
|
||||
if (IS_ERR(ctx->display))
|
||||
return PTR_ERR(ctx->display);
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
for (win = 0; win < WINDOWS_NR; win++)
|
||||
fimd_clear_win(ctx, win);
|
||||
ret = component_add(&pdev->dev, &fimd_component_ops);
|
||||
if (ret)
|
||||
goto err_disable_pm_runtime;
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
|
||||
err_disable_pm_runtime:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
err_del_component:
|
||||
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fimd_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct exynos_drm_manager *mgr = platform_get_drvdata(pdev);
|
||||
|
||||
exynos_dpi_remove(&pdev->dev);
|
||||
|
||||
exynos_drm_manager_unregister(&fimd_manager);
|
||||
|
||||
fimd_dpms(mgr, DRM_MODE_DPMS_OFF);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
component_del(&pdev->dev, &fimd_component_ops);
|
||||
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -612,22 +612,20 @@ int exynos_drm_gem_dumb_create(struct drm_file *file_priv,
|
||||
args->pitch = args->width * ((args->bpp + 7) / 8);
|
||||
args->size = args->pitch * args->height;
|
||||
|
||||
exynos_gem_obj = exynos_drm_gem_create(dev, EXYNOS_BO_CONTIG |
|
||||
EXYNOS_BO_WC, args->size);
|
||||
/*
|
||||
* If physically contiguous memory allocation fails and if IOMMU is
|
||||
* supported then try to get buffer from non physically contiguous
|
||||
* memory area.
|
||||
*/
|
||||
if (IS_ERR(exynos_gem_obj) && is_drm_iommu_supported(dev)) {
|
||||
dev_warn(dev->dev, "contiguous FB allocation failed, falling back to non-contiguous\n");
|
||||
if (is_drm_iommu_supported(dev)) {
|
||||
exynos_gem_obj = exynos_drm_gem_create(dev,
|
||||
EXYNOS_BO_NONCONTIG | EXYNOS_BO_WC,
|
||||
args->size);
|
||||
EXYNOS_BO_NONCONTIG | EXYNOS_BO_WC,
|
||||
args->size);
|
||||
} else {
|
||||
exynos_gem_obj = exynos_drm_gem_create(dev,
|
||||
EXYNOS_BO_CONTIG | EXYNOS_BO_WC,
|
||||
args->size);
|
||||
}
|
||||
|
||||
if (IS_ERR(exynos_gem_obj))
|
||||
if (IS_ERR(exynos_gem_obj)) {
|
||||
dev_warn(dev->dev, "FB allocation failed.\n");
|
||||
return PTR_ERR(exynos_gem_obj);
|
||||
}
|
||||
|
||||
ret = exynos_drm_gem_handle_create(&exynos_gem_obj->base, file_priv,
|
||||
&args->handle);
|
||||
|
@ -1335,11 +1335,7 @@ static irqreturn_t gsc_irq_handler(int irq, void *dev_id)
|
||||
|
||||
static int gsc_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
|
||||
{
|
||||
struct drm_exynos_ipp_prop_list *prop_list;
|
||||
|
||||
prop_list = devm_kzalloc(ippdrv->dev, sizeof(*prop_list), GFP_KERNEL);
|
||||
if (!prop_list)
|
||||
return -ENOMEM;
|
||||
struct drm_exynos_ipp_prop_list *prop_list = &ippdrv->prop_list;
|
||||
|
||||
prop_list->version = 1;
|
||||
prop_list->writeback = 1;
|
||||
@ -1363,8 +1359,6 @@ static int gsc_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
|
||||
prop_list->scale_min.hsize = GSC_SCALE_MIN;
|
||||
prop_list->scale_min.vsize = GSC_SCALE_MIN;
|
||||
|
||||
ippdrv->prop_list = prop_list;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1387,7 +1381,7 @@ static int gsc_ippdrv_check_property(struct device *dev,
|
||||
{
|
||||
struct gsc_context *ctx = get_gsc_context(dev);
|
||||
struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
|
||||
struct drm_exynos_ipp_prop_list *pp = ippdrv->prop_list;
|
||||
struct drm_exynos_ipp_prop_list *pp = &ippdrv->prop_list;
|
||||
struct drm_exynos_ipp_config *config;
|
||||
struct drm_exynos_pos *pos;
|
||||
struct drm_exynos_sz *sz;
|
||||
|
@ -167,6 +167,13 @@ static int ipp_create_id(struct idr *id_idr, struct mutex *lock, void *obj,
|
||||
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)
|
||||
{
|
||||
void *obj;
|
||||
@ -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);
|
||||
|
||||
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.
|
||||
* 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)
|
||||
list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) {
|
||||
DRM_DEBUG_KMS("count[%d]ippdrv[0x%x]\n", count++, (int)ippdrv);
|
||||
|
||||
if (!list_empty(&ippdrv->cmd_list)) {
|
||||
list_for_each_entry(c_node, &ippdrv->cmd_list, list)
|
||||
if (c_node->property.prop_id == prop_id)
|
||||
return ippdrv;
|
||||
mutex_lock(&ippdrv->cmd_lock);
|
||||
list_for_each_entry(c_node, &ippdrv->cmd_list, list) {
|
||||
if (c_node->property.prop_id == prop_id) {
|
||||
mutex_unlock(&ippdrv->cmd_lock);
|
||||
return ippdrv;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&ippdrv->cmd_lock);
|
||||
}
|
||||
|
||||
return ERR_PTR(-ENODEV);
|
||||
@ -325,6 +330,7 @@ int exynos_drm_ipp_get_property(struct drm_device *drm_dev, void *data,
|
||||
if (!prop_list->ipp_id) {
|
||||
list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list)
|
||||
count++;
|
||||
|
||||
/*
|
||||
* Supports ippdrv list count for user application.
|
||||
* First step user application getting ippdrv count.
|
||||
@ -346,7 +352,7 @@ int exynos_drm_ipp_get_property(struct drm_device *drm_dev, void *data,
|
||||
return PTR_ERR(ippdrv);
|
||||
}
|
||||
|
||||
prop_list = ippdrv->prop_list;
|
||||
*prop_list = ippdrv->prop_list;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -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.
|
||||
* return property information set in this command node.
|
||||
*/
|
||||
mutex_lock(&ippdrv->cmd_lock);
|
||||
list_for_each_entry(c_node, &ippdrv->cmd_list, list) {
|
||||
if ((c_node->property.prop_id == prop_id) &&
|
||||
(c_node->state == IPP_STATE_STOP)) {
|
||||
mutex_unlock(&ippdrv->cmd_lock);
|
||||
DRM_DEBUG_KMS("found cmd[%d]ippdrv[0x%x]\n",
|
||||
property->cmd, (int)ippdrv);
|
||||
|
||||
@ -396,6 +404,7 @@ static int ipp_find_and_set_property(struct drm_exynos_ipp_property *property)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&ippdrv->cmd_lock);
|
||||
|
||||
DRM_ERROR("failed to search property.\n");
|
||||
|
||||
@ -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();
|
||||
if (IS_ERR(c_node->start_work)) {
|
||||
DRM_ERROR("failed to create start work.\n");
|
||||
goto err_clear;
|
||||
goto err_remove_id;
|
||||
}
|
||||
|
||||
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,
|
||||
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->event_lock);
|
||||
|
||||
@ -526,7 +535,9 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data,
|
||||
|
||||
INIT_LIST_HEAD(&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);
|
||||
mutex_unlock(&ippdrv->cmd_lock);
|
||||
|
||||
/* make dedicated state without m2m */
|
||||
if (!ipp_is_m2m_cmd(property->cmd))
|
||||
@ -538,18 +549,24 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data,
|
||||
kfree(c_node->stop_work);
|
||||
err_free_start:
|
||||
kfree(c_node->start_work);
|
||||
err_remove_id:
|
||||
ipp_remove_id(&ctx->prop_idr, &ctx->prop_lock, property->prop_id);
|
||||
err_clear:
|
||||
kfree(c_node);
|
||||
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 */
|
||||
list_del(&c_node->list);
|
||||
|
||||
ipp_remove_id(&ctx->prop_idr, &ctx->prop_lock,
|
||||
c_node->property.prop_id);
|
||||
|
||||
/* destroy mutex */
|
||||
mutex_destroy(&c_node->cmd_lock);
|
||||
mutex_destroy(&c_node->lock);
|
||||
mutex_destroy(&c_node->mem_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)
|
||||
struct list_head *head;
|
||||
int ret, i, count[EXYNOS_DRM_OPS_MAX] = { 0, };
|
||||
|
||||
mutex_lock(&c_node->mem_lock);
|
||||
|
||||
for_each_ipp_ops(i) {
|
||||
/* source/destination memory list */
|
||||
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 */
|
||||
list_for_each_entry(m_node, head, list) {
|
||||
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)
|
||||
ret = max(count[EXYNOS_DRM_OPS_SRC],
|
||||
count[EXYNOS_DRM_OPS_DST]);
|
||||
|
||||
mutex_unlock(&c_node->mem_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -646,16 +654,13 @@ static int ipp_set_mem_node(struct exynos_drm_ippdrv *ippdrv,
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
mutex_lock(&c_node->mem_lock);
|
||||
|
||||
DRM_DEBUG_KMS("ops_id[%d]\n", m_node->ops_id);
|
||||
|
||||
/* get operations callback */
|
||||
ops = ippdrv->ops[m_node->ops_id];
|
||||
if (!ops) {
|
||||
DRM_ERROR("not support ops.\n");
|
||||
ret = -EFAULT;
|
||||
goto err_unlock;
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* set address and enable irq */
|
||||
@ -664,12 +669,10 @@ static int ipp_set_mem_node(struct exynos_drm_ippdrv *ippdrv,
|
||||
m_node->buf_id, IPP_BUF_ENQUEUE);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to set addr.\n");
|
||||
goto err_unlock;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
err_unlock:
|
||||
mutex_unlock(&c_node->mem_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -684,11 +687,9 @@ static struct drm_exynos_ipp_mem_node
|
||||
void *addr;
|
||||
int i;
|
||||
|
||||
mutex_lock(&c_node->mem_lock);
|
||||
|
||||
m_node = kzalloc(sizeof(*m_node), GFP_KERNEL);
|
||||
if (!m_node)
|
||||
goto err_unlock;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/* clear base address for error handling */
|
||||
memset(&buf_info, 0x0, sizeof(buf_info));
|
||||
@ -722,15 +723,14 @@ static struct drm_exynos_ipp_mem_node
|
||||
|
||||
m_node->filp = file;
|
||||
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]);
|
||||
|
||||
mutex_unlock(&c_node->mem_lock);
|
||||
|
||||
return m_node;
|
||||
|
||||
err_clear:
|
||||
kfree(m_node);
|
||||
err_unlock:
|
||||
mutex_unlock(&c_node->mem_lock);
|
||||
return ERR_PTR(-EFAULT);
|
||||
}
|
||||
|
||||
@ -747,13 +747,6 @@ static int ipp_put_mem_node(struct drm_device *drm_dev,
|
||||
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);
|
||||
|
||||
/* put gem buffer */
|
||||
@ -768,8 +761,6 @@ static int ipp_put_mem_node(struct drm_device *drm_dev,
|
||||
list_del(&m_node->list);
|
||||
kfree(m_node);
|
||||
|
||||
mutex_unlock(&c_node->mem_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -805,7 +796,9 @@ static int ipp_get_event(struct drm_device *drm_dev,
|
||||
e->base.event = &e->event.base;
|
||||
e->base.file_priv = file;
|
||||
e->base.destroy = ipp_free_event;
|
||||
mutex_lock(&c_node->event_lock);
|
||||
list_add_tail(&e->base.link, &c_node->event_list);
|
||||
mutex_unlock(&c_node->event_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -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;
|
||||
int count = 0;
|
||||
|
||||
if (list_empty(&c_node->event_list)) {
|
||||
DRM_DEBUG_KMS("event_list is empty.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_lock(&c_node->event_lock);
|
||||
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);
|
||||
|
||||
@ -841,9 +830,13 @@ static void ipp_put_event(struct drm_exynos_ipp_cmd_node *c_node,
|
||||
/* delete list */
|
||||
list_del(&e->base.link);
|
||||
kfree(e);
|
||||
return;
|
||||
goto out_unlock;
|
||||
}
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&c_node->event_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
static void ipp_handle_cmd_work(struct device *dev,
|
||||
@ -887,7 +880,9 @@ static int ipp_queue_buf_with_run(struct device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
mutex_lock(&c_node->mem_lock);
|
||||
if (!ipp_check_mem_list(c_node)) {
|
||||
mutex_unlock(&c_node->mem_lock);
|
||||
DRM_DEBUG_KMS("empty memory.\n");
|
||||
return 0;
|
||||
}
|
||||
@ -904,10 +899,12 @@ static int ipp_queue_buf_with_run(struct device *dev,
|
||||
} else {
|
||||
ret = ipp_set_mem_node(ippdrv, c_node, m_node);
|
||||
if (ret) {
|
||||
mutex_unlock(&c_node->mem_lock);
|
||||
DRM_ERROR("failed to set m node.\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&c_node->mem_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -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;
|
||||
|
||||
if (!list_empty(&c_node->mem_list[qbuf->ops_id])) {
|
||||
/* delete list */
|
||||
list_for_each_entry_safe(m_node, tm_node,
|
||||
&c_node->mem_list[qbuf->ops_id], list) {
|
||||
if (m_node->buf_id == qbuf->buf_id &&
|
||||
m_node->ops_id == qbuf->ops_id)
|
||||
ipp_put_mem_node(drm_dev, c_node, m_node);
|
||||
}
|
||||
/* delete list */
|
||||
mutex_lock(&c_node->mem_lock);
|
||||
list_for_each_entry_safe(m_node, tm_node,
|
||||
&c_node->mem_list[qbuf->ops_id], list) {
|
||||
if (m_node->buf_id == qbuf->buf_id &&
|
||||
m_node->ops_id == qbuf->ops_id)
|
||||
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,
|
||||
@ -998,7 +995,7 @@ int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data,
|
||||
}
|
||||
break;
|
||||
case IPP_BUF_DEQUEUE:
|
||||
mutex_lock(&c_node->cmd_lock);
|
||||
mutex_lock(&c_node->lock);
|
||||
|
||||
/* put event for destination buffer */
|
||||
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,
|
||||
|
||||
ipp_clean_queue_buf(drm_dev, c_node, qbuf);
|
||||
|
||||
mutex_unlock(&c_node->cmd_lock);
|
||||
mutex_unlock(&c_node->lock);
|
||||
break;
|
||||
default:
|
||||
DRM_ERROR("invalid buffer control.\n");
|
||||
@ -1109,12 +1106,12 @@ int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, void *data,
|
||||
case IPP_CTRL_PLAY:
|
||||
if (pm_runtime_suspended(ippdrv->dev))
|
||||
pm_runtime_get_sync(ippdrv->dev);
|
||||
|
||||
c_node->state = IPP_STATE_START;
|
||||
|
||||
cmd_work = c_node->start_work;
|
||||
cmd_work->ctrl = cmd_ctrl->ctrl;
|
||||
ipp_handle_cmd_work(dev, ippdrv, cmd_work, c_node);
|
||||
c_node->state = IPP_STATE_START;
|
||||
break;
|
||||
case IPP_CTRL_STOP:
|
||||
cmd_work = c_node->stop_work;
|
||||
@ -1129,10 +1126,12 @@ int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, void *data,
|
||||
|
||||
c_node->state = IPP_STATE_STOP;
|
||||
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))
|
||||
pm_runtime_put_sync(ippdrv->dev);
|
||||
mutex_unlock(&ippdrv->cmd_lock);
|
||||
break;
|
||||
case IPP_CTRL_PAUSE:
|
||||
cmd_work = c_node->stop_work;
|
||||
@ -1260,9 +1259,11 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv,
|
||||
/* store command info in ippdrv */
|
||||
ippdrv->c_node = c_node;
|
||||
|
||||
mutex_lock(&c_node->mem_lock);
|
||||
if (!ipp_check_mem_list(c_node)) {
|
||||
DRM_DEBUG_KMS("empty memory.\n");
|
||||
return -ENOMEM;
|
||||
ret = -ENOMEM;
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
/* set current property in ippdrv */
|
||||
@ -1270,7 +1271,7 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv,
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to set property.\n");
|
||||
ippdrv->c_node = NULL;
|
||||
return ret;
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
/* check command */
|
||||
@ -1285,7 +1286,7 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv,
|
||||
if (!m_node) {
|
||||
DRM_ERROR("failed to get node.\n");
|
||||
ret = -EFAULT;
|
||||
return ret;
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
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,
|
||||
ret = ipp_set_mem_node(ippdrv, c_node, m_node);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to set m node.\n");
|
||||
return ret;
|
||||
goto err_unlock;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -1305,7 +1306,7 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv,
|
||||
ret = ipp_set_mem_node(ippdrv, c_node, m_node);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to set m node.\n");
|
||||
return ret;
|
||||
goto err_unlock;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -1317,14 +1318,16 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv,
|
||||
ret = ipp_set_mem_node(ippdrv, c_node, m_node);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to set m node.\n");
|
||||
return ret;
|
||||
goto err_unlock;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
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);
|
||||
|
||||
@ -1333,11 +1336,17 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv,
|
||||
ret = ippdrv->start(ippdrv->dev, property->cmd);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to start ops.\n");
|
||||
ippdrv->c_node = NULL;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
@ -1354,6 +1363,8 @@ static int ipp_stop_property(struct drm_device *drm_dev,
|
||||
/* put event */
|
||||
ipp_put_event(c_node, NULL);
|
||||
|
||||
mutex_lock(&c_node->mem_lock);
|
||||
|
||||
/* check command */
|
||||
switch (property->cmd) {
|
||||
case IPP_CMD_M2M:
|
||||
@ -1361,11 +1372,6 @@ static int ipp_stop_property(struct drm_device *drm_dev,
|
||||
/* source/destination memory list */
|
||||
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,
|
||||
head, list) {
|
||||
ret = ipp_put_mem_node(drm_dev, c_node,
|
||||
@ -1381,11 +1387,6 @@ static int ipp_stop_property(struct drm_device *drm_dev,
|
||||
/* destination memory list */
|
||||
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) {
|
||||
ret = ipp_put_mem_node(drm_dev, c_node, m_node);
|
||||
if (ret) {
|
||||
@ -1398,11 +1399,6 @@ static int ipp_stop_property(struct drm_device *drm_dev,
|
||||
/* source memory list */
|
||||
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) {
|
||||
ret = ipp_put_mem_node(drm_dev, c_node, m_node);
|
||||
if (ret) {
|
||||
@ -1418,6 +1414,8 @@ static int ipp_stop_property(struct drm_device *drm_dev,
|
||||
}
|
||||
|
||||
err_clear:
|
||||
mutex_unlock(&c_node->mem_lock);
|
||||
|
||||
/* stop operations */
|
||||
if (ippdrv->stop)
|
||||
ippdrv->stop(ippdrv->dev, property->cmd);
|
||||
@ -1446,7 +1444,7 @@ void ipp_sched_cmd(struct work_struct *work)
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_lock(&c_node->cmd_lock);
|
||||
mutex_lock(&c_node->lock);
|
||||
|
||||
property = &c_node->property;
|
||||
|
||||
@ -1494,7 +1492,7 @@ void ipp_sched_cmd(struct work_struct *work)
|
||||
DRM_DEBUG_KMS("ctrl[%d] done.\n", cmd_work->ctrl);
|
||||
|
||||
err_unlock:
|
||||
mutex_unlock(&c_node->cmd_lock);
|
||||
mutex_unlock(&c_node->lock);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
mutex_lock(&c_node->event_lock);
|
||||
if (list_empty(&c_node->event_list)) {
|
||||
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)) {
|
||||
DRM_DEBUG_KMS("empty memory.\n");
|
||||
return 0;
|
||||
ret = 0;
|
||||
goto err_mem_unlock;
|
||||
}
|
||||
|
||||
/* check command */
|
||||
@ -1545,7 +1547,8 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
|
||||
struct drm_exynos_ipp_mem_node, list);
|
||||
if (!m_node) {
|
||||
DRM_ERROR("empty memory node.\n");
|
||||
return -ENOMEM;
|
||||
ret = -ENOMEM;
|
||||
goto err_mem_unlock;
|
||||
}
|
||||
|
||||
tbuf_id[i] = m_node->buf_id;
|
||||
@ -1567,7 +1570,8 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
|
||||
m_node = ipp_find_mem_node(c_node, &qbuf);
|
||||
if (!m_node) {
|
||||
DRM_ERROR("empty memory node.\n");
|
||||
return -ENOMEM;
|
||||
ret = -ENOMEM;
|
||||
goto err_mem_unlock;
|
||||
}
|
||||
|
||||
tbuf_id[EXYNOS_DRM_OPS_DST] = m_node->buf_id;
|
||||
@ -1584,7 +1588,8 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
|
||||
struct drm_exynos_ipp_mem_node, list);
|
||||
if (!m_node) {
|
||||
DRM_ERROR("empty memory node.\n");
|
||||
return -ENOMEM;
|
||||
ret = -ENOMEM;
|
||||
goto err_mem_unlock;
|
||||
}
|
||||
|
||||
tbuf_id[EXYNOS_DRM_OPS_SRC] = m_node->buf_id;
|
||||
@ -1595,8 +1600,10 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
|
||||
break;
|
||||
default:
|
||||
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])
|
||||
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,
|
||||
e = list_first_entry(&c_node->event_list,
|
||||
struct drm_exynos_ipp_send_event, base.link);
|
||||
|
||||
if (!e) {
|
||||
DRM_ERROR("empty event.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
do_gettimeofday(&now);
|
||||
DRM_DEBUG_KMS("tv_sec[%ld]tv_usec[%ld]\n", now.tv_sec, now.tv_usec);
|
||||
e->event.tv_sec = now.tv_sec;
|
||||
@ -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);
|
||||
wake_up_interruptible(&e->base.file_priv->event_wait);
|
||||
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",
|
||||
property->cmd, property->prop_id, tbuf_id[EXYNOS_DRM_OPS_DST]);
|
||||
|
||||
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)
|
||||
@ -1676,8 +1685,6 @@ void ipp_sched_event(struct work_struct *work)
|
||||
goto err_completion;
|
||||
}
|
||||
|
||||
mutex_lock(&c_node->event_lock);
|
||||
|
||||
ret = ipp_send_event(ippdrv, c_node, event_work->buf_id);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to send event.\n");
|
||||
@ -1687,8 +1694,6 @@ void ipp_sched_event(struct work_struct *work)
|
||||
err_completion:
|
||||
if (ipp_is_m2m_cmd(c_node->property.cmd))
|
||||
complete(&c_node->start_complete);
|
||||
|
||||
mutex_unlock(&c_node->event_lock);
|
||||
}
|
||||
|
||||
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 */
|
||||
list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) {
|
||||
u32 ipp_id;
|
||||
|
||||
ippdrv->drm_dev = drm_dev;
|
||||
|
||||
ret = ipp_create_id(&ctx->ipp_idr, &ctx->ipp_lock, ippdrv,
|
||||
&ippdrv->ipp_id);
|
||||
if (ret) {
|
||||
&ipp_id);
|
||||
if (ret || ipp_id == 0) {
|
||||
DRM_ERROR("failed to create id.\n");
|
||||
goto err_idr;
|
||||
goto err;
|
||||
}
|
||||
|
||||
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) {
|
||||
DRM_ERROR("failed to get ipp_id[%d]\n",
|
||||
ippdrv->ipp_id);
|
||||
goto err_idr;
|
||||
}
|
||||
ippdrv->prop_list.ipp_id = ipp_id;
|
||||
|
||||
/* store parent device for node */
|
||||
ippdrv->parent_dev = 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->sched_event = ipp_sched_event;
|
||||
INIT_LIST_HEAD(&ippdrv->cmd_list);
|
||||
mutex_init(&ippdrv->cmd_lock);
|
||||
|
||||
if (is_drm_iommu_supported(drm_dev)) {
|
||||
ret = drm_iommu_attach_device(drm_dev, ippdrv->dev);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to activate iommu\n");
|
||||
goto err_iommu;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_iommu:
|
||||
err:
|
||||
/* 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))
|
||||
drm_iommu_detach_device(drm_dev, ippdrv->dev);
|
||||
|
||||
err_idr:
|
||||
idr_destroy(&ctx->ipp_idr);
|
||||
idr_destroy(&ctx->prop_idr);
|
||||
ipp_remove_id(&ctx->ipp_idr, &ctx->ipp_lock,
|
||||
ippdrv->prop_list.ipp_id);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ipp_subdrv_remove(struct drm_device *drm_dev, struct device *dev)
|
||||
{
|
||||
struct exynos_drm_ippdrv *ippdrv;
|
||||
struct ipp_context *ctx = get_ipp_context(dev);
|
||||
|
||||
/* get ipp driver entry */
|
||||
list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) {
|
||||
if (is_drm_iommu_supported(drm_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;
|
||||
exynos_drm_ippdrv_unregister(ippdrv);
|
||||
}
|
||||
@ -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 exynos_drm_ipp_private *priv = file_priv->ipp_priv;
|
||||
struct exynos_drm_ippdrv *ippdrv = NULL;
|
||||
struct ipp_context *ctx = get_ipp_context(dev);
|
||||
struct drm_exynos_ipp_cmd_node *c_node, *tc_node;
|
||||
int count = 0;
|
||||
|
||||
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) {
|
||||
if (list_empty(&ippdrv->cmd_list))
|
||||
continue;
|
||||
|
||||
mutex_lock(&ippdrv->cmd_lock);
|
||||
list_for_each_entry_safe(c_node, tc_node,
|
||||
&ippdrv->cmd_list, list) {
|
||||
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,
|
||||
}
|
||||
|
||||
ippdrv->dedicated = false;
|
||||
ipp_clean_cmd_node(c_node);
|
||||
ipp_clean_cmd_node(ctx, c_node);
|
||||
if (list_empty(&ippdrv->cmd_list))
|
||||
pm_runtime_put_sync(ippdrv->dev);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&ippdrv->cmd_lock);
|
||||
}
|
||||
|
||||
err_clear:
|
||||
kfree(priv);
|
||||
return;
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ struct drm_exynos_ipp_cmd_work {
|
||||
* @list: list head to command queue information.
|
||||
* @event_list: list head of event.
|
||||
* @mem_list: list head to source,destination memory queue information.
|
||||
* @cmd_lock: lock for synchronization of access to ioctl.
|
||||
* @lock: lock for synchronization of access to ioctl.
|
||||
* @mem_lock: lock for synchronization of access to memory nodes.
|
||||
* @event_lock: lock for synchronization of access to scheduled event.
|
||||
* @start_complete: completion of start of command.
|
||||
@ -68,7 +68,7 @@ struct drm_exynos_ipp_cmd_node {
|
||||
struct list_head list;
|
||||
struct list_head event_list;
|
||||
struct list_head mem_list[EXYNOS_DRM_OPS_MAX];
|
||||
struct mutex cmd_lock;
|
||||
struct mutex lock;
|
||||
struct mutex mem_lock;
|
||||
struct mutex event_lock;
|
||||
struct completion start_complete;
|
||||
@ -83,7 +83,7 @@ struct drm_exynos_ipp_cmd_node {
|
||||
/*
|
||||
* A structure of buffer information.
|
||||
*
|
||||
* @gem_objs: Y, Cb, Cr each gem object.
|
||||
* @handles: Y, Cb, Cr each gem object handle.
|
||||
* @base: Y, Cb, Cr each planar address.
|
||||
*/
|
||||
struct drm_exynos_ipp_buf_info {
|
||||
@ -142,12 +142,12 @@ struct exynos_drm_ipp_ops {
|
||||
* @parent_dev: parent device information.
|
||||
* @dev: platform device.
|
||||
* @drm_dev: drm device.
|
||||
* @ipp_id: id of ipp driver.
|
||||
* @dedicated: dedicated ipp device.
|
||||
* @ops: source, destination operations.
|
||||
* @event_workq: event work queue.
|
||||
* @c_node: current command information.
|
||||
* @cmd_list: list head for command information.
|
||||
* @cmd_lock: lock for synchronization of access to cmd_list.
|
||||
* @prop_list: property informations of current ipp driver.
|
||||
* @check_property: check property about format, size, buffer.
|
||||
* @reset: reset ipp block.
|
||||
@ -160,13 +160,13 @@ struct exynos_drm_ippdrv {
|
||||
struct device *parent_dev;
|
||||
struct device *dev;
|
||||
struct drm_device *drm_dev;
|
||||
u32 ipp_id;
|
||||
bool dedicated;
|
||||
struct exynos_drm_ipp_ops *ops[EXYNOS_DRM_OPS_MAX];
|
||||
struct workqueue_struct *event_workq;
|
||||
struct drm_exynos_ipp_cmd_node *c_node;
|
||||
struct list_head cmd_list;
|
||||
struct drm_exynos_ipp_prop_list *prop_list;
|
||||
struct mutex cmd_lock;
|
||||
struct drm_exynos_ipp_prop_list prop_list;
|
||||
|
||||
int (*check_property)(struct device *dev,
|
||||
struct drm_exynos_ipp_property *property);
|
||||
|
@ -158,8 +158,9 @@ static irqreturn_t rotator_irq_handler(int irq, void *arg)
|
||||
rot->cur_buf_id[EXYNOS_DRM_OPS_DST];
|
||||
queue_work(ippdrv->event_workq,
|
||||
(struct work_struct *)event_work);
|
||||
} else
|
||||
} else {
|
||||
DRM_ERROR("the SFR is set illegally\n");
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
@ -469,11 +470,7 @@ static struct exynos_drm_ipp_ops rot_dst_ops = {
|
||||
|
||||
static int rotator_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
|
||||
{
|
||||
struct drm_exynos_ipp_prop_list *prop_list;
|
||||
|
||||
prop_list = devm_kzalloc(ippdrv->dev, sizeof(*prop_list), GFP_KERNEL);
|
||||
if (!prop_list)
|
||||
return -ENOMEM;
|
||||
struct drm_exynos_ipp_prop_list *prop_list = &ippdrv->prop_list;
|
||||
|
||||
prop_list->version = 1;
|
||||
prop_list->flip = (1 << EXYNOS_DRM_FLIP_VERTICAL) |
|
||||
@ -486,8 +483,6 @@ static int rotator_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
|
||||
prop_list->crop = 0;
|
||||
prop_list->scale = 0;
|
||||
|
||||
ippdrv->prop_list = prop_list;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -51,6 +51,7 @@ struct vidi_context {
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_encoder *encoder;
|
||||
struct drm_connector connector;
|
||||
struct exynos_drm_subdrv subdrv;
|
||||
struct vidi_win_data win_data[WINDOWS_NR];
|
||||
struct edid *raw_edid;
|
||||
unsigned int clkdiv;
|
||||
@ -294,14 +295,13 @@ static void vidi_dpms(struct exynos_drm_manager *mgr, int mode)
|
||||
}
|
||||
|
||||
static int vidi_mgr_initialize(struct exynos_drm_manager *mgr,
|
||||
struct drm_device *drm_dev, int pipe)
|
||||
struct drm_device *drm_dev)
|
||||
{
|
||||
struct vidi_context *ctx = mgr->ctx;
|
||||
struct exynos_drm_private *priv = drm_dev->dev_private;
|
||||
|
||||
DRM_ERROR("vidi initialize ct=%p dev=%p pipe=%d\n", ctx, drm_dev, pipe);
|
||||
|
||||
ctx->drm_dev = drm_dev;
|
||||
ctx->pipe = pipe;
|
||||
mgr->drm_dev = ctx->drm_dev = drm_dev;
|
||||
mgr->pipe = ctx->pipe = priv->pipe++;
|
||||
|
||||
/*
|
||||
* enable drm irq mode.
|
||||
@ -324,7 +324,6 @@ static int vidi_mgr_initialize(struct exynos_drm_manager *mgr,
|
||||
}
|
||||
|
||||
static struct exynos_drm_manager_ops vidi_manager_ops = {
|
||||
.initialize = vidi_mgr_initialize,
|
||||
.dpms = vidi_dpms,
|
||||
.commit = vidi_commit,
|
||||
.enable_vblank = vidi_enable_vblank,
|
||||
@ -579,13 +578,38 @@ static struct exynos_drm_display vidi_display = {
|
||||
.ops = &vidi_display_ops,
|
||||
};
|
||||
|
||||
static int vidi_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
|
||||
{
|
||||
struct exynos_drm_manager *mgr = get_vidi_mgr(dev);
|
||||
struct vidi_context *ctx = mgr->ctx;
|
||||
struct drm_crtc *crtc = ctx->crtc;
|
||||
int ret;
|
||||
|
||||
vidi_mgr_initialize(mgr, drm_dev);
|
||||
|
||||
ret = exynos_drm_crtc_create(&vidi_manager);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to create crtc.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = exynos_drm_create_enc_conn(drm_dev, &vidi_display);
|
||||
if (ret) {
|
||||
crtc->funcs->destroy(crtc);
|
||||
DRM_ERROR("failed to create encoder and connector.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vidi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct exynos_drm_subdrv *subdrv;
|
||||
struct vidi_context *ctx;
|
||||
int ret;
|
||||
|
||||
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
|
||||
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -600,28 +624,43 @@ static int vidi_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, &vidi_manager);
|
||||
|
||||
ret = device_create_file(dev, &dev_attr_connection);
|
||||
if (ret < 0)
|
||||
DRM_INFO("failed to create connection sysfs.\n");
|
||||
subdrv = &ctx->subdrv;
|
||||
subdrv->dev = &pdev->dev;
|
||||
subdrv->probe = vidi_subdrv_probe;
|
||||
|
||||
exynos_drm_manager_register(&vidi_manager);
|
||||
exynos_drm_display_register(&vidi_display);
|
||||
ret = exynos_drm_subdrv_register(subdrv);
|
||||
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;
|
||||
}
|
||||
|
||||
static int vidi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct vidi_context *ctx = platform_get_drvdata(pdev);
|
||||
|
||||
exynos_drm_display_unregister(&vidi_display);
|
||||
exynos_drm_manager_unregister(&vidi_manager);
|
||||
struct exynos_drm_manager *mgr = platform_get_drvdata(pdev);
|
||||
struct vidi_context *ctx = mgr->ctx;
|
||||
struct drm_encoder *encoder = ctx->encoder;
|
||||
struct drm_crtc *crtc = mgr->crtc;
|
||||
|
||||
if (ctx->raw_edid != (struct edid *)fake_edid_info) {
|
||||
kfree(ctx->raw_edid);
|
||||
ctx->raw_edid = NULL;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
crtc->funcs->destroy(crtc);
|
||||
encoder->funcs->destroy(encoder);
|
||||
drm_connector_cleanup(&ctx->connector);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -633,3 +672,31 @@ struct platform_driver vidi_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
int exynos_drm_probe_vidi(void)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
int ret;
|
||||
|
||||
pdev = platform_device_register_simple("exynos-drm-vidi", -1, NULL, 0);
|
||||
if (IS_ERR(pdev))
|
||||
return PTR_ERR(pdev);
|
||||
|
||||
ret = platform_driver_register(&vidi_driver);
|
||||
if (ret) {
|
||||
platform_device_unregister(pdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void exynos_drm_remove_vidi(void)
|
||||
{
|
||||
struct vidi_context *ctx = vidi_manager.ctx;
|
||||
struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
|
||||
struct platform_device *pdev = to_platform_device(subdrv->dev);
|
||||
|
||||
platform_driver_unregister(&vidi_driver);
|
||||
platform_device_unregister(pdev);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,23 +0,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
|
@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 Samsung Electronics Co.Ltd
|
||||
* Authors:
|
||||
* Seung-Woo Kim <sw0312.kim@samsung.com>
|
||||
* Inki Dae <inki.dae@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include "exynos_drm_drv.h"
|
||||
#include "exynos_hdmi.h"
|
||||
|
||||
|
||||
static int hdmiphy_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
hdmi_attach_hdmiphy_client(client);
|
||||
|
||||
dev_info(&client->adapter->dev, "attached s5p_hdmiphy "
|
||||
"into i2c adapter successfully\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdmiphy_remove(struct i2c_client *client)
|
||||
{
|
||||
dev_info(&client->adapter->dev, "detached s5p_hdmiphy "
|
||||
"from i2c adapter successfully\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id hdmiphy_match_types[] = {
|
||||
{
|
||||
.compatible = "samsung,exynos5-hdmiphy",
|
||||
}, {
|
||||
.compatible = "samsung,exynos4210-hdmiphy",
|
||||
}, {
|
||||
.compatible = "samsung,exynos4212-hdmiphy",
|
||||
}, {
|
||||
/* end node */
|
||||
}
|
||||
};
|
||||
|
||||
struct i2c_driver hdmiphy_driver = {
|
||||
.driver = {
|
||||
.name = "exynos-hdmiphy",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = hdmiphy_match_types,
|
||||
},
|
||||
.probe = hdmiphy_probe,
|
||||
.remove = hdmiphy_remove,
|
||||
.command = NULL,
|
||||
};
|
||||
EXPORT_SYMBOL(hdmiphy_driver);
|
@ -31,6 +31,7 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/component.h>
|
||||
|
||||
#include <drm/exynos_drm.h>
|
||||
|
||||
@ -830,13 +831,15 @@ static int vp_resources_init(struct mixer_context *mixer_ctx)
|
||||
}
|
||||
|
||||
static int mixer_initialize(struct exynos_drm_manager *mgr,
|
||||
struct drm_device *drm_dev, int pipe)
|
||||
struct drm_device *drm_dev)
|
||||
{
|
||||
int ret;
|
||||
struct mixer_context *mixer_ctx = mgr->ctx;
|
||||
struct exynos_drm_private *priv;
|
||||
priv = drm_dev->dev_private;
|
||||
|
||||
mixer_ctx->drm_dev = drm_dev;
|
||||
mixer_ctx->pipe = pipe;
|
||||
mgr->drm_dev = mixer_ctx->drm_dev = drm_dev;
|
||||
mgr->pipe = mixer_ctx->pipe = priv->pipe++;
|
||||
|
||||
/* acquire resources: regs, irqs, clocks */
|
||||
ret = mixer_resources_init(mixer_ctx);
|
||||
@ -1142,8 +1145,6 @@ int mixer_check_mode(struct drm_display_mode *mode)
|
||||
}
|
||||
|
||||
static struct exynos_drm_manager_ops mixer_manager_ops = {
|
||||
.initialize = mixer_initialize,
|
||||
.remove = mixer_mgr_remove,
|
||||
.dpms = mixer_dpms,
|
||||
.enable_vblank = mixer_enable_vblank,
|
||||
.disable_vblank = mixer_disable_vblank,
|
||||
@ -1200,11 +1201,13 @@ static struct of_device_id mixer_match_types[] = {
|
||||
}
|
||||
};
|
||||
|
||||
static int mixer_probe(struct platform_device *pdev)
|
||||
static int mixer_bind(struct device *dev, struct device *manager, void *data)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct drm_device *drm_dev = data;
|
||||
struct mixer_context *ctx;
|
||||
struct mixer_drv_data *drv;
|
||||
int ret;
|
||||
|
||||
dev_info(dev, "probe start\n");
|
||||
|
||||
@ -1233,19 +1236,61 @@ static int mixer_probe(struct platform_device *pdev)
|
||||
atomic_set(&ctx->wait_vsync_event, 0);
|
||||
|
||||
mixer_manager.ctx = ctx;
|
||||
ret = mixer_initialize(&mixer_manager, drm_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
platform_set_drvdata(pdev, &mixer_manager);
|
||||
exynos_drm_manager_register(&mixer_manager);
|
||||
ret = exynos_drm_crtc_create(&mixer_manager);
|
||||
if (ret) {
|
||||
mixer_mgr_remove(&mixer_manager);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static 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)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC,
|
||||
mixer_manager.type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = component_add(&pdev->dev, &mixer_component_ops);
|
||||
if (ret)
|
||||
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mixer_remove(struct platform_device *pdev)
|
||||
{
|
||||
dev_info(&pdev->dev, "remove successful\n");
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
component_del(&pdev->dev, &mixer_component_ops);
|
||||
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -578,4 +578,20 @@
|
||||
#define HDMI_TG_VACT_ST4_H HDMI_TG_BASE(0x0074)
|
||||
#define HDMI_TG_3D HDMI_TG_BASE(0x00F0)
|
||||
|
||||
/* HDMI PHY Registers Offsets*/
|
||||
#define HDMIPHY_POWER (0x74 >> 2)
|
||||
#define HDMIPHY_MODE_SET_DONE (0x7c >> 2)
|
||||
|
||||
/* HDMI PHY Values */
|
||||
#define HDMI_PHY_POWER_ON 0x80
|
||||
#define HDMI_PHY_POWER_OFF 0xff
|
||||
|
||||
/* HDMI PHY Values */
|
||||
#define HDMI_PHY_DISABLE_MODE_SET 0x80
|
||||
#define HDMI_PHY_ENABLE_MODE_SET 0x00
|
||||
|
||||
/* PMU Registers for PHY */
|
||||
#define PMU_HDMI_PHY_CONTROL 0x700
|
||||
#define PMU_HDMI_PHY_ENABLE_BIT BIT(0)
|
||||
|
||||
#endif /* SAMSUNG_REGS_HDMI_H */
|
||||
|
@ -310,6 +310,8 @@
|
||||
# define DP_TEST_NAK (1 << 1)
|
||||
# define DP_TEST_EDID_CHECKSUM_WRITE (1 << 2)
|
||||
|
||||
#define DP_TEST_EDID_CHECKSUM 0x261
|
||||
|
||||
#define DP_TEST_SINK 0x270
|
||||
#define DP_TEST_SINK_START (1 << 0)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user