mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 03:15:31 +09:00
Merge commit '424557b2d34d9e876f0f1d36f3771e73ecfbdea8'
* commit '424557b2d34d9e876f0f1d36f3771e73ecfbdea8': ARM: rockchip: rv1106: sleep: fix compile error media: rockchip: vicap: fixes error of quick stream for AOV without rtt mcu media: rockchip: vicap change stream suspend/resume early media: rockchip: vicap support resume mode of oneframe without rtt phy: rockchip: mipi csi2 dphy support set quick stream phy: rockchip: csi2-dphy: fixes NULL of dphy node for dcphy mfd: display-serdes: add split mode function support dt-bindings: opp: Document rockchip opp bindings arm64: dts: rockchip: rk3588: Change opp clock for gpu and npu opp table Change-Id: I2570e20cda5e192e35b5d7476fcf5fc45b4787ec
This commit is contained in:
@@ -8,83 +8,4 @@ property of OPPS when it is parsed by the OPP framework. This is based
|
||||
on operating-points-v2, but the driver can also create the "cpufreq-dt"
|
||||
platform_device to compatibility with operating-points.
|
||||
|
||||
For more information about the expected DT format [See: ../opp/opp.txt].
|
||||
|
||||
Optional properties:
|
||||
In 'operating-points-v2' table:
|
||||
- rockchip,leakage-voltage-sel: The property is an array of 3-tuples items, and
|
||||
each item consists of leakage and voltage selector like
|
||||
<min-leakage max-leakage volt-selector>.
|
||||
min-leakage: minimum leakage in mA, ranges from 1 to 254.
|
||||
max-leakage: maximum leakage in mA, ranges from 1 to 254.
|
||||
voltage-selector: a sequence number which is used to math
|
||||
opp-microvolt-L<number> roperty in OPP node.
|
||||
|
||||
- rockchip,pvtm-voltage-sel: The property is an array of 3-tuples items, and
|
||||
each item consists of pvtm and voltage selector like
|
||||
<min-pvtm max-pvtm volt-selector>.
|
||||
min-pvtm: minimum frequency count in KHz.
|
||||
max-pvtm: maximum frequency count in KHz.
|
||||
voltage-selector: a sequence number which is used to math
|
||||
opp-microvolt-L<number> roperty in OPP node.
|
||||
- rockchip,pvtm-freq: Clock frequency in KHz, which is used to set the cpu clock
|
||||
frequency before get frequency count of pvtm.
|
||||
- rockchip,pvtm-volt: Voltage in uV, which is used to set the cpu voltage before
|
||||
get frequency count of pvtm.
|
||||
- rockchip,pvtm-ch: An array of two integers containing pvtm channel and clock
|
||||
oscillation ring.
|
||||
- rockchip,pvtm-sample-time: The number of milliseconds to wait for pvtm to
|
||||
finish counting.
|
||||
- rockchip,pvtm-number: An integer indicating the number of sampling points.
|
||||
- rockchip,pvtm-error: An integer indicating the error between the sample
|
||||
results.
|
||||
- rockchip,pvtm-ref-temp: The SoC internal temperature in degree centigrade, the
|
||||
min-pvtm and max-pvtm in 'leakage-voltage-sel' are
|
||||
measured at reference temperature.
|
||||
- rockchip,pvtm-temp-prop: An array of two integers containing proportional
|
||||
constants which is used to convert the value at current
|
||||
temperature to reference temperature. The first one is
|
||||
used when current temperature is below reference
|
||||
temperature. Conversely, The second one is used when
|
||||
current temperature is above reference temperature.
|
||||
- rockchip,pvtm-thermal-zone: A thermal zone node containing thermal sensor,
|
||||
it's used to get the current temperature.
|
||||
- rockchip,thermal-zone: A thermal zone node containing thermal sensor,
|
||||
it's used to get the current temperature.
|
||||
|
||||
- nvmem-cells: A phandle to cpu_leakage data provided by a nvmem device.
|
||||
- nvmem-cell-names: Should be "cpu_leakage"
|
||||
|
||||
- rockchip,threshold-freq: Clock frequency in KHz, it's used to reduce power
|
||||
for SoCs with two clusters.
|
||||
- rockchip,freq-limit: Only one cluster can contain the property, and the
|
||||
cluster's maximum frequency will be limited to its
|
||||
threshold frequency, if the other cluster's frequency
|
||||
is geater than or equal to its threshold frequency.
|
||||
|
||||
Examples:
|
||||
|
||||
cpus {
|
||||
cpu@0 {
|
||||
operating-points-v2 = <&cluster0_opp>;
|
||||
};
|
||||
}
|
||||
|
||||
cluster0_opp: opp_table0 {
|
||||
compatible = "operating-points-v2";
|
||||
opp-shared;
|
||||
rockchip,leakage-voltage-sel = <
|
||||
1 24 0
|
||||
25 254 1
|
||||
>;
|
||||
nvmem-cells = <&cpu_leakage>;
|
||||
nvmem-cell-names = "cpu_leakage";
|
||||
|
||||
opp@216000000 {
|
||||
opp-hz = /bits/ 64 <216000000>;
|
||||
opp-microvolt = <950000 950000 1350000>;
|
||||
opp-microvolt-L0 = <1050000 1050000 1350000>;
|
||||
opp-microvolt-L1 = <950000 950000 1350000>;
|
||||
opp-suspend;
|
||||
}
|
||||
};
|
||||
For more information about the expected DT format [See: ../opp/rockchip-opp.txt].
|
||||
|
||||
173
Documentation/devicetree/bindings/opp/rockchip-opp.txt
Normal file
173
Documentation/devicetree/bindings/opp/rockchip-opp.txt
Normal file
@@ -0,0 +1,173 @@
|
||||
Rockchip OPP bindings to describe OPP nodes
|
||||
|
||||
The bindings are based on top of the operating-points-v2 bindings
|
||||
described in Documentation/devicetree/bindings/opp/opp.txt
|
||||
Additional properties are described below.
|
||||
|
||||
Optional properties:
|
||||
In 'operating-points-v2' table:
|
||||
- rockchip,supported-hw: The property allows to set supported hardware version.
|
||||
|
||||
- rockchip,opp-shared-dsu: Indicates the dsu also uses the opp table.
|
||||
|
||||
- rockchip,leakage-voltage-sel: The property is an array of 3-tuples items, and
|
||||
each item consists of leakage and voltage selector like
|
||||
<min-leakage max-leakage volt-selector>.
|
||||
min-leakage: minimum leakage in mA, ranges from 1 to 254.
|
||||
max-leakage: maximum leakage in mA, ranges from 1 to 254.
|
||||
voltage-selector: a sequence number which is used to math
|
||||
opp-microvolt-L<number> property in OPP node.
|
||||
- rockchip,leakage-scaling-sel: Similar to 'rockchip,leakage-voltage-sel', this
|
||||
allows to change max frequency according to leakage.
|
||||
|
||||
- rockchip,pvtm-hw: Similar to 'opp-supported-hw', but only one hierarchical
|
||||
level, if hardware version is set in the property, the
|
||||
'rockchip,pvtm-voltage-sel-hw' will be used, otherwise
|
||||
'rockchip,pvtm-voltage-sel' will be used.
|
||||
- rockchip,pvtm-voltage-sel-hw: Similar to 'rockchip,pvtm-voltage-sel', this
|
||||
depends on 'rockchip,pvtm-hw' property.
|
||||
- rockchip,pvtm-voltage-sel: The property is an array of 3-tuples items, and
|
||||
each item consists of pvtm and voltage selector like
|
||||
<min-pvtm max-pvtm volt-selector>.
|
||||
min-pvtm: minimum frequency count in KHz.
|
||||
max-pvtm: maximum frequency count in KHz.
|
||||
voltage-selector: a sequence number which is used to math
|
||||
opp-microvolt-L<number> property in OPP node.
|
||||
- rockchip,pvtm-scaling-sel: Similar to 'rockchip,pvtm-voltage-sel', this allows
|
||||
to change maximum frequency according to pvtm.
|
||||
|
||||
- rockchip,bin-voltage-sel: The property is an array of 2-tuples items, and
|
||||
each item consists of bin and voltage selector like
|
||||
<min-bin volt-selector>.
|
||||
min-bin: chip version.
|
||||
voltage-selector: a sequence number which is used to math
|
||||
opp-microvolt-L<number> property in OPP node.
|
||||
- rockchip,bin-scaling-sel: Similar to 'rockchip,bin-voltage-sel', this allows
|
||||
to change maximum frequency according to chip version.
|
||||
|
||||
- rockchip,grf: The phandle of the syscon node for GRF register.
|
||||
- rockchip,dsu-grf: the phandle of the syscon node for DSU GRF register.
|
||||
- rockchip,opp-clocks: List of clocks for accessing pvtpll and read margin.
|
||||
- volt-mem-read-margin: The property is an array of 2-tuples items, and
|
||||
each item consists voltage an memory read margin like
|
||||
<voltage_uV rm>, this allows to change read margin
|
||||
according to voltage.
|
||||
- low-volt-mem-read-margin: The memory read margin value of low voltage.
|
||||
- intermediate-threshold-freq: Clock frequency in KHz, if current frequency or
|
||||
target frequency less than the threshold frequency,
|
||||
there may be no need to set intermediate rate.
|
||||
|
||||
- rockchip,pvtpll-avg-offset: The offset of average value register.
|
||||
- rockchip,pvtpll-min-rate: Clock frequency in KHz, if opp frequency is higher
|
||||
than the minimum frequency, the opp voltage will be
|
||||
changed.
|
||||
- rockchip,pvtpll-volt-step: Voltage step in uV.
|
||||
|
||||
- rockchip,pvtm-pvtpll: Indicates pvtm value is from pvtpll.
|
||||
- rockchip,pvtm-offset: The offset of pvtm value register.
|
||||
- rockchip,pvtm-freq: Clock frequency in KHz, which is used to set the cpu clock
|
||||
frequency before get frequency count of pvtm.
|
||||
- rockchip,pvtm-volt: Voltage in uV, which is used to set the cpu voltage before
|
||||
get frequency count of pvtm.
|
||||
- rockchip,pvtm-ch: An array of two integers containing pvtm channel and clock
|
||||
oscillation ring.
|
||||
- rockchip,pvtm-sample-time: The number of milliseconds to wait for pvtm to
|
||||
finish counting.
|
||||
- rockchip,pvtm-number: An integer indicating the number of sampling points.
|
||||
- rockchip,pvtm-error: An integer indicating the error between the sample
|
||||
results.
|
||||
- rockchip,pvtm-ref-temp: The SoC internal temperature in degree centigrade, the
|
||||
min-pvtm and max-pvtm in 'leakage-voltage-sel' are
|
||||
measured at reference temperature.
|
||||
- rockchip,pvtm-temp-prop: An array of two integers containing proportional
|
||||
constants which is used to convert the value at current
|
||||
temperature to reference temperature. The first one is
|
||||
used when current temperature is below reference
|
||||
temperature. Conversely, The second one is used when
|
||||
current temperature is above reference temperature.
|
||||
- rockchip,pvtm-thermal-zone: A thermal zone node containing thermal sensor,
|
||||
it's used to get the current temperature.
|
||||
- rockchip,thermal-zone: A thermal zone node containing thermal sensor,
|
||||
it's used to get the current temperature.
|
||||
|
||||
- nvmem-cells: A phandle to soc info provided by a nvmem device.
|
||||
- nvmem-cell-names: Should be "leakage", "pvtm", "mbist-vmin", "opp-info",
|
||||
"specification_serial_number", "performance", or
|
||||
"remark_spec_serial_number".
|
||||
|
||||
- rockchip,threshold-freq: Clock frequency in KHz, it's used to reduce power
|
||||
for SoCs with two clusters.
|
||||
- rockchip,freq-limit: Only one cluster can contain the property, and the
|
||||
cluster's maximum frequency will be limited to its
|
||||
threshold frequency, if the other cluster's frequency
|
||||
is geater than or equal to its threshold frequency.
|
||||
|
||||
- rockchip,init-freq: The initial clock frequency in KHz.
|
||||
|
||||
- rockchip,max-volt: The maximum voltage of opp.
|
||||
- rockchip,evb-irdrop: The evb irdrop in uV.
|
||||
- rockchip,board-irdrop: The property is an array of 3-tuples items, and
|
||||
each item consists of frequency and voltage like
|
||||
<min-freq max-freq volt>. This allows to change voltage
|
||||
for different frequencice.
|
||||
|
||||
- rockchip,temp-hysteresis: Temperature hysteresis in millicelsius.
|
||||
- rockchip,low-temp: Threshold temperature in millicelsius.
|
||||
- rockchip,low-temp-adjust-volt: The property is an array of 3-tuples items, and
|
||||
each item consists of frequency and voltage like
|
||||
<min-freq max-freq volt>. This allows to change voltage
|
||||
according to different frequencice at low temperature.
|
||||
- rockchip,low-temp-min-volt: The minimum voltage of regulator at low temperature.
|
||||
- rockchip,high-temp: Threshold temperature in millicelsius.
|
||||
- rockchip,high-temp-max-volt: The maximum voltage of regulator at high temperature.
|
||||
- rockchip,high-temp-max-freq: The maximum frequency of clock at high temperature.
|
||||
- rockchip,temp-freq-table: The property is an array of 2-tuples items, and each
|
||||
item consists of temperature and frequency like
|
||||
<min-temp max-freq>. This allows to change the max
|
||||
frequency according to temperature.
|
||||
- rockchip,high-temp-limit-table: Similar to 'rockchip,temp-freq-table', but
|
||||
it's discarded.
|
||||
|
||||
- rockchip,early-min-microvolt: Voltage in uV, this allows to limit the minimum
|
||||
voltage of regulator when start up.
|
||||
|
||||
- rockchip,video-4k-freq: Clock frequency in KHz, change to the frequency when
|
||||
enter SYS_STATUS_VIDEO_4K status.
|
||||
- rockchip,reboot-freq: Clock frequency in KHz, change to the frequency when
|
||||
reboot.
|
||||
|
||||
- rockchip,avs-enable: Similar to 'rockchip,avs', but it's discarded.
|
||||
- rockchip,avs: Indicates avs is enabled.
|
||||
- rockchip,avs-scale: The minimum scale of avs.
|
||||
- clocks: PLL clock, it's used to get avs frequency and avs scale.
|
||||
|
||||
- mbist-vmin: The minimum voltage of memory, this allows to adjust opp voltage
|
||||
according to mbist in otp.
|
||||
|
||||
Examples:
|
||||
|
||||
cpus {
|
||||
cpu@0 {
|
||||
operating-points-v2 = <&cluster0_opp>;
|
||||
};
|
||||
}
|
||||
|
||||
cluster0_opp: opp_table0 {
|
||||
compatible = "operating-points-v2";
|
||||
opp-shared;
|
||||
|
||||
rockchip,leakage-voltage-sel = <
|
||||
1 24 0
|
||||
25 254 1
|
||||
>;
|
||||
nvmem-cells = <&cpu_leakage>;
|
||||
nvmem-cell-names = "cpu_leakage";
|
||||
|
||||
opp@216000000 {
|
||||
opp-hz = /bits/ 64 <216000000>;
|
||||
opp-microvolt = <950000 950000 1350000>;
|
||||
opp-microvolt-L0 = <1050000 1050000 1350000>;
|
||||
opp-microvolt-L1 = <950000 950000 1350000>;
|
||||
opp-suspend;
|
||||
}
|
||||
};
|
||||
@@ -389,7 +389,7 @@ static void rv1106_dbg_pmu_wkup_src(void)
|
||||
if (pmu_int_st & BIT(RV1106_PMU_WAKEUP_USBDEV_EN))
|
||||
rkpm_printstr("USBDEV detect wakeup\n");
|
||||
|
||||
if (pmu_int_st & BIT(RV1106_PMU_WAKEUP_TIMEROUT_EN))
|
||||
if (pmu_int_st & BIT(RV1106_PMU_WAKEUP_TIMEOUT_EN))
|
||||
rkpm_printstr("TIMEOUT interrupt wakeup\n");
|
||||
|
||||
rkpm_printch('\n');
|
||||
|
||||
@@ -2414,8 +2414,7 @@
|
||||
rockchip,pvtm-temp-prop = <(-135) (-135)>;
|
||||
rockchip,pvtm-thermal-zone = "gpu-thermal";
|
||||
|
||||
clocks = <&cru CLK_GPU>;
|
||||
clock-names = "clk";
|
||||
rockchip,opp-clocks = <&cru CLK_GPU>;
|
||||
rockchip,grf = <&gpu_grf>;
|
||||
volt-mem-read-margin = <
|
||||
855000 1
|
||||
@@ -3507,8 +3506,7 @@
|
||||
rockchip,pvtm-temp-prop = <(-113) (-113)>;
|
||||
rockchip,pvtm-thermal-zone = "npu-thermal";
|
||||
|
||||
clocks = <&cru PCLK_NPU_GRF>, <&cru HCLK_NPU_ROOT>;
|
||||
clock-names = "pclk", "hclk";
|
||||
rockchip,opp-clocks = <&cru PCLK_NPU_GRF>, <&cru HCLK_NPU_ROOT>;
|
||||
rockchip,grf = <&npu_grf>;
|
||||
volt-mem-read-margin = <
|
||||
855000 1
|
||||
|
||||
@@ -676,7 +676,7 @@ static int rkcif_output_fmt_check(struct rkcif_stream *stream,
|
||||
break;
|
||||
}
|
||||
if (ret)
|
||||
v4l2_err(&stream->cifdev->v4l2_dev,
|
||||
v4l2_dbg(4, rkcif_debug, &stream->cifdev->v4l2_dev,
|
||||
"input mbus_code 0x%x, can't transform to %c%c%c%c\n",
|
||||
input_fmt->mbus_code,
|
||||
output_fmt->fourcc & 0xff,
|
||||
@@ -1933,6 +1933,26 @@ static void rkcif_assign_new_buffer_init_toisp(struct rkcif_stream *stream,
|
||||
stream->buf_owner = RKCIF_DMAEN_BY_ISP;
|
||||
}
|
||||
|
||||
static void rkcif_dphy_quick_stream(struct rkcif_device *dev, int on)
|
||||
{
|
||||
struct rkcif_pipeline *p = NULL;
|
||||
int j = 0;
|
||||
|
||||
if (dev->active_sensor->mbus.type == V4L2_MBUS_CSI2_DPHY ||
|
||||
dev->active_sensor->mbus.type == V4L2_MBUS_CSI2_CPHY ||
|
||||
dev->active_sensor->mbus.type == V4L2_MBUS_CCP2) {
|
||||
p = &dev->pipe;
|
||||
for (j = 0; j < p->num_subdevs; j++) {
|
||||
if (p->subdevs[j] != dev->terminal_sensor.sd &&
|
||||
p->subdevs[j] != dev->active_sensor->sd) {
|
||||
v4l2_subdev_call(p->subdevs[j], core, ioctl,
|
||||
RKMODULE_SET_QUICK_STREAM, &on);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int rkcif_assign_new_buffer_update_toisp(struct rkcif_stream *stream,
|
||||
int channel_id)
|
||||
{
|
||||
@@ -1944,6 +1964,7 @@ static int rkcif_assign_new_buffer_update_toisp(struct rkcif_stream *stream,
|
||||
struct sditf_priv *priv = dev->sditf[0];
|
||||
u32 frm_addr_y, buff_addr_y;
|
||||
unsigned long flags;
|
||||
int on = 0;
|
||||
|
||||
if (mbus_cfg->type == V4L2_MBUS_CSI2_DPHY ||
|
||||
mbus_cfg->type == V4L2_MBUS_CSI2_CPHY ||
|
||||
@@ -2024,17 +2045,6 @@ static int rkcif_assign_new_buffer_update_toisp(struct rkcif_stream *stream,
|
||||
}
|
||||
if (stream->lack_buf_cnt)
|
||||
stream->lack_buf_cnt--;
|
||||
if (stream->lack_buf_cnt == 2 || stream->is_single_cap) {
|
||||
stream->to_stop_dma = RKCIF_DMAEN_BY_ISP;
|
||||
rkcif_stop_dma_capture(stream);
|
||||
stream->is_single_cap = false;
|
||||
if ((dev->hdr.hdr_mode == NO_HDR && atomic_read(&dev->streamoff_cnt) == 1) ||
|
||||
(dev->hdr.hdr_mode == HDR_X2 && atomic_read(&dev->streamoff_cnt) == 2) ||
|
||||
(dev->hdr.hdr_mode == HDR_X3 && atomic_read(&dev->streamoff_cnt) == 3)) {
|
||||
dev->sensor_work.on = 0;
|
||||
schedule_work(&dev->sensor_work.work);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (priv->mode.rdbk_mode == RKISP_VICAP_ONLINE)
|
||||
goto out_get_buf;
|
||||
@@ -2107,7 +2117,18 @@ static int rkcif_assign_new_buffer_update_toisp(struct rkcif_stream *stream,
|
||||
if (dev->is_support_tools && stream->tools_vdev && active_buf)
|
||||
rkcif_rdbk_with_tools(stream, active_buf);
|
||||
}
|
||||
|
||||
if (stream->is_single_cap) {
|
||||
stream->to_stop_dma = RKCIF_DMAEN_BY_ISP;
|
||||
rkcif_stop_dma_capture(stream);
|
||||
rkcif_dphy_quick_stream(stream->cifdev, on);
|
||||
stream->is_single_cap = false;
|
||||
if ((dev->hdr.hdr_mode == NO_HDR && atomic_read(&dev->streamoff_cnt) == 1) ||
|
||||
(dev->hdr.hdr_mode == HDR_X2 && atomic_read(&dev->streamoff_cnt) == 2) ||
|
||||
(dev->hdr.hdr_mode == HDR_X3 && atomic_read(&dev->streamoff_cnt) == 3)) {
|
||||
dev->sensor_work.on = 0;
|
||||
schedule_work(&dev->sensor_work.work);
|
||||
}
|
||||
}
|
||||
out_get_buf:
|
||||
stream->frame_phase_cache = stream->frame_phase;
|
||||
if (buffer) {
|
||||
@@ -2531,6 +2552,7 @@ static int rkcif_assign_new_buffer_update(struct rkcif_stream *stream,
|
||||
int ret = 0;
|
||||
u32 buff_addr_y, buff_addr_cbcr;
|
||||
unsigned long flags;
|
||||
int on = 0;
|
||||
|
||||
if (mbus_cfg->type == V4L2_MBUS_CSI2_DPHY ||
|
||||
mbus_cfg->type == V4L2_MBUS_CSI2_CPHY ||
|
||||
@@ -2629,6 +2651,18 @@ static int rkcif_assign_new_buffer_update(struct rkcif_stream *stream,
|
||||
}
|
||||
}
|
||||
stream->frame_phase_cache = stream->frame_phase;
|
||||
if (stream->is_single_cap) {
|
||||
stream->to_stop_dma = RKCIF_DMAEN_BY_VICAP;
|
||||
rkcif_stop_dma_capture(stream);
|
||||
rkcif_dphy_quick_stream(stream->cifdev, on);
|
||||
stream->is_single_cap = false;
|
||||
if ((dev->hdr.hdr_mode == NO_HDR && atomic_read(&dev->streamoff_cnt) == 1) ||
|
||||
(dev->hdr.hdr_mode == HDR_X2 && atomic_read(&dev->streamoff_cnt) == 2) ||
|
||||
(dev->hdr.hdr_mode == HDR_X3 && atomic_read(&dev->streamoff_cnt) == 3)) {
|
||||
dev->sensor_work.on = on;
|
||||
schedule_work(&dev->sensor_work.work);
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer) {
|
||||
buff_addr_y = buffer->buff_addr[RKCIF_PLANE_Y];
|
||||
@@ -6610,6 +6644,8 @@ void rkcif_stream_init(struct rkcif_device *dev, u32 id)
|
||||
stream->is_single_cap = false;
|
||||
atomic_set(&stream->buf_cnt, 0);
|
||||
stream->rx_buf_num = 0;
|
||||
init_completion(&stream->stop_complete);
|
||||
stream->is_wait_stop_complete = false;
|
||||
}
|
||||
|
||||
static int rkcif_fh_open(struct file *filp)
|
||||
@@ -7209,10 +7245,12 @@ static long rkcif_ioctl_default(struct file *file, void *fh,
|
||||
struct csi_channel_info csi_info;
|
||||
struct rkcif_fps fps;
|
||||
int reset_src;
|
||||
int on = 0;
|
||||
struct rkcif_quick_stream_param *stream_param;
|
||||
bool is_single_dev = false;
|
||||
struct v4l2_subdev *sd;
|
||||
int ret = -EINVAL;
|
||||
int i = 0;
|
||||
int stream_num = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case RKCIF_CMD_GET_CSI_MEMORY_MODE:
|
||||
@@ -7262,12 +7300,22 @@ static long rkcif_ioctl_default(struct file *file, void *fh,
|
||||
reset_src = *(int *)arg;
|
||||
return rkcif_do_reset_work(dev, reset_src);
|
||||
case RKCIF_CMD_SET_QUICK_STREAM:
|
||||
on = *(int *)arg;
|
||||
stream_param = (struct rkcif_quick_stream_param *)arg;
|
||||
if (!dev->sditf[0])
|
||||
return -EINVAL;
|
||||
if (on) {
|
||||
if (dev->hdr.hdr_mode == HDR_X2)
|
||||
stream_num = 2;
|
||||
else if (dev->hdr.hdr_mode == HDR_X3)
|
||||
stream_num = 3;
|
||||
else
|
||||
stream_num = 1;
|
||||
if (stream_param->on) {
|
||||
is_single_dev = rkcif_check_single_dev_stream_on(dev->hw_dev);
|
||||
if (is_single_dev) {
|
||||
for (i = 0; i < stream_num - 1; i++) {
|
||||
dev->stream[i].to_en_dma = RKCIF_DMAEN_BY_ISP;
|
||||
rkcif_enable_dma_capture(&dev->stream[i], true);
|
||||
}
|
||||
dev->sditf[0]->mode.rdbk_mode = RKISP_VICAP_ONLINE;
|
||||
sditf_change_to_online(dev->sditf[0]);
|
||||
sd = get_rkisp_sd(dev->sditf[0]);
|
||||
@@ -7279,23 +7327,47 @@ static long rkcif_ioctl_default(struct file *file, void *fh,
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
dev->sditf[0]->mode.rdbk_mode = RKISP_VICAP_RDBK_AUTO;
|
||||
sditf_disable_immediately(dev->sditf[0]);
|
||||
stream->to_en_dma = RKCIF_DMAEN_BY_ISP;
|
||||
rkcif_enable_dma_capture(stream, true);
|
||||
for (i = 0; i < stream_num; i++) {
|
||||
if (dev->sditf[0]->mode.rdbk_mode == RKISP_VICAP_RDBK_AUTO)
|
||||
dev->stream[i].to_en_dma = RKCIF_DMAEN_BY_ISP;
|
||||
else
|
||||
dev->stream[i].to_en_dma = RKCIF_DMAEN_BY_VICAP;
|
||||
rkcif_enable_dma_capture(&dev->stream[i], true);
|
||||
}
|
||||
}
|
||||
|
||||
rkcif_dphy_quick_stream(dev, stream_param->on);
|
||||
v4l2_subdev_call(dev->terminal_sensor.sd, core, ioctl,
|
||||
RKMODULE_SET_QUICK_STREAM, &on);
|
||||
RKMODULE_SET_QUICK_STREAM, &stream_param->on);
|
||||
} else {
|
||||
if (dev->sditf[0]->mode.rdbk_mode == RKISP_VICAP_RDBK_AUTO) {
|
||||
stream->to_stop_dma = RKCIF_DMAEN_BY_ISP;
|
||||
} else if (dev->sditf[0]->mode.rdbk_mode == RKISP_VICAP_RDBK_AIQ) {
|
||||
stream->to_stop_dma = RKCIF_DMAEN_BY_VICAP;
|
||||
} else {
|
||||
stream->cifdev->sensor_state = 0;
|
||||
if (dev->sditf[0]->mode.rdbk_mode == RKISP_VICAP_ONLINE) {
|
||||
for (i = 0; i < stream_num - 1; i++) {
|
||||
reinit_completion(&dev->stream[i].stop_complete);
|
||||
dev->stream[i].is_wait_stop_complete = true;
|
||||
dev->stream[i].to_stop_dma = RKCIF_DMAEN_BY_ISP;
|
||||
wait_for_completion_timeout(&dev->stream[i].stop_complete,
|
||||
msecs_to_jiffies(RKCIF_STOP_MAX_WAIT_TIME_MS));
|
||||
}
|
||||
stream->cifdev->sensor_state = stream_param->on;
|
||||
stream->cifdev->sensor_state_change = true;
|
||||
dev->stream[i].is_wait_stop_complete = true;
|
||||
wait_for_completion_timeout(&dev->stream[i].stop_complete,
|
||||
msecs_to_jiffies(RKCIF_STOP_MAX_WAIT_TIME_MS));
|
||||
} else {
|
||||
for (i = 0; i < stream_num; i++) {
|
||||
dev->stream[i].is_wait_stop_complete = true;
|
||||
reinit_completion(&dev->stream[i].stop_complete);
|
||||
if (dev->sditf[0]->mode.rdbk_mode == RKISP_VICAP_RDBK_AUTO)
|
||||
dev->stream[i].to_stop_dma = RKCIF_DMAEN_BY_ISP;
|
||||
else
|
||||
dev->stream[i].to_stop_dma = RKCIF_DMAEN_BY_VICAP;
|
||||
wait_for_completion_timeout(&dev->stream[i].stop_complete,
|
||||
msecs_to_jiffies(RKCIF_STOP_MAX_WAIT_TIME_MS));
|
||||
}
|
||||
}
|
||||
stream_param->frame_num = dev->stream[0].frame_idx - 1;
|
||||
if (!dev->is_rtt_suspend)
|
||||
dev->resume_mode = stream_param->resume_mode;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -9994,6 +10066,7 @@ static void rkcif_toisp_check_stop_status(struct sditf_priv *priv,
|
||||
int i = 0;
|
||||
u32 val = 0;
|
||||
u64 cur_time = 0;
|
||||
int on = 0;
|
||||
|
||||
for (i = 0; i < TOISP_CH_MAX; i++) {
|
||||
ch = rkcif_g_toisp_ch(intstat_glb, index);
|
||||
@@ -10012,9 +10085,20 @@ static void rkcif_toisp_check_stop_status(struct sditf_priv *priv,
|
||||
wake_up(&stream->wq_stopped);
|
||||
}
|
||||
if (stream->cifdev->sensor_state_change) {
|
||||
rkcif_dphy_quick_stream(stream->cifdev, on);
|
||||
stream->cifdev->sensor_work.on = stream->cifdev->sensor_state;
|
||||
schedule_work(&stream->cifdev->sensor_work.work);
|
||||
stream->cifdev->sensor_state_change = false;
|
||||
if (stream->is_wait_stop_complete) {
|
||||
stream->is_wait_stop_complete = false;
|
||||
complete(&stream->stop_complete);
|
||||
}
|
||||
}
|
||||
if (stream->is_single_cap) {
|
||||
rkcif_dphy_quick_stream(stream->cifdev, on);
|
||||
stream->cifdev->sensor_work.on = 0;
|
||||
schedule_work(&stream->cifdev->sensor_work.work);
|
||||
stream->is_single_cap = false;
|
||||
}
|
||||
if (stream->cifdev->chip_id >= CHIP_RV1106_CIF)
|
||||
rkcif_modify_frame_skip_config(stream);
|
||||
@@ -10329,7 +10413,6 @@ static void rkcif_get_resmem_head(struct rkcif_device *cif_dev)
|
||||
int cam_idx = 0;
|
||||
char cam_idx_str[3] = {0};
|
||||
|
||||
cif_dev->resume_mode = RKISP_RTT_MODE_NORMAL;
|
||||
if (!cif_dev->is_thunderboot && !cif_dev->is_rtt_suspend)
|
||||
return;
|
||||
strscpy(cam_idx_str, cif_dev->terminal_sensor.sd->name + 1, 2);
|
||||
@@ -10403,7 +10486,7 @@ static int rkcif_subdevs_set_stream(struct rkcif_device *cif_dev, int on)
|
||||
for (i = 0; i < p->num_subdevs; i++) {
|
||||
if (p->subdevs[i] == terminal_sensor->sd && on)
|
||||
rkcif_set_sof(cif_dev, cif_dev->stream[0].frame_idx);
|
||||
if (p->subdevs[i] == terminal_sensor->sd && cif_dev->is_rtt_suspend) {
|
||||
if (p->subdevs[i] == terminal_sensor->sd) {
|
||||
if (!rk_tb_mcu_is_done() && on) {
|
||||
cif_dev->tb_client.data = p->subdevs[i];
|
||||
cif_dev->tb_client.cb = rkcif_sensor_quick_streaming_cb;
|
||||
@@ -10517,6 +10600,7 @@ int rkcif_stream_resume(struct rkcif_device *cif_dev, int mode)
|
||||
{
|
||||
struct rkcif_stream *stream = NULL;
|
||||
struct sditf_priv *priv = cif_dev->sditf[0];
|
||||
struct v4l2_subdev *sd = NULL;
|
||||
int ret = 0;
|
||||
int i = 0;
|
||||
u32 capture_mode = 0;
|
||||
@@ -10530,9 +10614,26 @@ int rkcif_stream_resume(struct rkcif_device *cif_dev, int mode)
|
||||
rkcif_get_resmem_head(cif_dev);
|
||||
is_single_dev = rkcif_check_single_dev_stream_on(cif_dev->hw_dev);
|
||||
if (cif_dev->resume_mode == RKISP_RTT_MODE_ONE_FRAME) {
|
||||
capture_mode = RKCIF_STREAM_MODE_TOISP_RDBK;
|
||||
if (priv)
|
||||
priv->mode.rdbk_mode = RKISP_VICAP_RDBK_AUTO;
|
||||
if (cif_dev->is_rtt_suspend) {
|
||||
capture_mode = RKCIF_STREAM_MODE_TOISP_RDBK;
|
||||
if (priv)
|
||||
priv->mode.rdbk_mode = RKISP_VICAP_RDBK_AUTO;
|
||||
} else {
|
||||
if (priv && priv->mode.rdbk_mode == RKISP_VICAP_ONLINE) {
|
||||
if (cif_dev->chip_id == CHIP_RV1106_CIF) {
|
||||
capture_mode = RKCIF_STREAM_MODE_TOISP_RDBK;
|
||||
priv->mode.rdbk_mode = RKISP_VICAP_RDBK_AUTO;
|
||||
} else {
|
||||
capture_mode = RKCIF_STREAM_MODE_TOISP;
|
||||
}
|
||||
} else if (priv &&
|
||||
(priv->mode.rdbk_mode == RKISP_VICAP_RDBK_AUTO ||
|
||||
priv->mode.rdbk_mode == RKISP_VICAP_RDBK_AUTO_ONE_FRAME)) {
|
||||
capture_mode = RKCIF_STREAM_MODE_TOISP_RDBK;
|
||||
} else {
|
||||
capture_mode = RKCIF_STREAM_MODE_CAPTURE;
|
||||
}
|
||||
}
|
||||
} else if (cif_dev->resume_mode == RKISP_RTT_MODE_MULTI_FRAME) {
|
||||
if (is_single_dev) {
|
||||
capture_mode = RKCIF_STREAM_MODE_TOISP;
|
||||
@@ -10546,7 +10647,9 @@ int rkcif_stream_resume(struct rkcif_device *cif_dev, int mode)
|
||||
} else {
|
||||
if (priv && priv->mode.rdbk_mode == RKISP_VICAP_ONLINE)
|
||||
capture_mode = RKCIF_STREAM_MODE_TOISP;
|
||||
else if (priv && priv->mode.rdbk_mode == RKISP_VICAP_RDBK_AUTO)
|
||||
else if (priv &&
|
||||
(priv->mode.rdbk_mode == RKISP_VICAP_RDBK_AUTO ||
|
||||
priv->mode.rdbk_mode == RKISP_VICAP_RDBK_AUTO_ONE_FRAME))
|
||||
capture_mode = RKCIF_STREAM_MODE_TOISP_RDBK;
|
||||
else
|
||||
capture_mode = RKCIF_STREAM_MODE_CAPTURE;
|
||||
@@ -10621,6 +10724,18 @@ int rkcif_stream_resume(struct rkcif_device *cif_dev, int mode)
|
||||
(priv->hdr_cfg.hdr_mode == HDR_X3 && (stream->id == 0 || stream->id == 1))))
|
||||
rkcif_init_rx_buf(stream, 1);
|
||||
} else {
|
||||
if (stream->is_single_cap && stream->id == 0) {
|
||||
priv->mode.rdbk_mode = RKISP_VICAP_RDBK_AUTO_ONE_FRAME;
|
||||
sd = get_rkisp_sd(priv);
|
||||
if (sd) {
|
||||
ret = v4l2_subdev_call(sd, core, ioctl,
|
||||
RKISP_VICAP_CMD_MODE, &priv->mode);
|
||||
if (ret)
|
||||
v4l2_err(&cif_dev->v4l2_dev,
|
||||
"set isp work mode rdbk aotu oneframe fail\n");
|
||||
|
||||
}
|
||||
}
|
||||
sditf_disable_immediately(priv);
|
||||
if (!stream->rx_buf_num &&
|
||||
capture_mode == RKCIF_STREAM_MODE_TOISP_RDBK) {
|
||||
@@ -10884,6 +10999,13 @@ void rkcif_irq_pingpong_v1(struct rkcif_device *cif_dev)
|
||||
stream->is_finish_stop_dma = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (stream->is_finish_stop_dma && stream->is_wait_stop_complete) {
|
||||
stream->is_finish_stop_dma = false;
|
||||
stream->is_wait_stop_complete = false;
|
||||
complete(&stream->stop_complete);
|
||||
}
|
||||
|
||||
if (stream->crop_dyn_en)
|
||||
rkcif_dynamic_crop(stream);
|
||||
|
||||
@@ -10922,12 +11044,13 @@ void rkcif_irq_pingpong_v1(struct rkcif_device *cif_dev)
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&stream->vbq_lock, flags);
|
||||
if (!(stream->cur_stream_mode & RKCIF_STREAM_MODE_TOISP) && stream->lack_buf_cnt == 2) {
|
||||
if (!(stream->cur_stream_mode & RKCIF_STREAM_MODE_TOISP) &&
|
||||
stream->lack_buf_cnt == 2) {
|
||||
stream->to_stop_dma = RKCIF_DMAEN_BY_VICAP;
|
||||
rkcif_stop_dma_capture(stream);
|
||||
v4l2_dbg(4, rkcif_debug, &cif_dev->v4l2_dev,
|
||||
"stream[%d] to stop dma, lack_buf_cnt %d\n",
|
||||
stream->id, stream->lack_buf_cnt);
|
||||
stream->to_stop_dma = RKCIF_DMAEN_BY_VICAP;
|
||||
rkcif_stop_dma_capture(stream);
|
||||
}
|
||||
spin_unlock_irqrestore(&stream->vbq_lock, flags);
|
||||
if (stream->to_en_scale) {
|
||||
|
||||
@@ -2216,7 +2216,7 @@ static int rkcif_plat_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused rkcif_runtime_prepare(struct device *dev)
|
||||
static int __maybe_unused rkcif_sleep_suspend(struct device *dev)
|
||||
{
|
||||
struct rkcif_device *cif_dev = dev_get_drvdata(dev);
|
||||
|
||||
@@ -2224,11 +2224,12 @@ static int __maybe_unused rkcif_runtime_prepare(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __maybe_unused rkcif_runtime_complete(struct device *dev)
|
||||
static int __maybe_unused rkcif_sleep_resume(struct device *dev)
|
||||
{
|
||||
struct rkcif_device *cif_dev = dev_get_drvdata(dev);
|
||||
|
||||
rkcif_stream_resume(cif_dev, RKCIF_RESUME_CIF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused rkcif_runtime_suspend(struct device *dev)
|
||||
@@ -2301,8 +2302,7 @@ late_initcall(rkcif_clr_unready_dev);
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops rkcif_plat_pm_ops = {
|
||||
.prepare = rkcif_runtime_prepare,
|
||||
.complete = rkcif_runtime_complete,
|
||||
SET_SYSTEM_SLEEP_PM_OPS(rkcif_sleep_suspend, rkcif_sleep_resume)
|
||||
SET_RUNTIME_PM_OPS(rkcif_runtime_suspend, rkcif_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
|
||||
@@ -94,6 +94,11 @@
|
||||
#define CROP_SRC_SENSOR_MASK (0x1 << 0)
|
||||
#define CROP_SRC_USR_MASK (0x1 << 1)
|
||||
|
||||
/*
|
||||
* max wait time for stream stop
|
||||
*/
|
||||
#define RKCIF_STOP_MAX_WAIT_TIME_MS (500)
|
||||
|
||||
enum rkcif_workmode {
|
||||
RKCIF_WORKMODE_ONEFRAME = 0x00,
|
||||
RKCIF_WORKMODE_PINGPONG = 0x01,
|
||||
@@ -541,6 +546,7 @@ struct rkcif_stream {
|
||||
int last_frame_idx;
|
||||
int new_fource_idx;
|
||||
atomic_t buf_cnt;
|
||||
struct completion stop_complete;
|
||||
bool stopping;
|
||||
bool crop_enable;
|
||||
bool crop_dyn_en;
|
||||
@@ -559,6 +565,7 @@ struct rkcif_stream {
|
||||
bool is_stop_capture;
|
||||
bool is_wait_dma_stop;
|
||||
bool is_single_cap;
|
||||
bool is_wait_stop_complete;
|
||||
};
|
||||
|
||||
struct rkcif_lvds_subdev {
|
||||
|
||||
@@ -494,9 +494,9 @@ static int sditf_channel_enable(struct sditf_priv *priv, int user)
|
||||
ch0 = 24;//dvp
|
||||
ctrl_val = (ch0 << 3) | 0x1;
|
||||
if (user == 0)
|
||||
int_en = CIF_TOISP0_FS(0);
|
||||
int_en = CIF_TOISP0_FS(0) | CIF_TOISP0_FE(0);
|
||||
else
|
||||
int_en = CIF_TOISP1_FS(0);
|
||||
int_en = CIF_TOISP1_FS(0) | CIF_TOISP1_FE(0);
|
||||
priv->toisp_inf.ch_info[0].is_valid = true;
|
||||
priv->toisp_inf.ch_info[0].id = ch0;
|
||||
} else if (priv->hdr_cfg.hdr_mode == HDR_X2) {
|
||||
@@ -505,9 +505,11 @@ static int sditf_channel_enable(struct sditf_priv *priv, int user)
|
||||
ctrl_val = (ch0 << 3) | 0x1;
|
||||
ctrl_val |= (ch1 << 11) | 0x100;
|
||||
if (user == 0)
|
||||
int_en = CIF_TOISP0_FS(0) | CIF_TOISP0_FS(1);
|
||||
int_en = CIF_TOISP0_FS(0) | CIF_TOISP0_FS(1) |
|
||||
CIF_TOISP0_FE(0) | CIF_TOISP0_FE(1);
|
||||
else
|
||||
int_en = CIF_TOISP1_FS(0) | CIF_TOISP1_FS(1);
|
||||
int_en = CIF_TOISP1_FS(0) | CIF_TOISP1_FS(1) |
|
||||
CIF_TOISP1_FE(0) | CIF_TOISP1_FE(1);
|
||||
priv->toisp_inf.ch_info[0].is_valid = true;
|
||||
priv->toisp_inf.ch_info[0].id = ch0;
|
||||
priv->toisp_inf.ch_info[1].is_valid = true;
|
||||
@@ -520,9 +522,11 @@ static int sditf_channel_enable(struct sditf_priv *priv, int user)
|
||||
ctrl_val |= (ch1 << 11) | 0x100;
|
||||
ctrl_val |= (ch2 << 19) | 0x10000;
|
||||
if (user == 0)
|
||||
int_en = CIF_TOISP0_FS(0) | CIF_TOISP0_FS(1) | CIF_TOISP0_FS(2);
|
||||
int_en = CIF_TOISP0_FS(0) | CIF_TOISP0_FS(1) | CIF_TOISP0_FS(2) |
|
||||
CIF_TOISP0_FE(0) | CIF_TOISP0_FE(1) | CIF_TOISP0_FE(2);
|
||||
else
|
||||
int_en = CIF_TOISP1_FS(0) | CIF_TOISP1_FS(1) | CIF_TOISP1_FS(2);
|
||||
int_en = CIF_TOISP1_FS(0) | CIF_TOISP1_FS(1) | CIF_TOISP1_FS(2) |
|
||||
CIF_TOISP1_FE(0) | CIF_TOISP1_FE(1) | CIF_TOISP1_FE(2);
|
||||
priv->toisp_inf.ch_info[0].is_valid = true;
|
||||
priv->toisp_inf.ch_info[0].id = ch0;
|
||||
priv->toisp_inf.ch_info[1].is_valid = true;
|
||||
@@ -853,7 +857,7 @@ static int sditf_s_rx_buffer(struct v4l2_subdev *sd,
|
||||
return -EINVAL;
|
||||
|
||||
rx_buf = to_cif_rx_buf(dbufs);
|
||||
v4l2_dbg(rkcif_debug, 3, &cif_dev->v4l2_dev, "buf back to vicap 0x%x\n",
|
||||
v4l2_dbg(3, rkcif_debug, &cif_dev->v4l2_dev, "buf back to vicap 0x%x\n",
|
||||
(u32)rx_buf->dummy.dma_addr);
|
||||
spin_lock_irqsave(&stream->vbq_lock, flags);
|
||||
stream->last_rx_buf_idx = dbufs->sequence + 1;
|
||||
|
||||
@@ -10,4 +10,4 @@ obj-$(CONFIG_SERDES_DISPLAY_CHIP_NOVO) += novo/
|
||||
|
||||
serdes-mfd-display-$(CONFIG_MFD_SERDES_DISPLAY) += serdes-core.o serdes-irq.o
|
||||
|
||||
obj-$(CONFIG_MFD_SERDES_DISPLAY) += serdes-mfd-display.o serdes-i2c.o serdes-bridge.o serdes-panel.o serdes-gpio.o serdes-pinctrl.o
|
||||
obj-$(CONFIG_MFD_SERDES_DISPLAY) += serdes-mfd-display.o serdes-i2c.o serdes-bridge.o serdes-bridge-split.o serdes-panel.o serdes-panel-split.o serdes-gpio.o serdes-pinctrl.o
|
||||
|
||||
@@ -102,8 +102,15 @@
|
||||
#endif
|
||||
|
||||
#define MFD_SERDES_DISPLAY_VERSION "serdes-mfd-displaly-v10-230901"
|
||||
|
||||
#define MAX_NUM_SERDES_SPLIT 8
|
||||
struct serdes;
|
||||
enum ser_link_mode {
|
||||
SER_DUAL_LINK,
|
||||
SER_LINKA,
|
||||
SER_LINKB,
|
||||
SER_SPLITTER_MODE,
|
||||
};
|
||||
|
||||
struct serdes_chip_pinctrl_info {
|
||||
struct pinctrl_pin_desc *pins;
|
||||
unsigned int num_pins;
|
||||
@@ -163,6 +170,12 @@ struct serdes_chip_gpio_ops {
|
||||
int (*to_irq)(struct serdes *serdes, int gpio);
|
||||
};
|
||||
|
||||
struct serdes_chip_split_ops {
|
||||
int (*select)(struct serdes *serdes, int chan);
|
||||
int (*deselect)(struct serdes *serdes, int chan);
|
||||
int (*set_i2c_addr)(struct serdes *serdes, int address, int link);
|
||||
};
|
||||
|
||||
struct serdes_chip_pm_ops {
|
||||
/* serdes chip function for suspend and resume */
|
||||
int (*suspend)(struct serdes *serdes);
|
||||
@@ -198,6 +211,7 @@ struct serdes_chip_data {
|
||||
struct serdes_chip_panel_ops *panel_ops;
|
||||
struct serdes_chip_pinctrl_ops *pinctrl_ops;
|
||||
struct serdes_chip_gpio_ops *gpio_ops;
|
||||
struct serdes_chip_split_ops *split_ops;
|
||||
struct serdes_chip_pm_ops *pm_ops;
|
||||
struct serdes_chip_irq_ops *irq_ops;
|
||||
};
|
||||
@@ -241,7 +255,35 @@ struct serdes_panel {
|
||||
struct serdes *parent;
|
||||
struct regmap *regmap;
|
||||
struct mipi_dsi_device *dsi;
|
||||
struct device_node *dsi_node;
|
||||
struct device_node *remote_node;
|
||||
struct drm_display_mode mode;
|
||||
struct backlight_device *backlight;
|
||||
struct serdes_init_seq *serdes_init_seq;
|
||||
bool sel_mipi;
|
||||
bool dv_swp_ab;
|
||||
bool dpi_deskew_en;
|
||||
bool split_mode;
|
||||
u32 num_lanes;
|
||||
u32 dsi_lane_map[4];
|
||||
};
|
||||
|
||||
struct serdes_panel_split {
|
||||
struct drm_panel panel;
|
||||
enum drm_connector_status status;
|
||||
struct drm_connector connector;
|
||||
|
||||
const char *name;
|
||||
u32 width_mm;
|
||||
u32 height_mm;
|
||||
u32 link_rate;
|
||||
u32 lane_count;
|
||||
bool ssc;
|
||||
|
||||
struct device *dev;
|
||||
struct serdes *parent;
|
||||
struct regmap *regmap;
|
||||
struct mipi_dsi_device *dsi;
|
||||
struct device_node *remote_node;
|
||||
struct drm_display_mode mode;
|
||||
struct backlight_device *backlight;
|
||||
struct serdes_init_seq *serdes_init_seq;
|
||||
@@ -265,7 +307,7 @@ struct serdes_bridge {
|
||||
struct serdes *parent;
|
||||
struct regmap *regmap;
|
||||
struct mipi_dsi_device *dsi;
|
||||
struct device_node *dsi_node;
|
||||
struct device_node *remote_node;
|
||||
struct drm_display_mode mode;
|
||||
struct backlight_device *backlight;
|
||||
|
||||
@@ -277,6 +319,29 @@ struct serdes_bridge {
|
||||
u32 dsi_lane_map[4];
|
||||
};
|
||||
|
||||
struct serdes_bridge_split {
|
||||
struct drm_bridge base_bridge;
|
||||
struct drm_bridge *next_bridge;
|
||||
enum drm_connector_status status;
|
||||
atomic_t triggered;
|
||||
struct drm_connector connector;
|
||||
struct drm_panel *panel;
|
||||
|
||||
struct device *dev;
|
||||
struct serdes *parent;
|
||||
struct regmap *regmap;
|
||||
struct mipi_dsi_device *dsi;
|
||||
struct device_node *remote_node;
|
||||
struct drm_display_mode mode;
|
||||
struct backlight_device *backlight;
|
||||
|
||||
bool sel_mipi;
|
||||
bool dv_swp_ab;
|
||||
bool dpi_deskew_en;
|
||||
u32 num_lanes;
|
||||
u32 dsi_lane_map[4];
|
||||
};
|
||||
|
||||
struct serdes {
|
||||
int num_gpio;
|
||||
struct mutex io_lock;
|
||||
@@ -307,6 +372,15 @@ struct serdes {
|
||||
struct delayed_work mfd_delay_work;
|
||||
bool route_enable;
|
||||
bool use_delay_work;
|
||||
|
||||
bool split_mode_enable;
|
||||
unsigned int reg_hw;
|
||||
unsigned int reg_use;
|
||||
unsigned int link_use;
|
||||
unsigned int id_serdes_bridge_split;
|
||||
unsigned int id_serdes_panel_split;
|
||||
struct serdes *g_serdes_bridge_split;
|
||||
|
||||
struct pinctrl *pinctrl_node;
|
||||
struct pinctrl_state *pins_default;
|
||||
struct pinctrl_state *pins_init;
|
||||
@@ -314,7 +388,9 @@ struct serdes {
|
||||
|
||||
struct serdes_init_seq *serdes_init_seq;
|
||||
struct serdes_bridge *serdes_bridge;
|
||||
struct serdes_bridge_split *serdes_bridge_split;
|
||||
struct serdes_panel *serdes_panel;
|
||||
struct serdes_panel_split *serdes_panel_split;
|
||||
struct serdes_pinctrl *pinctrl;
|
||||
struct serdes_chip_data *chip_data;
|
||||
};
|
||||
|
||||
@@ -762,6 +762,84 @@ static struct serdes_chip_gpio_ops max96745_gpio_ops = {
|
||||
.to_irq = max96745_gpio_to_irq,
|
||||
};
|
||||
|
||||
static int max96745_select(struct serdes *serdes, int chan)
|
||||
{
|
||||
/*0076 for linkA and 0086 for linkB*/
|
||||
if (chan == DUAL_LINK) {
|
||||
serdes_set_bits(serdes, 0x0076, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 0));
|
||||
serdes_set_bits(serdes, 0x0086, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 0));
|
||||
SERDES_DBG_CHIP("%s: enable %s remote i2c of linkA and linkB\n", __func__,
|
||||
serdes->chip_data->name);
|
||||
} else if (chan == LINKA) {
|
||||
serdes_set_bits(serdes, 0x0076, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 0));
|
||||
serdes_set_bits(serdes, 0x0086, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 1));
|
||||
SERDES_DBG_CHIP("%s: only enable %s remote i2c of linkA\n", __func__,
|
||||
serdes->chip_data->name);
|
||||
} else if (chan == LINKB) {
|
||||
serdes_set_bits(serdes, 0x0076, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 1));
|
||||
serdes_set_bits(serdes, 0x0086, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 0));
|
||||
SERDES_DBG_CHIP("%s: only enable %s remote i2c of linkB\n", __func__,
|
||||
serdes->chip_data->name);
|
||||
} else if (chan == SPLITTER_MODE) {
|
||||
serdes_set_bits(serdes, 0x0076, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 0));
|
||||
serdes_set_bits(serdes, 0x0086, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 0));
|
||||
SERDES_DBG_CHIP("%s: enable %s remote i2c of linkA and linkB\n", __func__,
|
||||
serdes->chip_data->name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max96745_deselect(struct serdes *serdes, int chan)
|
||||
{
|
||||
|
||||
if (chan == DUAL_LINK) {
|
||||
serdes_set_bits(serdes, 0x0076, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 1));
|
||||
serdes_set_bits(serdes, 0x0086, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 1));
|
||||
SERDES_DBG_CHIP("%s: disable %s remote i2c of linkA and linkB\n", __func__,
|
||||
serdes->chip_data->name);
|
||||
} else if (chan == LINKA) {
|
||||
serdes_set_bits(serdes, 0x0076, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 1));
|
||||
serdes_set_bits(serdes, 0x0086, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 0));
|
||||
SERDES_DBG_CHIP("%s: only disable %s remote i2c of linkA\n", __func__,
|
||||
serdes->chip_data->name);
|
||||
} else if (chan == LINKB) {
|
||||
serdes_set_bits(serdes, 0x0076, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 0));
|
||||
serdes_set_bits(serdes, 0x0086, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 1));
|
||||
SERDES_DBG_CHIP("%s: only disable %s remote i2c of linkB\n", __func__,
|
||||
serdes->chip_data->name);
|
||||
} else if (chan == SPLITTER_MODE) {
|
||||
serdes_set_bits(serdes, 0x0076, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 1));
|
||||
serdes_set_bits(serdes, 0x0086, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 1));
|
||||
SERDES_DBG_CHIP("%s: disable %s remote i2c of linkA and linkB\n", __func__,
|
||||
serdes->chip_data->name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct serdes_chip_split_ops max96745_split_ops = {
|
||||
.select = max96745_select,
|
||||
.deselect = max96745_deselect,
|
||||
};
|
||||
|
||||
static int max96745_pm_suspend(struct serdes *serdes)
|
||||
{
|
||||
return 0;
|
||||
@@ -802,6 +880,7 @@ struct serdes_chip_data serdes_max96745_data = {
|
||||
.bridge_ops = &max96745_bridge_ops,
|
||||
.pinctrl_ops = &max96745_pinctrl_ops,
|
||||
.gpio_ops = &max96745_gpio_ops,
|
||||
.split_ops = &max96745_split_ops,
|
||||
.pm_ops = &max96745_pm_ops,
|
||||
.irq_ops = &max96745_irq_ops,
|
||||
};
|
||||
|
||||
@@ -136,4 +136,11 @@
|
||||
/* 7074h */
|
||||
#define MAX_LINK_RATE GENMASK(7, 0)
|
||||
|
||||
enum link_mode {
|
||||
DUAL_LINK,
|
||||
LINKA,
|
||||
LINKB,
|
||||
SPLITTER_MODE,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -567,6 +567,37 @@ static struct serdes_chip_gpio_ops max96752_gpio_ops = {
|
||||
.to_irq = max96752_gpio_to_irq,
|
||||
};
|
||||
|
||||
static int max96752_set_i2c_addr(struct serdes *serdes, int address, int link)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (link == LINKA) {
|
||||
/* TX_SRC_ID[1] = 0 */
|
||||
ret = serdes_reg_write(serdes, 0x73, 0x31);
|
||||
/* Receive packets with this stream ID = 0 */
|
||||
ret = serdes_reg_write(serdes, 0x50, 0x00);
|
||||
ret = serdes_reg_write(serdes, 0x00, address << 1);
|
||||
} else if (link == LINKB) {
|
||||
/* TX_SRC_ID[1] = 1 */
|
||||
ret = serdes_reg_write(serdes, 0x73, 0x32);
|
||||
/* Receive packets with this stream ID = 1 */
|
||||
ret = serdes_reg_write(serdes, 0x50, 0x01);
|
||||
ret = serdes_reg_write(serdes, 0x00, address << 1);
|
||||
} else {
|
||||
dev_info(serdes->dev, "link %d is error\n", link);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
SERDES_DBG_CHIP("%s: set serdes chip %s i2c 7bit address to 0x%x\n", __func__,
|
||||
serdes->chip_data->name, address);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct serdes_chip_split_ops max96752_split_ops = {
|
||||
.set_i2c_addr = max96752_set_i2c_addr,
|
||||
};
|
||||
|
||||
static int max96752_pm_suspend(struct serdes *serdes)
|
||||
{
|
||||
return 0;
|
||||
@@ -607,6 +638,7 @@ struct serdes_chip_data serdes_max96752_data = {
|
||||
.panel_ops = &max96752_panel_ops,
|
||||
.bridge_ops = &max96752_bridge_ops,
|
||||
.pinctrl_ops = &max96752_pinctrl_ops,
|
||||
.split_ops = &max96752_split_ops,
|
||||
.gpio_ops = &max96752_gpio_ops,
|
||||
.pm_ops = &max96752_pm_ops,
|
||||
.irq_ops = &max96752_irq_ops,
|
||||
|
||||
@@ -35,4 +35,11 @@
|
||||
#define OVR_RES_CFG BIT(7)
|
||||
#define GPIO_RX_ID GENMASK(4, 0)
|
||||
|
||||
enum link_mode {
|
||||
DUAL_LINK,
|
||||
LINKA,
|
||||
LINKB,
|
||||
SPLITTER_MODE,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -679,6 +679,89 @@ static struct serdes_chip_gpio_ops max96789_gpio_ops = {
|
||||
.to_irq = max96789_gpio_to_irq,
|
||||
};
|
||||
|
||||
static int max96789_select(struct serdes *serdes, int chan)
|
||||
{
|
||||
u32 link_cfg, val;
|
||||
int ret;
|
||||
|
||||
serdes_set_bits(serdes, 0x0001, DIS_REM_CC,
|
||||
FIELD_PREP(DIS_REM_CC, 0));
|
||||
|
||||
serdes_reg_read(serdes, 0x0010, &link_cfg);
|
||||
if ((link_cfg & LINK_CFG) == SPLITTER_MODE)
|
||||
SERDES_DBG_CHIP("%s: serdes chip %s already split mode cfg=0x%x\n", __func__,
|
||||
serdes->chip_data->name, link_cfg);
|
||||
|
||||
if (chan == 0 && (link_cfg & LINK_CFG) != DUAL_LINK) {
|
||||
serdes_set_bits(serdes, 0x0004,
|
||||
LINK_EN_B | LINK_EN_A,
|
||||
FIELD_PREP(LINK_EN_A, 1) |
|
||||
FIELD_PREP(LINK_EN_B, 1));
|
||||
serdes_set_bits(serdes, 0x0010,
|
||||
RESET_ONESHOT | AUTO_LINK | LINK_CFG,
|
||||
FIELD_PREP(RESET_ONESHOT, 1) |
|
||||
FIELD_PREP(AUTO_LINK, 0) |
|
||||
FIELD_PREP(LINK_CFG, DUAL_LINK));
|
||||
SERDES_DBG_CHIP("%s: change to use dual link\n", __func__);
|
||||
} else if (chan == 1 && (link_cfg & LINK_CFG) != LINKA) {
|
||||
serdes_set_bits(serdes, 0x0004,
|
||||
LINK_EN_B | LINK_EN_A,
|
||||
FIELD_PREP(LINK_EN_A, 1) |
|
||||
FIELD_PREP(LINK_EN_B, 0));
|
||||
serdes_set_bits(serdes, 0x0010,
|
||||
RESET_ONESHOT | AUTO_LINK | LINK_CFG,
|
||||
FIELD_PREP(RESET_ONESHOT, 1) |
|
||||
FIELD_PREP(AUTO_LINK, 0) |
|
||||
FIELD_PREP(LINK_CFG, LINKA));
|
||||
SERDES_DBG_CHIP("%s: change to use linkA\n", __func__);
|
||||
} else if (chan == 2 && (link_cfg & LINK_CFG) != LINKB) {
|
||||
serdes_set_bits(serdes, 0x0004,
|
||||
LINK_EN_B | LINK_EN_A,
|
||||
FIELD_PREP(LINK_EN_A, 0) |
|
||||
FIELD_PREP(LINK_EN_B, 1));
|
||||
serdes_set_bits(serdes, 0x0010,
|
||||
RESET_ONESHOT | AUTO_LINK | LINK_CFG,
|
||||
FIELD_PREP(RESET_ONESHOT, 1) |
|
||||
FIELD_PREP(AUTO_LINK, 0) |
|
||||
FIELD_PREP(LINK_CFG, LINKB));
|
||||
SERDES_DBG_CHIP("%s: change to use linkB\n", __func__);
|
||||
} else if (chan == 3 && (link_cfg & LINK_CFG) != SPLITTER_MODE) {
|
||||
serdes_set_bits(serdes, 0x0004,
|
||||
LINK_EN_B | LINK_EN_A,
|
||||
FIELD_PREP(LINK_EN_A, 1) |
|
||||
FIELD_PREP(LINK_EN_B, 1));
|
||||
serdes_set_bits(serdes, 0x0010,
|
||||
RESET_ONESHOT | AUTO_LINK | LINK_CFG,
|
||||
FIELD_PREP(RESET_ONESHOT, 1) |
|
||||
FIELD_PREP(AUTO_LINK, 0) |
|
||||
FIELD_PREP(LINK_CFG, SPLITTER_MODE));
|
||||
SERDES_DBG_CHIP("%s: change to use split mode\n", __func__);
|
||||
}
|
||||
|
||||
ret = regmap_read_poll_timeout(serdes->regmap, 0x0013, val,
|
||||
val & LOCKED, 100,
|
||||
50 * USEC_PER_MSEC);
|
||||
if (ret < 0) {
|
||||
dev_err(serdes->dev, "GMSL2 link lock timeout\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max96789_deselect(struct serdes *serdes, int chan)
|
||||
{
|
||||
//serdes_set_bits(serdes, 0x0001, DIS_REM_CC,
|
||||
// FIELD_PREP(DIS_REM_CC, 1));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct serdes_chip_split_ops max96789_split_ops = {
|
||||
.select = max96789_select,
|
||||
.deselect = max96789_deselect,
|
||||
};
|
||||
|
||||
static int max96789_pm_suspend(struct serdes *serdes)
|
||||
{
|
||||
return 0;
|
||||
@@ -719,6 +802,7 @@ struct serdes_chip_data serdes_max96789_data = {
|
||||
.bridge_ops = &max96789_bridge_ops,
|
||||
.pinctrl_ops = &max96789_pinctrl_ops,
|
||||
.gpio_ops = &max96789_gpio_ops,
|
||||
.split_ops = &max96789_split_ops,
|
||||
.pm_ops = &max96789_pm_ops,
|
||||
.irq_ops = &max96789_irq_ops,
|
||||
};
|
||||
|
||||
@@ -39,6 +39,15 @@
|
||||
#define UART_2_EN BIT(5)
|
||||
#define UART_1_EN BIT(4)
|
||||
|
||||
/* 0004h */
|
||||
#define GMSL2_B BIT(7)
|
||||
#define GMSL2_A BIT(6)
|
||||
#define LINK_EN_B BIT(5)
|
||||
#define LINK_EN_A BIT(4)
|
||||
#define AUD_TX_SRC_Y BIT(1)
|
||||
#define AUD_TX_SRC_X BIT(0)
|
||||
|
||||
|
||||
/* 0005h */
|
||||
#define LOCK_EN BIT(7)
|
||||
#define ERRB_EN BIT(6)
|
||||
|
||||
371
drivers/mfd/display-serdes/serdes-bridge-split.c
Normal file
371
drivers/mfd/display-serdes/serdes-bridge-split.c
Normal file
@@ -0,0 +1,371 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* serdes-bridge.c -- drm bridge access for different serdes chips
|
||||
*
|
||||
* Copyright (c) 2023-2028 Rockchip Electronics Co. Ltd.
|
||||
*
|
||||
* Author: luowei <lw@rock-chips.com>
|
||||
*/
|
||||
|
||||
#include "core.h"
|
||||
|
||||
static struct serdes_bridge_split *to_serdes_bridge_split(struct drm_bridge *bridge)
|
||||
{
|
||||
return container_of(bridge, struct serdes_bridge_split, base_bridge);
|
||||
}
|
||||
|
||||
static struct mipi_dsi_device *serdes_attach_dsi(struct serdes_bridge_split *serdes_bridge_split,
|
||||
struct device_node *remote_node)
|
||||
{
|
||||
struct mipi_dsi_device_info info = { "serdes", 0, NULL };
|
||||
struct serdes *serdes = serdes_bridge_split->parent;
|
||||
struct mipi_dsi_device *dsi;
|
||||
struct mipi_dsi_host *host;
|
||||
int ret;
|
||||
|
||||
if (serdes->chip_data->name)
|
||||
memcpy(&info.type, serdes->chip_data->name, ARRAY_SIZE(info.type));
|
||||
|
||||
SERDES_DBG_MFD("%s: type=%s, name=%s\n", __func__,
|
||||
info.type, serdes->chip_data->name);
|
||||
|
||||
host = of_find_mipi_dsi_host_by_node(remote_node);
|
||||
if (!host) {
|
||||
dev_err(serdes_bridge_split->dev, "failed to find serdes dsi host\n");
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
|
||||
dsi = mipi_dsi_device_register_full(host, &info);
|
||||
if (IS_ERR(dsi)) {
|
||||
dev_err(serdes_bridge_split->dev, "failed to create serdes dsi device\n");
|
||||
return dsi;
|
||||
}
|
||||
|
||||
dsi->lanes = 4;
|
||||
dsi->format = MIPI_DSI_FMT_RGB888;
|
||||
|
||||
if (serdes->chip_data->name) {
|
||||
if ((!strcmp(serdes->chip_data->name, "bu18tl82")) ||
|
||||
(!strcmp(serdes->chip_data->name, "bu18rl82"))) {
|
||||
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
|
||||
MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_EOT_PACKET;
|
||||
SERDES_DBG_MFD("%s: %s dsi_mode MIPI_DSI_MODE_VIDEO_BURST 0x%lx\n",
|
||||
__func__, serdes->chip_data->name, dsi->mode_flags);
|
||||
} else {
|
||||
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
|
||||
SERDES_DBG_MFD("%s: %s dsi_mode MIPI_DSI_MODE_VIDEO_SYNC_PULSE 0x%lx\n",
|
||||
__func__, serdes->chip_data->name, dsi->mode_flags);
|
||||
}
|
||||
} else {
|
||||
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
|
||||
SERDES_DBG_MFD("%s: %s dsi_mode MIPI_DSI_MODE_VIDEO_SYNC_PULSE 0x%lx\n",
|
||||
__func__, serdes->chip_data->name, dsi->mode_flags);
|
||||
}
|
||||
|
||||
ret = mipi_dsi_attach(dsi);
|
||||
if (ret < 0) {
|
||||
dev_err(serdes_bridge_split->dev, "failed to attach serdes dsi to host\n");
|
||||
mipi_dsi_device_unregister(dsi);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return dsi;
|
||||
}
|
||||
|
||||
static int serdes_bridge_split_attach(struct drm_bridge *bridge,
|
||||
enum drm_bridge_attach_flags flags)
|
||||
{
|
||||
struct serdes_bridge_split *serdes_bridge_split = to_serdes_bridge_split(bridge);
|
||||
struct serdes *serdes = serdes_bridge_split->parent;
|
||||
int ret = 0;
|
||||
|
||||
ret = drm_of_find_panel_or_bridge(bridge->of_node, 1, -1,
|
||||
&serdes_bridge_split->panel,
|
||||
&serdes_bridge_split->next_bridge);
|
||||
if (ret) {
|
||||
dev_err(serdes_bridge_split->dev->parent,
|
||||
"failed to find serdes bridge, ret=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (serdes_bridge_split->sel_mipi) {
|
||||
dev_info(serdes_bridge_split->dev->parent, "serdes sel_mipi %d\n",
|
||||
serdes_bridge_split->sel_mipi);
|
||||
/* Attach primary DSI */
|
||||
serdes_bridge_split->dsi = serdes_attach_dsi(serdes_bridge_split,
|
||||
serdes_bridge_split->remote_node);
|
||||
if (IS_ERR(serdes_bridge_split->dsi))
|
||||
return PTR_ERR(serdes_bridge_split->dsi);
|
||||
}
|
||||
|
||||
if (serdes_bridge_split->next_bridge) {
|
||||
ret = drm_bridge_attach(bridge->encoder, serdes_bridge_split->next_bridge,
|
||||
bridge, flags);
|
||||
if (ret) {
|
||||
if (serdes_bridge_split->sel_mipi)
|
||||
mipi_dsi_device_unregister(serdes_bridge_split->dsi);
|
||||
|
||||
dev_err(serdes_bridge_split->dev->parent,
|
||||
"failed to attach bridge, ret=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (serdes->chip_data->bridge_ops->attach)
|
||||
ret = serdes->chip_data->bridge_ops->attach(serdes);
|
||||
|
||||
SERDES_DBG_MFD("%s: ret=%d\n", __func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void serdes_bridge_split_detach(struct drm_bridge *bridge)
|
||||
{
|
||||
struct serdes_bridge_split *serdes_bridge_split = to_serdes_bridge_split(bridge);
|
||||
|
||||
if (serdes_bridge_split->sel_mipi) {
|
||||
mipi_dsi_detach(serdes_bridge_split->dsi);
|
||||
mipi_dsi_device_unregister(serdes_bridge_split->dsi);
|
||||
}
|
||||
|
||||
SERDES_DBG_MFD("%s\n", __func__);
|
||||
}
|
||||
|
||||
static void serdes_bridge_split_disable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct serdes_bridge_split *serdes_bridge_split = to_serdes_bridge_split(bridge);
|
||||
struct serdes *serdes = serdes_bridge_split->parent;
|
||||
int ret = 0;
|
||||
|
||||
if (serdes_bridge_split->panel)
|
||||
drm_panel_disable(serdes_bridge_split->panel);
|
||||
|
||||
if (serdes->chip_data->bridge_ops->disable)
|
||||
ret = serdes->chip_data->bridge_ops->disable(serdes);
|
||||
|
||||
extcon_set_state_sync(serdes->extcon, EXTCON_JACK_VIDEO_OUT, false);
|
||||
|
||||
SERDES_DBG_MFD("%s: ret=%d\n", __func__, ret);
|
||||
}
|
||||
|
||||
static void serdes_bridge_split_post_disable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct serdes_bridge_split *serdes_bridge_split = to_serdes_bridge_split(bridge);
|
||||
struct serdes *serdes = serdes_bridge_split->parent;
|
||||
int ret = 0;
|
||||
|
||||
serdes_set_pinctrl_sleep(serdes);
|
||||
|
||||
if (serdes_bridge_split->panel)
|
||||
ret = drm_panel_unprepare(serdes_bridge_split->panel);
|
||||
|
||||
if (serdes->chip_data->bridge_ops->post_disable)
|
||||
ret = serdes->chip_data->bridge_ops->post_disable(serdes);
|
||||
|
||||
SERDES_DBG_MFD("%s: ret=%d\n", __func__, ret);
|
||||
}
|
||||
|
||||
static void serdes_bridge_split_pre_enable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct serdes_bridge_split *serdes_bridge_split = to_serdes_bridge_split(bridge);
|
||||
struct serdes *serdes = serdes_bridge_split->parent;
|
||||
int ret = 0;
|
||||
|
||||
if (serdes->chip_data->bridge_ops->init)
|
||||
ret = serdes->chip_data->bridge_ops->init(serdes);
|
||||
|
||||
if (serdes->chip_data->serdes_type == TYPE_DES) {
|
||||
if (serdes->chip_data->chip_init)
|
||||
serdes->chip_data->chip_init(serdes);
|
||||
ret = serdes_i2c_set_sequence(serdes);
|
||||
}
|
||||
|
||||
if (serdes->chip_data->bridge_ops->pre_enable)
|
||||
ret = serdes->chip_data->bridge_ops->pre_enable(serdes);
|
||||
|
||||
if (serdes_bridge_split->panel)
|
||||
ret = drm_panel_prepare(serdes_bridge_split->panel);
|
||||
|
||||
serdes_set_pinctrl_default(serdes);
|
||||
|
||||
SERDES_DBG_MFD("%s: %s ret=%d\n", __func__, dev_name(serdes->dev), ret);
|
||||
}
|
||||
|
||||
static void serdes_bridge_split_enable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct serdes_bridge_split *serdes_bridge_split = to_serdes_bridge_split(bridge);
|
||||
struct serdes *serdes = serdes_bridge_split->parent;
|
||||
int ret = 0;
|
||||
|
||||
if (serdes_bridge_split->panel)
|
||||
ret = drm_panel_enable(serdes_bridge_split->panel);
|
||||
|
||||
if (serdes->chip_data->bridge_ops->enable)
|
||||
ret = serdes->chip_data->bridge_ops->enable(serdes);
|
||||
|
||||
if (!ret) {
|
||||
extcon_set_state_sync(serdes->extcon, EXTCON_JACK_VIDEO_OUT, true);
|
||||
SERDES_DBG_MFD("%s: extcon is true\n", __func__);
|
||||
}
|
||||
|
||||
SERDES_DBG_MFD("%s: %s-%s ret=%d\n", __func__, dev_name(serdes->dev),
|
||||
serdes->chip_data->name, ret);
|
||||
}
|
||||
|
||||
static enum drm_connector_status
|
||||
serdes_bridge_split_detect(struct drm_bridge *bridge)
|
||||
{
|
||||
struct serdes_bridge_split *serdes_bridge_split = to_serdes_bridge_split(bridge);
|
||||
struct serdes *serdes = serdes_bridge_split->parent;
|
||||
enum drm_connector_status status = connector_status_connected;
|
||||
|
||||
if (serdes->chip_data->bridge_ops->detect)
|
||||
status = serdes->chip_data->bridge_ops->detect(serdes);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int serdes_bridge_split_get_modes(struct drm_bridge *bridge,
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
struct serdes_bridge_split *serdes_bridge_split = to_serdes_bridge_split(bridge);
|
||||
struct serdes *serdes = serdes_bridge_split->parent;
|
||||
int ret = 0;
|
||||
|
||||
if (serdes->chip_data->bridge_ops->get_modes)
|
||||
ret = serdes->chip_data->bridge_ops->get_modes(serdes);
|
||||
|
||||
if (serdes_bridge_split->next_bridge)
|
||||
ret = drm_bridge_get_modes(serdes_bridge_split->next_bridge, connector);
|
||||
|
||||
if (serdes_bridge_split->panel)
|
||||
ret = drm_panel_get_modes(serdes_bridge_split->panel, connector);
|
||||
|
||||
SERDES_DBG_MFD("%s:name=%s, node=%s\n", __func__,
|
||||
serdes->chip_data->name, serdes_bridge_split->dev->of_node->name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct drm_bridge_funcs serdes_bridge_split_funcs = {
|
||||
.attach = serdes_bridge_split_attach,
|
||||
.detach = serdes_bridge_split_detach,
|
||||
.disable = serdes_bridge_split_disable,
|
||||
.post_disable = serdes_bridge_split_post_disable,
|
||||
.pre_enable = serdes_bridge_split_pre_enable,
|
||||
.enable = serdes_bridge_split_enable,
|
||||
.detect = serdes_bridge_split_detect,
|
||||
.get_modes = serdes_bridge_split_get_modes,
|
||||
.atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt,
|
||||
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
|
||||
.atomic_reset = drm_atomic_helper_bridge_reset,
|
||||
};
|
||||
|
||||
static int serdes_bridge_split_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct serdes *serdes = dev_get_drvdata(pdev->dev.parent);
|
||||
struct device *dev = &pdev->dev;
|
||||
struct serdes_bridge_split *serdes_bridge_split;
|
||||
|
||||
if (!serdes->dev)
|
||||
return -1;
|
||||
|
||||
serdes_bridge_split = devm_kzalloc(dev, sizeof(*serdes_bridge_split), GFP_KERNEL);
|
||||
if (!serdes_bridge_split)
|
||||
return -ENOMEM;
|
||||
|
||||
serdes->serdes_bridge_split = serdes_bridge_split;
|
||||
serdes_bridge_split->dev = dev;
|
||||
serdes_bridge_split->parent = dev_get_drvdata(dev->parent);
|
||||
platform_set_drvdata(pdev, serdes_bridge_split);
|
||||
serdes_bridge_split->regmap = dev_get_regmap(dev->parent, NULL);
|
||||
if (!serdes_bridge_split->regmap)
|
||||
return dev_err_probe(dev, -ENODEV, "failed to get serdes regmap\n");
|
||||
|
||||
serdes_bridge_split->sel_mipi = of_property_read_bool(dev->parent->of_node, "sel-mipi");
|
||||
SERDES_DBG_MFD("%s: sel_mipi=%d\n", __func__, serdes_bridge_split->sel_mipi);
|
||||
|
||||
serdes_bridge_split->base_bridge.of_node = dev->parent->of_node;
|
||||
serdes_bridge_split->remote_node = of_graph_get_remote_node(dev->parent->of_node, 0, -1);
|
||||
if (!serdes_bridge_split->remote_node) {
|
||||
serdes_bridge_split->base_bridge.of_node = dev->of_node;
|
||||
SERDES_DBG_MFD("warning: failed to get remote node for serdes on %s\n",
|
||||
dev_name(dev->parent));
|
||||
serdes_bridge_split->remote_node = of_graph_get_remote_node(dev->of_node, 0, -1);
|
||||
if (!serdes_bridge_split->remote_node) {
|
||||
return dev_err_probe(dev, -ENODEV,
|
||||
"failed to get remote node for serdes dsi\n");
|
||||
}
|
||||
}
|
||||
|
||||
serdes_bridge_split->base_bridge.funcs = &serdes_bridge_split_funcs;
|
||||
serdes_bridge_split->base_bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_MODES;
|
||||
|
||||
if (serdes_bridge_split->sel_mipi) {
|
||||
serdes_bridge_split->base_bridge.type = DRM_MODE_CONNECTOR_DSI;
|
||||
SERDES_DBG_MFD("%s: type DRM_MODE_CONNECTOR_DSI\n", __func__);
|
||||
} else if (serdes_bridge_split->parent->chip_data->connector_type) {
|
||||
serdes_bridge_split->base_bridge.type =
|
||||
serdes_bridge_split->parent->chip_data->connector_type;
|
||||
SERDES_DBG_MFD("%s: type %d\n", __func__, serdes_bridge_split->base_bridge.type);
|
||||
} else {
|
||||
serdes_bridge_split->base_bridge.type = DRM_MODE_CONNECTOR_eDP;
|
||||
SERDES_DBG_MFD("%s: type DRM_MODE_CONNECTOR_LVDS\n", __func__);
|
||||
}
|
||||
|
||||
drm_bridge_add(&serdes_bridge_split->base_bridge);
|
||||
|
||||
dev_info(dev, "serdes %s, %s successful mipi=%d, of_node=%s\n",
|
||||
serdes->chip_data->name, __func__, serdes_bridge_split->sel_mipi,
|
||||
serdes_bridge_split->base_bridge.of_node->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int serdes_bridge_split_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct serdes_bridge_split *serdes_bridge_split = platform_get_drvdata(pdev);
|
||||
|
||||
drm_bridge_remove(&serdes_bridge_split->base_bridge);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id serdes_bridge_split_of_match[] = {
|
||||
{ .compatible = "rohm,bu18tl82-bridge-split", },
|
||||
{ .compatible = "rohm,bu18rl82-bridge-split", },
|
||||
{ .compatible = "maxim,max96745-bridge-split", },
|
||||
{ .compatible = "maxim,max96755-bridge-split", },
|
||||
{ .compatible = "maxim,max96752-bridge-split", },
|
||||
{ .compatible = "maxim,max96789-bridge-split", },
|
||||
{ .compatible = "rockchip,rkx111-bridge-split", },
|
||||
{ .compatible = "rockchip,rkx121-bridge-split", },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct platform_driver serdes_bridge_split_driver = {
|
||||
.driver = {
|
||||
.name = "serdes-bridge-split",
|
||||
.of_match_table = of_match_ptr(serdes_bridge_split_of_match),
|
||||
},
|
||||
.probe = serdes_bridge_split_probe,
|
||||
.remove = serdes_bridge_split_remove,
|
||||
};
|
||||
|
||||
static int __init serdes_bridge_split_init(void)
|
||||
{
|
||||
return platform_driver_register(&serdes_bridge_split_driver);
|
||||
}
|
||||
device_initcall(serdes_bridge_split_init);
|
||||
|
||||
static void __exit serdes_bridge_split_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&serdes_bridge_split_driver);
|
||||
}
|
||||
module_exit(serdes_bridge_split_exit);
|
||||
|
||||
MODULE_AUTHOR("Luo Wei <lw@rock-chips.com>");
|
||||
MODULE_DESCRIPTION("display bridge interface for different serdes");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:serdes-bridge-split");
|
||||
@@ -15,7 +15,7 @@ static struct serdes_bridge *to_serdes_bridge(struct drm_bridge *bridge)
|
||||
}
|
||||
|
||||
static struct mipi_dsi_device *serdes_attach_dsi(struct serdes_bridge *serdes_bridge,
|
||||
struct device_node *dsi_node)
|
||||
struct device_node *remote_node)
|
||||
{
|
||||
struct mipi_dsi_device_info info = { "serdes", 0, NULL };
|
||||
struct serdes *serdes = serdes_bridge->parent;
|
||||
@@ -29,7 +29,7 @@ static struct mipi_dsi_device *serdes_attach_dsi(struct serdes_bridge *serdes_br
|
||||
SERDES_DBG_MFD("%s: type=%s, name=%s\n", __func__,
|
||||
info.type, serdes->chip_data->name);
|
||||
|
||||
host = of_find_mipi_dsi_host_by_node(dsi_node);
|
||||
host = of_find_mipi_dsi_host_by_node(remote_node);
|
||||
if (!host) {
|
||||
dev_err(serdes_bridge->dev, "failed to find serdes dsi host\n");
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
@@ -90,7 +90,7 @@ static int serdes_bridge_attach(struct drm_bridge *bridge,
|
||||
dev_info(serdes_bridge->dev->parent, "serdes sel_mipi %d\n",
|
||||
serdes_bridge->sel_mipi);
|
||||
/* Attach primary DSI */
|
||||
serdes_bridge->dsi = serdes_attach_dsi(serdes_bridge, serdes_bridge->dsi_node);
|
||||
serdes_bridge->dsi = serdes_attach_dsi(serdes_bridge, serdes_bridge->remote_node);
|
||||
if (IS_ERR(serdes_bridge->dsi))
|
||||
return PTR_ERR(serdes_bridge->dsi);
|
||||
}
|
||||
@@ -281,17 +281,22 @@ static int serdes_bridge_probe(struct platform_device *pdev)
|
||||
return dev_err_probe(dev, -ENODEV, "failed to get serdes regmap\n");
|
||||
|
||||
serdes_bridge->sel_mipi = of_property_read_bool(dev->parent->of_node, "sel-mipi");
|
||||
if (serdes_bridge->sel_mipi) {
|
||||
serdes_bridge->dsi_node = of_graph_get_remote_node(dev->parent->of_node, 0, -1);
|
||||
if (!serdes_bridge->dsi_node)
|
||||
return dev_err_probe(dev->parent, -ENODEV,
|
||||
"failed to get remote node for serdes dsi\n");
|
||||
SERDES_DBG_MFD("%s: sel_mipi=%d\n", __func__, serdes_bridge->sel_mipi);
|
||||
|
||||
SERDES_DBG_MFD("%s: sel_mipi=%d\n", __func__, serdes_bridge->sel_mipi);
|
||||
serdes_bridge->base_bridge.of_node = dev->parent->of_node;
|
||||
serdes_bridge->remote_node = of_graph_get_remote_node(dev->parent->of_node, 0, -1);
|
||||
if (!serdes_bridge->remote_node) {
|
||||
serdes_bridge->base_bridge.of_node = dev->of_node;
|
||||
SERDES_DBG_MFD("warning: failed to get remote node for serdes on %s\n",
|
||||
dev_name(dev->parent));
|
||||
serdes_bridge->remote_node = of_graph_get_remote_node(dev->of_node, 0, -1);
|
||||
if (!serdes_bridge->remote_node) {
|
||||
return dev_err_probe(dev, -ENODEV,
|
||||
"failed to get remote node for serdes dsi\n");
|
||||
}
|
||||
}
|
||||
|
||||
serdes_bridge->base_bridge.funcs = &serdes_bridge_funcs;
|
||||
serdes_bridge->base_bridge.of_node = dev->parent->of_node;
|
||||
serdes_bridge->base_bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_MODES;
|
||||
|
||||
if (serdes_bridge->sel_mipi) {
|
||||
|
||||
@@ -62,6 +62,10 @@ static const struct mfd_cell serdes_max96789_devs[] = {
|
||||
.name = "serdes-bridge",
|
||||
.of_compatible = "maxim,max96789-bridge",
|
||||
},
|
||||
{
|
||||
.name = "serdes-bridge-split",
|
||||
.of_compatible = "maxim,max96789-bridge-split",
|
||||
},
|
||||
};
|
||||
|
||||
static const struct mfd_cell serdes_max96752_devs[] = {
|
||||
@@ -73,6 +77,10 @@ static const struct mfd_cell serdes_max96752_devs[] = {
|
||||
.name = "serdes-panel",
|
||||
.of_compatible = "maxim,max96752-panel",
|
||||
},
|
||||
{
|
||||
.name = "serdes-panel-split",
|
||||
.of_compatible = "maxim,max96752-panel-split",
|
||||
},
|
||||
};
|
||||
|
||||
static const struct mfd_cell serdes_max96772_devs[] = {
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
#include "core.h"
|
||||
|
||||
static struct serdes *g_serdes_ser_split[MAX_NUM_SERDES_SPLIT];
|
||||
|
||||
int serdes_i2c_set_sequence(struct serdes *serdes)
|
||||
{
|
||||
struct device *dev = serdes->dev;
|
||||
@@ -53,6 +55,42 @@ int serdes_i2c_set_sequence(struct serdes *serdes)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(serdes_i2c_set_sequence);
|
||||
|
||||
static int serdes_set_i2c_address(struct serdes *serdes, u32 reg_hw, u32 reg_use, int link)
|
||||
{
|
||||
int ret = 0;
|
||||
struct i2c_client *client_split;
|
||||
struct serdes *serdes_split = serdes->g_serdes_bridge_split;
|
||||
|
||||
if (!serdes_split) {
|
||||
pr_info("%s: serdes_split is null\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
client_split = to_i2c_client(serdes->regmap->dev);
|
||||
SERDES_DBG_MFD("%s: %s-%s addr=0x%x reg_hw=0x%x, reg_use=0x%x serdes_split=0x%p\n",
|
||||
__func__, dev_name(serdes_split->dev), client_split->name,
|
||||
client_split->addr, serdes->reg_hw, serdes->reg_use, serdes_split);
|
||||
|
||||
client_split->addr = serdes->reg_hw;
|
||||
|
||||
if (serdes_split && serdes_split->chip_data->split_ops &&
|
||||
serdes_split->chip_data->split_ops->select)
|
||||
ret = serdes_split->chip_data->split_ops->select(serdes_split, link);
|
||||
|
||||
if (serdes->chip_data->split_ops && serdes->chip_data->split_ops->set_i2c_addr)
|
||||
serdes->chip_data->split_ops->set_i2c_addr(serdes, reg_use, link);
|
||||
|
||||
if (serdes_split && serdes_split->chip_data->split_ops &&
|
||||
serdes_split->chip_data->split_ops->select)
|
||||
ret = serdes_split->chip_data->split_ops->select(serdes_split, SER_SPLITTER_MODE);
|
||||
|
||||
client_split->addr = serdes->reg_use;
|
||||
|
||||
serdes_i2c_set_sequence(serdes);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void serdes_mfd_work(struct work_struct *work)
|
||||
{
|
||||
struct serdes *serdes = container_of(work, struct serdes, mfd_delay_work.work);
|
||||
@@ -149,7 +187,8 @@ static int serdes_i2c_probe(struct i2c_client *client,
|
||||
serdes->chip_data = (struct serdes_chip_data *)of_device_get_match_data(dev);
|
||||
i2c_set_clientdata(client, serdes);
|
||||
|
||||
dev_info(dev, "serdes %s probe start\n", serdes->chip_data->name);
|
||||
dev_info(dev, "serdes %s probe start, id=%d\n", serdes->chip_data->name,
|
||||
serdes->chip_data->serdes_id);
|
||||
|
||||
serdes->type = serdes->chip_data->serdes_type;
|
||||
serdes->regmap = devm_regmap_init_i2c(client, serdes->chip_data->regmap_config);
|
||||
@@ -206,6 +245,31 @@ static int serdes_i2c_probe(struct i2c_client *client,
|
||||
return ret;
|
||||
}
|
||||
|
||||
of_property_read_u32(dev->of_node, "id-serdes-bridge-split",
|
||||
&serdes->id_serdes_bridge_split);
|
||||
if ((serdes->id_serdes_bridge_split < MAX_NUM_SERDES_SPLIT) && (serdes->type == TYPE_SER)) {
|
||||
g_serdes_ser_split[serdes->id_serdes_bridge_split] = serdes;
|
||||
SERDES_DBG_MFD("%s: %s-%s g_serdes_split[%d]=0x%p\n", __func__,
|
||||
dev_name(serdes->dev), serdes->chip_data->name,
|
||||
serdes->id_serdes_bridge_split, serdes);
|
||||
}
|
||||
|
||||
of_property_read_u32(dev->of_node, "reg-hw", &serdes->reg_hw);
|
||||
of_property_read_u32(dev->of_node, "reg", &serdes->reg_use);
|
||||
of_property_read_u32(dev->of_node, "link", &serdes->link_use);
|
||||
of_property_read_u32(dev->of_node, "id-serdes-panel-split", &serdes->id_serdes_panel_split);
|
||||
if ((serdes->id_serdes_panel_split) && (serdes->type == TYPE_DES)) {
|
||||
serdes->g_serdes_bridge_split = g_serdes_ser_split[serdes->id_serdes_panel_split];
|
||||
SERDES_DBG_MFD("%s: id=%d p=0x%p\n", __func__,
|
||||
serdes->id_serdes_panel_split, serdes->g_serdes_bridge_split);
|
||||
}
|
||||
|
||||
if (serdes->reg_hw) {
|
||||
SERDES_DBG_MFD("%s: %s start change i2c address from 0x%x to 0x%x\n",
|
||||
__func__, dev->of_node->name, serdes->reg_hw, serdes->reg_use);
|
||||
serdes_set_i2c_address(serdes, serdes->reg_hw, serdes->reg_use, serdes->link_use);
|
||||
}
|
||||
|
||||
serdes->use_delay_work = of_property_read_bool(dev->of_node, "use-delay-work");
|
||||
if (serdes->use_delay_work) {
|
||||
serdes->mfd_wq = alloc_ordered_workqueue("%s",
|
||||
|
||||
277
drivers/mfd/display-serdes/serdes-panel-split.c
Normal file
277
drivers/mfd/display-serdes/serdes-panel-split.c
Normal file
@@ -0,0 +1,277 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* serdes-panel.c -- drm panel access for different serdes chips
|
||||
*
|
||||
* Copyright (c) 2023-2028 Rockchip Electronics Co. Ltd.
|
||||
*
|
||||
* Author: luowei <lw@rock-chips.com>
|
||||
*/
|
||||
|
||||
#include "core.h"
|
||||
|
||||
static inline struct serdes_panel_split *to_serdes_panel_split(struct drm_panel *panel)
|
||||
{
|
||||
return container_of(panel, struct serdes_panel_split, panel);
|
||||
}
|
||||
|
||||
static int serdes_panel_split_prepare(struct drm_panel *panel)
|
||||
{
|
||||
struct serdes_panel_split *serdes_panel_split = to_serdes_panel_split(panel);
|
||||
struct serdes *serdes = serdes_panel_split->parent;
|
||||
int ret = 0;
|
||||
|
||||
if (serdes->chip_data->panel_ops && serdes->chip_data->panel_ops->init)
|
||||
ret = serdes->chip_data->panel_ops->init(serdes);
|
||||
|
||||
if (serdes->chip_data->serdes_type == TYPE_DES)
|
||||
serdes_i2c_set_sequence(serdes);
|
||||
|
||||
if (serdes->chip_data->panel_ops && serdes->chip_data->panel_ops->prepare)
|
||||
ret = serdes->chip_data->panel_ops->prepare(serdes);
|
||||
|
||||
serdes_set_pinctrl_default(serdes);
|
||||
|
||||
SERDES_DBG_MFD("%s: %s\n", __func__, serdes->chip_data->name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int serdes_panel_split_unprepare(struct drm_panel *panel)
|
||||
{
|
||||
struct serdes_panel_split *serdes_panel_split = to_serdes_panel_split(panel);
|
||||
struct serdes *serdes = serdes_panel_split->parent;
|
||||
int ret = 0;
|
||||
|
||||
if (serdes->chip_data->panel_ops && serdes->chip_data->panel_ops->unprepare)
|
||||
ret = serdes->chip_data->panel_ops->unprepare(serdes);
|
||||
|
||||
serdes_set_pinctrl_sleep(serdes);
|
||||
|
||||
SERDES_DBG_MFD("%s: %s\n", __func__, serdes->chip_data->name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int serdes_panel_split_enable(struct drm_panel *panel)
|
||||
{
|
||||
struct serdes_panel_split *serdes_panel_split = to_serdes_panel_split(panel);
|
||||
struct serdes *serdes = serdes_panel_split->parent;
|
||||
int ret = 0;
|
||||
|
||||
if (serdes->chip_data->panel_ops && serdes->chip_data->panel_ops->enable)
|
||||
ret = serdes->chip_data->panel_ops->enable(serdes);
|
||||
|
||||
backlight_enable(serdes_panel_split->backlight);
|
||||
|
||||
SERDES_DBG_MFD("%s: %s\n", __func__, serdes->chip_data->name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int serdes_panel_split_disable(struct drm_panel *panel)
|
||||
{
|
||||
struct serdes_panel_split *serdes_panel_split = to_serdes_panel_split(panel);
|
||||
struct serdes *serdes = serdes_panel_split->parent;
|
||||
int ret = 0;
|
||||
|
||||
if (serdes->chip_data->panel_ops && serdes->chip_data->panel_ops->disable)
|
||||
ret = serdes->chip_data->panel_ops->disable(serdes);
|
||||
|
||||
backlight_disable(serdes_panel_split->backlight);
|
||||
|
||||
SERDES_DBG_MFD("%s: %s\n", __func__, serdes->chip_data->name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int serdes_panel_split_get_modes(struct drm_panel *panel,
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
struct serdes_panel_split *serdes_panel_split = to_serdes_panel_split(panel);
|
||||
struct serdes *serdes = serdes_panel_split->parent;
|
||||
struct drm_display_mode *mode;
|
||||
u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
|
||||
int ret = 1;
|
||||
|
||||
connector->display_info.width_mm = serdes_panel_split->width_mm; //323; //346;
|
||||
connector->display_info.height_mm = serdes_panel_split->height_mm; //182; //194;
|
||||
drm_display_info_set_bus_formats(&connector->display_info, &bus_format, 1);
|
||||
|
||||
mode = drm_mode_duplicate(connector->dev, &serdes_panel_split->mode);
|
||||
mode->width_mm = serdes_panel_split->width_mm; //323; //346;
|
||||
mode->height_mm = serdes_panel_split->height_mm; //182; //194;
|
||||
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
|
||||
|
||||
drm_mode_set_name(mode);
|
||||
drm_mode_probed_add(connector, mode);
|
||||
|
||||
if (serdes->chip_data->panel_ops && serdes->chip_data->panel_ops->get_modes)
|
||||
ret = serdes->chip_data->panel_ops->get_modes(serdes);
|
||||
|
||||
pr_info("%s: %s wxh=%dx%d mode clock %u kHz, flags[0x%x]\n"
|
||||
" H: %04d %04d %04d %04d\n"
|
||||
" V: %04d %04d %04d %04d\n"
|
||||
"bus_format: 0x%x\n",
|
||||
dev_name(serdes->dev),
|
||||
panel->dev->of_node->name,
|
||||
serdes_panel_split->width_mm, serdes_panel_split->height_mm,
|
||||
mode->clock, mode->flags,
|
||||
mode->hdisplay, mode->hsync_start,
|
||||
mode->hsync_end, mode->htotal,
|
||||
mode->vdisplay, mode->vsync_start,
|
||||
mode->vsync_end, mode->vtotal,
|
||||
bus_format);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct drm_panel_funcs serdes_panel_split_funcs = {
|
||||
.prepare = serdes_panel_split_prepare,
|
||||
.unprepare = serdes_panel_split_unprepare,
|
||||
.enable = serdes_panel_split_enable,
|
||||
.disable = serdes_panel_split_disable,
|
||||
.get_modes = serdes_panel_split_get_modes,
|
||||
};
|
||||
|
||||
static int serdes_panel_split_parse_dt(struct serdes_panel_split *serdes_panel_split)
|
||||
{
|
||||
struct device *dev = serdes_panel_split->dev;
|
||||
struct display_timing dt;
|
||||
struct videomode vm;
|
||||
int ret, len;
|
||||
unsigned int panel_size[2] = {320, 180};
|
||||
unsigned int link_rate_count_ssc[3] = {DP_LINK_BW_2_7, 4, 0};
|
||||
|
||||
//pr_info("%s: node=%s\n", __func__, dev->of_node->name);
|
||||
|
||||
serdes_panel_split->width_mm = panel_size[0];
|
||||
serdes_panel_split->height_mm = panel_size[1];
|
||||
|
||||
serdes_panel_split->link_rate = link_rate_count_ssc[0];
|
||||
serdes_panel_split->lane_count = link_rate_count_ssc[1];
|
||||
serdes_panel_split->ssc = link_rate_count_ssc[2];
|
||||
|
||||
if (of_find_property(dev->of_node, "panel-size", &len)) {
|
||||
len /= sizeof(unsigned int);
|
||||
ret = of_property_read_u32_array(dev->of_node, "panel-size",
|
||||
panel_size, len);
|
||||
if (!ret) {
|
||||
serdes_panel_split->width_mm = panel_size[0];
|
||||
serdes_panel_split->height_mm = panel_size[1];
|
||||
}
|
||||
}
|
||||
|
||||
if (of_find_property(dev->of_node, "rate-count-ssc", &len)) {
|
||||
len /= sizeof(unsigned int);
|
||||
ret = of_property_read_u32_array(dev->of_node, "rate-count-ssc",
|
||||
panel_size, len);
|
||||
if (!ret) {
|
||||
serdes_panel_split->link_rate = link_rate_count_ssc[0];
|
||||
serdes_panel_split->lane_count = link_rate_count_ssc[1];
|
||||
serdes_panel_split->ssc = link_rate_count_ssc[2];
|
||||
}
|
||||
}
|
||||
|
||||
dev_info(dev, "panle size %dx%d, rate=%d, cnt=%d, ssc=%d\n",
|
||||
serdes_panel_split->width_mm, serdes_panel_split->height_mm,
|
||||
serdes_panel_split->link_rate, serdes_panel_split->lane_count,
|
||||
serdes_panel_split->ssc);
|
||||
|
||||
ret = of_get_display_timing(dev->of_node, "panel-timing", &dt);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%pOF:serdes no panel-timing node found\n", dev->of_node);
|
||||
return ret;
|
||||
}
|
||||
|
||||
videomode_from_timing(&dt, &vm);
|
||||
drm_display_mode_from_videomode(&vm, &serdes_panel_split->mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int serdes_panel_split_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct serdes *serdes = dev_get_drvdata(pdev->dev.parent);
|
||||
struct device *dev = &pdev->dev;
|
||||
struct serdes_panel_split *serdes_panel_split;
|
||||
int ret;
|
||||
|
||||
serdes_panel_split = devm_kzalloc(dev, sizeof(*serdes_panel_split), GFP_KERNEL);
|
||||
if (!serdes_panel_split)
|
||||
return -ENOMEM;
|
||||
|
||||
serdes->serdes_panel_split = serdes_panel_split;
|
||||
serdes_panel_split->dev = dev;
|
||||
serdes_panel_split->parent = dev_get_drvdata(dev->parent);
|
||||
platform_set_drvdata(pdev, serdes_panel_split);
|
||||
|
||||
serdes_panel_split->regmap = dev_get_regmap(dev->parent, NULL);
|
||||
if (!serdes_panel_split->regmap)
|
||||
return dev_err_probe(dev, -ENODEV, "failed to get serdes regmap\n");
|
||||
|
||||
ret = serdes_panel_split_parse_dt(serdes_panel_split);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to parse serdes DT\n");
|
||||
|
||||
serdes_panel_split->backlight = devm_of_find_backlight(dev);
|
||||
if (IS_ERR(serdes_panel_split->backlight))
|
||||
return dev_err_probe(dev, PTR_ERR(serdes_panel_split->backlight),
|
||||
"failed to get serdes backlight\n");
|
||||
|
||||
if (serdes_panel_split->parent->chip_data->connector_type) {
|
||||
drm_panel_init(&serdes_panel_split->panel, dev, &serdes_panel_split_funcs,
|
||||
serdes_panel_split->parent->chip_data->connector_type);
|
||||
} else {
|
||||
drm_panel_init(&serdes_panel_split->panel, dev, &serdes_panel_split_funcs,
|
||||
DRM_MODE_CONNECTOR_LVDS);
|
||||
}
|
||||
drm_panel_add(&serdes_panel_split->panel);
|
||||
|
||||
dev_info(dev, "serdes %s-%s %s successful\n", dev_name(serdes->dev),
|
||||
serdes->chip_data->name, __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int serdes_panel_split_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct serdes_panel_split *serdes_panel_split = platform_get_drvdata(pdev);
|
||||
|
||||
drm_panel_remove(&serdes_panel_split->panel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id serdes_panel_split_of_match[] = {
|
||||
{ .compatible = "rohm,bu18rl82-panel-split" },
|
||||
{ .compatible = "maxim,max96752-panel-split" },
|
||||
{ .compatible = "maxim,max96772-panel-split" },
|
||||
{ .compatible = "rockchip,rkx121-panel-split" },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct platform_driver serdes_panel_split_driver = {
|
||||
.driver = {
|
||||
.name = "serdes-panel-split",
|
||||
.of_match_table = of_match_ptr(serdes_panel_split_of_match),
|
||||
},
|
||||
.probe = serdes_panel_split_probe,
|
||||
.remove = serdes_panel_split_remove,
|
||||
};
|
||||
|
||||
static int __init serdes_panel_split_init(void)
|
||||
{
|
||||
return platform_driver_register(&serdes_panel_split_driver);
|
||||
}
|
||||
device_initcall(serdes_panel_split_init);
|
||||
|
||||
static void __exit serdes_panel_split_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&serdes_panel_split_driver);
|
||||
}
|
||||
module_exit(serdes_panel_split_exit);
|
||||
|
||||
MODULE_AUTHOR("Luo Wei <lw@rock-chips.com>");
|
||||
MODULE_DESCRIPTION("display panel interface for different serdes");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:serdes-panel-split");
|
||||
@@ -141,6 +141,8 @@ static int serdes_panel_parse_dt(struct serdes_panel *serdes_panel)
|
||||
unsigned int panel_size[2] = {320, 180};
|
||||
unsigned int link_rate_count_ssc[3] = {DP_LINK_BW_2_7, 4, 0};
|
||||
|
||||
//pr_info("%s: node=%s\n", __func__, dev->of_node->name);
|
||||
|
||||
serdes_panel->width_mm = panel_size[0];
|
||||
serdes_panel->height_mm = panel_size[1];
|
||||
|
||||
@@ -233,7 +235,8 @@ static int serdes_panel_probe(struct platform_device *pdev)
|
||||
}
|
||||
drm_panel_add(&serdes_panel->panel);
|
||||
|
||||
dev_info(dev, "serdes %s serdes_panel_probe successful\n", serdes->chip_data->name);
|
||||
dev_info(dev, "serdes %s-%s serdes_panel_probe successful\n",
|
||||
dev_name(serdes->dev), serdes->chip_data->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -130,6 +130,8 @@ struct csi2_dphy_hw {
|
||||
int (*stream_off)(struct csi2_dphy *dphy, struct v4l2_subdev *sd);
|
||||
int (*ttl_mode_enable)(struct csi2_dphy_hw *hw);
|
||||
void (*ttl_mode_disable)(struct csi2_dphy_hw *hw);
|
||||
int (*quick_stream_on)(struct csi2_dphy *dphy, struct v4l2_subdev *sd);
|
||||
int (*quick_stream_off)(struct csi2_dphy *dphy, struct v4l2_subdev *sd);
|
||||
};
|
||||
|
||||
int rockchip_csi2_dphy_hw_init(void);
|
||||
|
||||
@@ -902,13 +902,90 @@ static int csi2_dphy_hw_stream_off(struct csi2_dphy *dphy,
|
||||
|
||||
write_csi2_dphy_reg(hw, CSI2PHY_REG_CTRL_LANE_ENABLE, 0x01);
|
||||
csi2_dphy_hw_do_reset(hw);
|
||||
usleep_range(500, 1000);
|
||||
|
||||
mutex_unlock(&hw->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int csi2_dphy_hw_quick_stream_on(struct csi2_dphy *dphy,
|
||||
struct v4l2_subdev *sd)
|
||||
{
|
||||
struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
|
||||
struct csi2_sensor *sensor;
|
||||
struct csi2_dphy_hw *hw = dphy->dphy_hw;
|
||||
u32 val = 0, pre_val = 0;
|
||||
|
||||
if (!sensor_sd)
|
||||
return -ENODEV;
|
||||
sensor = sd_to_sensor(dphy, sensor_sd);
|
||||
if (!sensor)
|
||||
return -ENODEV;
|
||||
|
||||
read_csi2_dphy_reg(hw, CSI2PHY_REG_CTRL_LANE_ENABLE, &pre_val);
|
||||
if (hw->lane_mode == LANE_MODE_FULL) {
|
||||
val |= (GENMASK(sensor->lanes - 1, 0) <<
|
||||
CSI2_DPHY_CTRL_DATALANE_ENABLE_OFFSET_BIT) |
|
||||
(0x1 << CSI2_DPHY_CTRL_CLKLANE_ENABLE_OFFSET_BIT);
|
||||
} else {
|
||||
if (!(pre_val & (0x1 << CSI2_DPHY_CTRL_CLKLANE_ENABLE_OFFSET_BIT)))
|
||||
val |= (0x1 << CSI2_DPHY_CTRL_CLKLANE_ENABLE_OFFSET_BIT);
|
||||
|
||||
if (dphy->phy_index % 3 == DPHY1)
|
||||
val |= (GENMASK(sensor->lanes - 1, 0) <<
|
||||
CSI2_DPHY_CTRL_DATALANE_ENABLE_OFFSET_BIT);
|
||||
|
||||
if (dphy->phy_index % 3 == DPHY2) {
|
||||
val |= (GENMASK(sensor->lanes - 1, 0) <<
|
||||
CSI2_DPHY_CTRL_DATALANE_SPLIT_LANE2_3_OFFSET_BIT);
|
||||
if (hw->drv_data->chip_id >= CHIP_ID_RK3588)
|
||||
write_csi2_dphy_reg(hw, CSI2PHY_CLK1_LANE_ENABLE, BIT(6));
|
||||
}
|
||||
}
|
||||
pre_val |= val;
|
||||
write_csi2_dphy_reg(hw, CSI2PHY_REG_CTRL_LANE_ENABLE, pre_val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int csi2_dphy_hw_quick_stream_off(struct csi2_dphy *dphy,
|
||||
struct v4l2_subdev *sd)
|
||||
{
|
||||
struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
|
||||
struct csi2_sensor *sensor;
|
||||
struct csi2_dphy_hw *hw = dphy->dphy_hw;
|
||||
u32 val = 0, pre_val = 0;
|
||||
|
||||
if (!sensor_sd)
|
||||
return -ENODEV;
|
||||
sensor = sd_to_sensor(dphy, sensor_sd);
|
||||
if (!sensor)
|
||||
return -ENODEV;
|
||||
|
||||
read_csi2_dphy_reg(hw, CSI2PHY_REG_CTRL_LANE_ENABLE, &pre_val);
|
||||
if (hw->lane_mode == LANE_MODE_FULL) {
|
||||
val |= (GENMASK(sensor->lanes - 1, 0) <<
|
||||
CSI2_DPHY_CTRL_DATALANE_ENABLE_OFFSET_BIT) |
|
||||
(0x1 << CSI2_DPHY_CTRL_CLKLANE_ENABLE_OFFSET_BIT);
|
||||
} else {
|
||||
if (!(pre_val & (0x1 << CSI2_DPHY_CTRL_CLKLANE_ENABLE_OFFSET_BIT)))
|
||||
val |= (0x1 << CSI2_DPHY_CTRL_CLKLANE_ENABLE_OFFSET_BIT);
|
||||
|
||||
if (dphy->phy_index % 3 == DPHY1)
|
||||
val |= (GENMASK(sensor->lanes - 1, 0) <<
|
||||
CSI2_DPHY_CTRL_DATALANE_ENABLE_OFFSET_BIT);
|
||||
|
||||
if (dphy->phy_index % 3 == DPHY2) {
|
||||
val |= (GENMASK(sensor->lanes - 1, 0) <<
|
||||
CSI2_DPHY_CTRL_DATALANE_SPLIT_LANE2_3_OFFSET_BIT);
|
||||
if (hw->drv_data->chip_id >= CHIP_ID_RK3588)
|
||||
write_csi2_dphy_reg(hw, CSI2PHY_CLK1_LANE_ENABLE, BIT(6));
|
||||
}
|
||||
}
|
||||
pre_val &= ~val;
|
||||
write_csi2_dphy_reg(hw, CSI2PHY_REG_CTRL_LANE_ENABLE, pre_val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int csi2_dphy_hw_ttl_mode_enable(struct csi2_dphy_hw *hw)
|
||||
{
|
||||
int ret = 0;
|
||||
@@ -1091,6 +1168,8 @@ static int rockchip_csi2_dphy_hw_probe(struct platform_device *pdev)
|
||||
}
|
||||
dphy_hw->stream_on = drv_data->stream_on;
|
||||
dphy_hw->stream_off = drv_data->stream_off;
|
||||
dphy_hw->quick_stream_on = csi2_dphy_hw_quick_stream_on;
|
||||
dphy_hw->quick_stream_off = csi2_dphy_hw_quick_stream_off;
|
||||
|
||||
if (drv_data->chip_id == CHIP_ID_RV1106) {
|
||||
dphy_hw->ttl_mode_enable = csi2_dphy_hw_ttl_mode_enable;
|
||||
|
||||
@@ -160,6 +160,7 @@ static int rockchip_csi2_dphy_attach_hw(struct csi2_dphy *dphy, int csi_idx, int
|
||||
if (csi_idx < 2) {
|
||||
dcphy_hw = dphy->samsung_phy_group[csi_idx];
|
||||
mutex_lock(&dcphy_hw->mutex);
|
||||
dcphy_hw->dphy_dev[dcphy_hw->dphy_dev_num] = dphy;
|
||||
dcphy_hw->dphy_dev_num++;
|
||||
mutex_unlock(&dcphy_hw->mutex);
|
||||
dphy->samsung_phy = dcphy_hw;
|
||||
@@ -199,6 +200,7 @@ static int rockchip_csi2_dphy_attach_hw(struct csi2_dphy *dphy, int csi_idx, int
|
||||
else
|
||||
dphy->phy_index = 5;
|
||||
}
|
||||
dphy_hw->dphy_dev[dphy_hw->dphy_dev_num] = dphy;
|
||||
dphy_hw->dphy_dev_num++;
|
||||
dphy->dphy_hw = dphy_hw;
|
||||
dphy->phy_hw[index] = (void *)dphy_hw;
|
||||
@@ -253,12 +255,56 @@ static int rockchip_csi2_dphy_attach_hw(struct csi2_dphy *dphy, int csi_idx, int
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rockchip_csi2_samsung_phy_remove_dphy_dev(struct csi2_dphy *dphy,
|
||||
struct samsung_mipi_dcphy *dcphy_hw)
|
||||
{
|
||||
int i = 0;
|
||||
bool is_find_dev = false;
|
||||
struct csi2_dphy *csi2_dphy = NULL;
|
||||
|
||||
for (i = 0; i < dcphy_hw->dphy_dev_num; i++) {
|
||||
csi2_dphy = dcphy_hw->dphy_dev[i];
|
||||
if (csi2_dphy &&
|
||||
csi2_dphy->phy_index == dphy->phy_index)
|
||||
is_find_dev = true;
|
||||
if (is_find_dev) {
|
||||
if (i < dcphy_hw->dphy_dev_num - 1)
|
||||
dcphy_hw->dphy_dev[i] = dcphy_hw->dphy_dev[i + 1];
|
||||
else
|
||||
dcphy_hw->dphy_dev[i] = NULL;
|
||||
}
|
||||
}
|
||||
if (is_find_dev)
|
||||
dcphy_hw->dphy_dev_num--;
|
||||
}
|
||||
|
||||
static void rockchip_csi2_inno_phy_remove_dphy_dev(struct csi2_dphy *dphy,
|
||||
struct csi2_dphy_hw *dphy_hw)
|
||||
{
|
||||
int i = 0;
|
||||
bool is_find_dev = false;
|
||||
struct csi2_dphy *csi2_dphy = NULL;
|
||||
|
||||
for (i = 0; i < dphy_hw->dphy_dev_num; i++) {
|
||||
csi2_dphy = dphy_hw->dphy_dev[i];
|
||||
if (csi2_dphy &&
|
||||
csi2_dphy->phy_index == dphy->phy_index)
|
||||
is_find_dev = true;
|
||||
if (is_find_dev) {
|
||||
if (i < dphy_hw->dphy_dev_num - 1)
|
||||
dphy_hw->dphy_dev[i] = dphy_hw->dphy_dev[i + 1];
|
||||
else
|
||||
dphy_hw->dphy_dev[i] = NULL;
|
||||
}
|
||||
}
|
||||
if (is_find_dev)
|
||||
dphy_hw->dphy_dev_num--;
|
||||
}
|
||||
|
||||
static int rockchip_csi2_dphy_detach_hw(struct csi2_dphy *dphy, int csi_idx, int index)
|
||||
{
|
||||
struct csi2_dphy_hw *dphy_hw = NULL;
|
||||
struct samsung_mipi_dcphy *dcphy_hw = NULL;
|
||||
struct csi2_dphy *csi2_dphy = NULL;
|
||||
int i = 0;
|
||||
|
||||
if (dphy->drv_data->chip_id == CHIP_ID_RK3568 ||
|
||||
dphy->drv_data->chip_id == CHIP_ID_RV1106) {
|
||||
@@ -269,15 +315,7 @@ static int rockchip_csi2_dphy_detach_hw(struct csi2_dphy *dphy, int csi_idx, int
|
||||
return -EINVAL;
|
||||
}
|
||||
mutex_lock(&dphy_hw->mutex);
|
||||
for (i = 0; i < dphy_hw->dphy_dev_num; i++) {
|
||||
csi2_dphy = dphy_hw->dphy_dev[i];
|
||||
if (csi2_dphy &&
|
||||
csi2_dphy->phy_index == dphy->phy_index) {
|
||||
dphy_hw->dphy_dev[i] = NULL;
|
||||
dphy_hw->dphy_dev_num--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
rockchip_csi2_inno_phy_remove_dphy_dev(dphy, dphy_hw);
|
||||
mutex_unlock(&dphy_hw->mutex);
|
||||
} else if (dphy->drv_data->chip_id == CHIP_ID_RK3588) {
|
||||
if (csi_idx < 2) {
|
||||
@@ -288,7 +326,7 @@ static int rockchip_csi2_dphy_detach_hw(struct csi2_dphy *dphy, int csi_idx, int
|
||||
return -EINVAL;
|
||||
}
|
||||
mutex_lock(&dcphy_hw->mutex);
|
||||
dcphy_hw->dphy_dev_num--;
|
||||
rockchip_csi2_samsung_phy_remove_dphy_dev(dphy, dcphy_hw);
|
||||
mutex_unlock(&dcphy_hw->mutex);
|
||||
} else {
|
||||
dphy_hw = (struct csi2_dphy_hw *)dphy->phy_hw[index];
|
||||
@@ -298,7 +336,7 @@ static int rockchip_csi2_dphy_detach_hw(struct csi2_dphy *dphy, int csi_idx, int
|
||||
return -EINVAL;
|
||||
}
|
||||
mutex_lock(&dphy_hw->mutex);
|
||||
dphy_hw->dphy_dev_num--;
|
||||
rockchip_csi2_inno_phy_remove_dphy_dev(dphy, dphy_hw);
|
||||
mutex_unlock(&dphy_hw->mutex);
|
||||
}
|
||||
} else {
|
||||
@@ -309,7 +347,7 @@ static int rockchip_csi2_dphy_detach_hw(struct csi2_dphy *dphy, int csi_idx, int
|
||||
return -EINVAL;
|
||||
}
|
||||
mutex_lock(&dphy_hw->mutex);
|
||||
dphy_hw->dphy_dev_num--;
|
||||
rockchip_csi2_inno_phy_remove_dphy_dev(dphy, dphy_hw);
|
||||
mutex_unlock(&dphy_hw->mutex);
|
||||
}
|
||||
|
||||
@@ -654,6 +692,8 @@ static long rkcif_csi2_dphy_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void
|
||||
{
|
||||
struct csi2_dphy *dphy = to_csi2_dphy(sd);
|
||||
long ret = 0;
|
||||
int i = 0;
|
||||
int on = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case RKCIF_CMD_SET_CSI_IDX:
|
||||
@@ -661,6 +701,24 @@ static long rkcif_csi2_dphy_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void
|
||||
dphy->drv_data->chip_id != CHIP_ID_RV1106)
|
||||
dphy->csi_info = *((struct rkcif_csi_info *)arg);
|
||||
break;
|
||||
case RKMODULE_SET_QUICK_STREAM:
|
||||
for (i = 0; i < dphy->csi_info.csi_num; i++) {
|
||||
if (dphy->csi_info.dphy_vendor[i] == PHY_VENDOR_INNO) {
|
||||
dphy->dphy_hw = (struct csi2_dphy_hw *)dphy->phy_hw[i];
|
||||
if (!dphy->dphy_hw ||
|
||||
!dphy->dphy_hw->quick_stream_off ||
|
||||
!dphy->dphy_hw->quick_stream_on) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
on = *(int *)arg;
|
||||
if (on)
|
||||
dphy->dphy_hw->quick_stream_on(dphy, sd);
|
||||
else
|
||||
dphy->dphy_hw->quick_stream_off(dphy, sd);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = -ENOIOCTLCMD;
|
||||
break;
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
_IOW('V', BASE_VIDIOC_PRIVATE + 7, struct rkcif_csi_info)
|
||||
|
||||
#define RKCIF_CMD_SET_QUICK_STREAM \
|
||||
_IOW('V', BASE_VIDIOC_PRIVATE + 8, int)
|
||||
_IOWR('V', BASE_VIDIOC_PRIVATE + 8, struct rkcif_quick_stream_param)
|
||||
|
||||
/* cif memory mode
|
||||
* 0: raw12/raw10/raw8 8bit memory compact
|
||||
@@ -82,4 +82,10 @@ struct rkcif_csi_info {
|
||||
int dphy_vendor[RKCIF_MAX_CSI_NUM];
|
||||
};
|
||||
|
||||
struct rkcif_quick_stream_param {
|
||||
int on;
|
||||
__u32 frame_num;
|
||||
int resume_mode;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user