Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 42 additions & 19 deletions drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.c
Original file line number Diff line number Diff line change
Expand Up @@ -728,7 +728,6 @@ static void ipu7_isys_dphy_config(struct ipu7_isys *isys, u8 id, u8 lanes,
static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes,
bool aggregation, u64 mbps)
{
u8 trios = 2;
u16 coarse_target;
u16 deass_thresh;
u16 delay_thresh;
Expand All @@ -746,18 +745,18 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes,
val = 0x155;

if (is_ipu7(isys->adev->isp->hw_ver))
trios = 3;
lanes = 3;

dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_7, val, 0, 9);
dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_7, 104, 0, 7);
dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_8, 16, 0, 7);

reg = CORE_DIG_CLANE_0_RW_LP_0;
for (i = 0; i < trios; i++)
for (i = 0; i < lanes; i++)
dwc_phy_write_mask(isys, id, reg + (i * 0x400), 6, 8, 11);

val = (mbps > 900U) ? 1U : 0U;
for (i = 0; i < trios; i++) {
for (i = 0; i < lanes; i++) {
reg = CORE_DIG_CLANE_0_RW_HS_RX_0;
dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 0, 0);
dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 1, 1);
Expand All @@ -782,7 +781,7 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes,
else
coarse_target = 56;

for (i = 0; i < trios; i++) {
for (i = 0; i < lanes; i++) {
reg = CORE_DIG_CLANE_0_RW_HS_RX_2 + i * 0x400;
dwc_phy_write_mask(isys, id, reg, coarse_target, 0, 15);
}
Expand All @@ -794,7 +793,7 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes,
dwc_phy_write_mask(isys, id,
CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2, 1, 0, 0);

if (!is_ipu7p5(isys->adev->isp->hw_ver) && lanes == 4) {
if (!is_ipu7p5(isys->adev->isp->hw_ver) && lanes > 2) {
dwc_phy_write_mask(isys, id,
CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_2,
1, 0, 0);
Expand All @@ -803,7 +802,7 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes,
0, 0, 0);
}

for (i = 0; i < trios; i++) {
for (i = 0; i < lanes; i++) {
reg = CORE_DIG_RW_TRIO0_0 + i * 0x400;
dwc_phy_write_mask(isys, id, reg, 1, 6, 8);
dwc_phy_write_mask(isys, id, reg, 1, 3, 5);
Expand All @@ -815,7 +814,7 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes,
deass_thresh++;

reg = CORE_DIG_RW_TRIO0_2;
for (i = 0; i < trios; i++)
for (i = 0; i < lanes; i++)
dwc_phy_write_mask(isys, id, reg + 0x400 * i,
deass_thresh, 0, 7);

Expand All @@ -825,7 +824,7 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes,
delay_thresh = 1;

reg = CORE_DIG_RW_TRIO0_1;
for (i = 0; i < trios; i++)
for (i = 0; i < lanes; i++)
dwc_phy_write_mask(isys, id, reg + 0x400 * i,
delay_thresh, 0, 15);

Expand All @@ -837,21 +836,21 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes,
reset_thresh = 1;

reg = CORE_DIG_RW_TRIO0_0;
for (i = 0; i < trios; i++)
for (i = 0; i < lanes; i++)
dwc_phy_write_mask(isys, id, reg + 0x400 * i,
reset_thresh, 9, 11);

/* Tuning ITMINRX to 2 for CPHY */
reg = CORE_DIG_CLANE_0_RW_LP_0;
for (i = 0; i < trios; i++)
for (i = 0; i < lanes; i++)
dwc_phy_write_mask(isys, id, reg + 0x400 * i, 2, 12, 15);

reg = CORE_DIG_CLANE_0_RW_LP_2;
for (i = 0; i < trios; i++)
for (i = 0; i < lanes; i++)
dwc_phy_write_mask(isys, id, reg + 0x400 * i, 0, 0, 0);

reg = CORE_DIG_CLANE_0_RW_HS_RX_0;
for (i = 0; i < trios; i++)
for (i = 0; i < lanes; i++)
dwc_phy_write_mask(isys, id, reg + 0x400 * i, 12, 2, 6);

for (i = 0; i < ARRAY_SIZE(table7); i++) {
Expand All @@ -873,6 +872,27 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes,
reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_7 + 0x400 * i;
dwc_phy_write_mask(isys, id, reg, cap_prog, 10, 12);
}

if (aggregation) {
dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_0, 1, 1, 1);

/*
* C-PHY has no clock lane, so unlike D-PHY no AFE lane is the
* shared clock that has to follow port A.
*/
for (i = 0; i < (lanes + 1); i++) {
reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_15 + 0x400 * i;
dwc_phy_write_mask(isys, id, reg, 3, 3, 4);
}
}

/* Only port A runs rext calibration; other ports reuse its result. */
if (isys->phy_rext_cal && id) {
dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_8,
isys->phy_rext_cal, 0, 3);
dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7,
1, 11, 11);
}
}

