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:
Tao Huang
2023-12-05 19:24:47 +08:00
26 changed files with 1557 additions and 164 deletions

View File

@@ -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].

View 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;
}
};

View File

@@ -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');

View File

@@ -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

View File

@@ -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) {

View File

@@ -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)
};

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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

View File

@@ -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;
};

View File

@@ -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,
};

View File

@@ -136,4 +136,11 @@
/* 7074h */
#define MAX_LINK_RATE GENMASK(7, 0)
enum link_mode {
DUAL_LINK,
LINKA,
LINKB,
SPLITTER_MODE,
};
#endif

View File

@@ -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,

View File

@@ -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

View File

@@ -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,
};

View File

@@ -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)

View 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");

View File

@@ -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) {

View File

@@ -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[] = {

View File

@@ -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",

View 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");

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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