static int ipu7_isys_phy_config(struct ipu7_isys *isys, u8 id, u8 lanes,
Expand Down Expand Up @@ -962,21 +982,23 @@ static int ipu7_isys_phy_config(struct ipu7_isys *isys, u8 id, u8 lanes,
int ipu7_isys_csi_phy_powerup(struct ipu7_isys_csi2 *csi2)
{
struct ipu7_isys *isys = csi2->isys;
u32 lanes = csi2->nlanes;
u32 total_lanes = csi2->nlanes;
u32 active_lanes = total_lanes;
bool aggregation = false;
u32 id = csi2->port;
int ret;

/* lanes remapping for aggregation (port AB) mode */
if (!is_ipu7(isys->adev->isp->hw_ver) && lanes > 2 && id == PORT_A) {
if (!is_ipu7(isys->adev->isp->hw_ver) && active_lanes > 2 &&
id == PORT_A) {
aggregation = true;
lanes = 2;
active_lanes = 2;
}

ipu7_isys_csi_phy_reset(isys, id);
gpreg_write(isys, id, PHY_CLK_LANE_CONTROL, 0x1);
gpreg_write(isys, id, PHY_CLK_LANE_FORCE_CONTROL, 0x2);
gpreg_write(isys, id, PHY_LANE_CONTROL_EN, (1U << lanes) - 1U);
gpreg_write(isys, id, PHY_LANE_CONTROL_EN, (1U << active_lanes) - 1U);
gpreg_write(isys, id, PHY_LANE_FORCE_CONTROL, 0xf);
gpreg_write(isys, id, PHY_MODE, csi2->phy_mode);

Expand All @@ -993,7 +1015,7 @@ int ipu7_isys_csi_phy_powerup(struct ipu7_isys_csi2 *csi2)
ipu7_isys_csi_ctrl_cfg(csi2);
ipu7_isys_csi_ctrl_dids_config(csi2, id);

ret = ipu7_isys_phy_config(isys, id, lanes, aggregation);
ret = ipu7_isys_phy_config(isys, id, active_lanes, aggregation);
if (ret < 0)
return ret;

Expand All @@ -1012,7 +1034,8 @@ int ipu7_isys_csi_phy_powerup(struct ipu7_isys_csi2 *csi2)

/* config PORT_B if aggregation mode */
if (aggregation) {
ret = ipu7_isys_phy_config(isys, PORT_B, 2, aggregation);
ret = ipu7_isys_phy_config(isys, PORT_B,
total_lanes - active_lanes, aggregation);
if (ret < 0)
return ret;

Expand Down
7 changes: 6 additions & 1 deletion drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,11 @@ static void ipu7_isys_csi2_disable_stream(struct ipu7_isys_csi2 *csi2)

ipu7_isys_csi_phy_powerdown(csi2);

if (csi2->port == 0U && csi2->nlanes > 2U &&
!is_ipu7(isys->adev->isp->hw_ver))
writel(0x0, isys_base + IS_IO_GPREGS_BASE +
CSI_PORTAB_AGGREGATION);

writel(0x4, isys_base + IS_IO_GPREGS_BASE + CLK_DIV_FACTOR_APB_CLK);
csi2_irq_disable(csi2);
}
Expand All @@ -195,7 +200,7 @@ static int ipu7_isys_csi2_enable_stream(struct ipu7_isys_csi2 *csi2)
dev_dbg(dev, "port %u CLK_GATE = 0x%04x DIV_FACTOR_APB_CLK=0x%04x\n",
port, readl(isys_base + offset + CSI_PORT_CLK_GATE),
readl(isys_base + offset + CLK_DIV_FACTOR_APB_CLK));
if (port == 0U && nlanes == 4U && !is_ipu7(isys->adev->isp->hw_ver)) {
if (port == 0U && nlanes > 2U && !is_ipu7(isys->adev->isp->hw_ver)) {
dev_dbg(dev, "CSI port %u in aggregation mode\n", port);
writel(0x1, isys_base + offset + CSI_PORTAB_AGGREGATION);
}
Expand Down
1 change: 0 additions & 1 deletion drivers/media/pci/intel/ipu7/ipu7-isys-video.c
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,6 @@ static void __ipu_isys_vidioc_try_fmt_vid_cap(struct ipu7_isys_video *av,
&f->fmt.pix.bytesperline, &f->fmt.pix.sizeimage);

f->fmt.pix.field = V4L2_FIELD_NONE;
f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW;
f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
f->fmt.pix.quantization = V4L2_QUANTIZATION_DEFAULT;
f->fmt.pix.xfer_func = V4L2_XFER_FUNC_DEFAULT;
Expand Down
73 changes: 61 additions & 12 deletions drivers/media/pci/intel/ipu7/ipu7-isys.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,23 +59,64 @@ isys_complete_ext_device_registration(struct ipu7_isys *isys,
struct ipu7_isys_csi2_config *csi2)
{
struct device *dev = &isys->adev->auxdev.dev;
unsigned int i;
int source_pad;
int ret;

v4l2_set_subdev_hostdata(sd, csi2);

for (i = 0; i < sd->entity.num_pads; i++) {
if (sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE)
break;
}
if (csi2->ep) {
struct v4l2_fwnode_endpoint vep_source = {
.bus_type = V4L2_MBUS_UNKNOWN
};
struct fwnode_handle *ep_source;

if (i == sd->entity.num_pads) {
dev_warn(dev, "no source pad in external entity\n");
ret = -ENOENT;
goto skip_unregister_subdev;
ep_source = fwnode_graph_get_remote_endpoint(csi2->ep);
if (!ep_source) {
dev_warn(dev, "no remote endpoint for subdev\n");
ret = -ENOENT;
goto skip_unregister_subdev;
}

source_pad = media_entity_get_fwnode_pad(&sd->entity, ep_source,
MEDIA_PAD_FL_SOURCE);

ret = v4l2_fwnode_endpoint_parse(ep_source, &vep_source);
fwnode_handle_put(ep_source);

if (source_pad < 0) {
dev_warn(
dev,
"error in no acquire source pad in external entity\n");
ret = -ENOENT;
goto skip_unregister_subdev;
}

dev_dbg(&isys->adev->auxdev.dev, "%s: CSI2 ep %pfw\n", __func__,
csi2->ep);
dev_dbg(&isys->adev->auxdev.dev,
"%s: source pad %d for subdev %s\n", __func__,
source_pad, sd->name);

if (ret)
goto skip_unregister_subdev;

csi2->nlanes = vep_source.bus.mipi_csi2.num_data_lanes;
csi2->bus_type = vep_source.bus_type;
} else {
for (source_pad = 0; source_pad < sd->entity.num_pads;
source_pad++) {
if (sd->entity.pads[source_pad].flags &
MEDIA_PAD_FL_SOURCE)
break;
}
if (source_pad == sd->entity.num_pads) {
dev_warn(dev, "no source pad in external entity\n");
ret = -ENOENT;
goto skip_unregister_subdev;
}
}

ret = media_create_pad_link(&sd->entity, i,
ret = media_create_pad_link(&sd->entity, source_pad,
&isys->csi2[csi2->port].asd.sd.entity,
0, MEDIA_LNK_FL_ENABLED |
MEDIA_LNK_FL_IMMUTABLE);
Expand Down Expand Up @@ -300,9 +341,18 @@ static int isys_notifier_complete(struct v4l2_async_notifier *notifier)
return v4l2_device_register_subdev_nodes(&isys->v4l2_dev);
}

static void isys_notifier_destroy(struct v4l2_async_connection *asc)
{
struct sensor_async_sd *s_asd =
container_of(asc, struct sensor_async_sd, asc);

fwnode_handle_put(s_asd->csi2.ep);
}

static const struct v4l2_async_notifier_operations isys_async_ops = {
.bound = isys_notifier_bound,
.complete = isys_notifier_complete,
.destroy = isys_notifier_destroy,
};

static int isys_notifier_init(struct ipu7_isys *isys)
Expand Down Expand Up @@ -351,8 +401,7 @@ static int isys_notifier_init(struct ipu7_isys *isys)
s_asd->csi2.port = vep.base.port;
s_asd->csi2.nlanes = vep.bus.mipi_csi2.num_data_lanes;
s_asd->csi2.bus_type = vep.bus_type;

fwnode_handle_put(ep);
s_asd->csi2.ep = ep;

continue;

Expand Down
1 change: 1 addition & 0 deletions drivers/media/pci/intel/ipu7/ipu7-isys.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ struct ipu7_isys_csi2_config {
unsigned int nlanes;
unsigned int port;
enum v4l2_mbus_type bus_type;
struct fwnode_handle *ep;
};

struct ipu7_isys_subdev_i2c_info {
Expand Down
6 changes: 3 additions & 3 deletions drivers/media/pci/intel/ipu7/ipu7.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,13 @@ struct ipu7_device {

void __iomem *base;
void __iomem *pb_base;
#ifdef CONFIG_DEBUG_FS
struct dentry *ipu7_dir;
#endif
u8 hw_ver;
bool ipc_reinit;
bool secure_mode;
bool ipu7_bus_ready_to_probe;
#ifdef CONFIG_DEBUG_FS
struct dentry *ipu7_dir;
#endif
};

#define IPU_DMA_MASK 39
Expand Down
Loading
Loading