diff options
Diffstat (limited to 'drivers/media/platform/renesas')
45 files changed, 1553 insertions, 1022 deletions
diff --git a/drivers/media/platform/renesas/Kconfig b/drivers/media/platform/renesas/Kconfig index ed788e991f74..c7fc718a30a5 100644 --- a/drivers/media/platform/renesas/Kconfig +++ b/drivers/media/platform/renesas/Kconfig @@ -14,6 +14,22 @@ config VIDEO_RENESAS_CEU help This is a v4l2 driver for the Renesas CEU Interface +config VIDEO_RCAR_CSI2 + tristate "R-Car MIPI CSI-2 Receiver" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV && OF + depends on ARCH_RENESAS || COMPILE_TEST + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select RESET_CONTROLLER + select V4L2_FWNODE + help + Support for Renesas R-Car MIPI CSI-2 receiver. + Supports R-Car Gen3 and RZ/G2 SoCs. + + To compile this driver as a module, choose M here: the + module will be called rcar-csi2. + config VIDEO_RCAR_ISP tristate "R-Car Image Signal Processor (ISP)" depends on V4L_PLATFORM_DRIVERS diff --git a/drivers/media/platform/renesas/Makefile b/drivers/media/platform/renesas/Makefile index 55854e868887..50774a20330c 100644 --- a/drivers/media/platform/renesas/Makefile +++ b/drivers/media/platform/renesas/Makefile @@ -7,6 +7,7 @@ obj-y += rcar-vin/ obj-y += rzg2l-cru/ obj-y += vsp1/ +obj-$(CONFIG_VIDEO_RCAR_CSI2) += rcar-csi2.o obj-$(CONFIG_VIDEO_RCAR_DRIF) += rcar_drif.o obj-$(CONFIG_VIDEO_RCAR_ISP) += rcar-isp.o obj-$(CONFIG_VIDEO_RENESAS_CEU) += renesas-ceu.o diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c b/drivers/media/platform/renesas/rcar-csi2.c index 582d5e35db0e..0a53dd47d7bf 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c +++ b/drivers/media/platform/renesas/rcar-csi2.c @@ -135,13 +135,23 @@ struct rcar_csi2; /* V4H BASE registers */ #define V4H_N_LANES_REG 0x0004 #define V4H_CSI2_RESETN_REG 0x0008 + #define V4H_PHY_MODE_REG 0x001c +#define V4H_PHY_MODE_DPHY 0 +#define V4H_PHY_MODE_CPHY 1 + #define V4H_PHY_SHUTDOWNZ_REG 0x0040 #define V4H_DPHY_RSTZ_REG 0x0044 #define V4H_FLDC_REG 0x0804 #define V4H_FLDD_REG 0x0808 #define V4H_IDIC_REG 0x0810 + #define V4H_PHY_EN_REG 0x2000 +#define V4H_PHY_EN_ENABLE_3 BIT(7) +#define V4H_PHY_EN_ENABLE_2 BIT(6) +#define V4H_PHY_EN_ENABLE_1 BIT(5) +#define V4H_PHY_EN_ENABLE_0 BIT(4) +#define V4H_PHY_EN_ENABLE_CLK BIT(0) #define V4H_ST_PHYST_REG 0x2814 #define V4H_ST_PHYST_ST_PHY_READY BIT(31) @@ -173,17 +183,19 @@ struct rcar_csi2; #define V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(n) (0x23840 + ((n) * 2)) /* n = 0 - 11 */ #define V4H_CORE_DIG_RW_COMMON_REG(n) (0x23880 + ((n) * 2)) /* n = 0 - 15 */ #define V4H_CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_REG(n) (0x239e0 + ((n) * 2)) /* n = 0 - 3 */ -#define V4H_CORE_DIG_CLANE_1_RW_CFG_0_REG 0x2a400 #define V4H_CORE_DIG_CLANE_1_RW_HS_TX_6_REG 0x2a60c /* V4H C-PHY */ #define V4H_CORE_DIG_RW_TRIO0_REG(n) (0x22100 + ((n) * 2)) /* n = 0 - 3 */ #define V4H_CORE_DIG_RW_TRIO1_REG(n) (0x22500 + ((n) * 2)) /* n = 0 - 3 */ #define V4H_CORE_DIG_RW_TRIO2_REG(n) (0x22900 + ((n) * 2)) /* n = 0 - 3 */ +#define V4H_CORE_DIG_CLANE_0_RW_CFG_0_REG 0x2a000 #define V4H_CORE_DIG_CLANE_0_RW_LP_0_REG 0x2a080 #define V4H_CORE_DIG_CLANE_0_RW_HS_RX_REG(n) (0x2a100 + ((n) * 2)) /* n = 0 - 6 */ +#define V4H_CORE_DIG_CLANE_1_RW_CFG_0_REG 0x2a400 #define V4H_CORE_DIG_CLANE_1_RW_LP_0_REG 0x2a480 #define V4H_CORE_DIG_CLANE_1_RW_HS_RX_REG(n) (0x2a500 + ((n) * 2)) /* n = 0 - 6 */ +#define V4H_CORE_DIG_CLANE_2_RW_CFG_0_REG 0x2a800 #define V4H_CORE_DIG_CLANE_2_RW_LP_0_REG 0x2a880 #define V4H_CORE_DIG_CLANE_2_RW_HS_RX_REG(n) (0x2a900 + ((n) * 2)) /* n = 0 - 6 */ @@ -237,17 +249,37 @@ static const struct rcsi2_cphy_setting cphy_setting_table_r8a779g0[] = { { /* sentinel */ }, }; +/* V4M registers */ +#define V4M_OVR1_REG 0x0848 +#define V4M_OVR1_FORCERXMODE_3 BIT(12) +#define V4M_OVR1_FORCERXMODE_2 BIT(11) +#define V4M_OVR1_FORCERXMODE_1 BIT(10) +#define V4M_OVR1_FORCERXMODE_0 BIT(9) + +#define V4M_FRXM_REG 0x2004 +#define V4M_FRXM_FORCERXMODE_3 BIT(3) +#define V4M_FRXM_FORCERXMODE_2 BIT(2) +#define V4M_FRXM_FORCERXMODE_1 BIT(1) +#define V4M_FRXM_FORCERXMODE_0 BIT(0) + +#define V4M_PHYPLL_REG 0x02050 +#define V4M_CSI0CLKFCPR_REG 0x02054 +#define V4M_PHTW_REG 0x02060 +#define V4M_PHTR_REG 0x02064 +#define V4M_PHTC_REG 0x02068 + struct phtw_value { - u16 data; - u16 code; + u8 data; + u8 code; }; -struct rcsi2_mbps_reg { +struct rcsi2_mbps_info { u16 mbps; - u16 reg; + u8 reg; + u16 osc_freq; /* V4M */ }; -static const struct rcsi2_mbps_reg phtw_mbps_v3u[] = { +static const struct rcsi2_mbps_info phtw_mbps_v3u[] = { { .mbps = 1500, .reg = 0xcc }, { .mbps = 1550, .reg = 0x1d }, { .mbps = 1600, .reg = 0x27 }, @@ -272,7 +304,7 @@ static const struct rcsi2_mbps_reg phtw_mbps_v3u[] = { { /* sentinel */ }, }; -static const struct rcsi2_mbps_reg phtw_mbps_h3_v3h_m3n[] = { +static const struct rcsi2_mbps_info phtw_mbps_h3_v3h_m3n[] = { { .mbps = 80, .reg = 0x86 }, { .mbps = 90, .reg = 0x86 }, { .mbps = 100, .reg = 0x87 }, @@ -292,7 +324,7 @@ static const struct rcsi2_mbps_reg phtw_mbps_h3_v3h_m3n[] = { { /* sentinel */ }, }; -static const struct rcsi2_mbps_reg phtw_mbps_v3m_e3[] = { +static const struct rcsi2_mbps_info phtw_mbps_v3m_e3[] = { { .mbps = 80, .reg = 0x00 }, { .mbps = 90, .reg = 0x20 }, { .mbps = 100, .reg = 0x40 }, @@ -336,7 +368,7 @@ static const struct rcsi2_mbps_reg phtw_mbps_v3m_e3[] = { #define PHYPLL_REG 0x68 #define PHYPLL_HSFREQRANGE(n) ((n) << 16) -static const struct rcsi2_mbps_reg hsfreqrange_v3u[] = { +static const struct rcsi2_mbps_info hsfreqrange_v3u[] = { { .mbps = 80, .reg = 0x00 }, { .mbps = 90, .reg = 0x10 }, { .mbps = 100, .reg = 0x20 }, @@ -402,7 +434,7 @@ static const struct rcsi2_mbps_reg hsfreqrange_v3u[] = { { /* sentinel */ }, }; -static const struct rcsi2_mbps_reg hsfreqrange_h3_v3h_m3n[] = { +static const struct rcsi2_mbps_info hsfreqrange_h3_v3h_m3n[] = { { .mbps = 80, .reg = 0x00 }, { .mbps = 90, .reg = 0x10 }, { .mbps = 100, .reg = 0x20 }, @@ -449,7 +481,7 @@ static const struct rcsi2_mbps_reg hsfreqrange_h3_v3h_m3n[] = { { /* sentinel */ }, }; -static const struct rcsi2_mbps_reg hsfreqrange_m3w[] = { +static const struct rcsi2_mbps_info hsfreqrange_m3w[] = { { .mbps = 80, .reg = 0x00 }, { .mbps = 90, .reg = 0x10 }, { .mbps = 100, .reg = 0x20 }, @@ -496,6 +528,73 @@ static const struct rcsi2_mbps_reg hsfreqrange_m3w[] = { { /* sentinel */ }, }; +static const struct rcsi2_mbps_info hsfreqrange_v4m[] = { + { .mbps = 80, .reg = 0x00, .osc_freq = 0x01a9 }, + { .mbps = 90, .reg = 0x10, .osc_freq = 0x01a9 }, + { .mbps = 100, .reg = 0x20, .osc_freq = 0x01a9 }, + { .mbps = 110, .reg = 0x30, .osc_freq = 0x01a9 }, + { .mbps = 120, .reg = 0x01, .osc_freq = 0x01a9 }, + { .mbps = 130, .reg = 0x11, .osc_freq = 0x01a9 }, + { .mbps = 140, .reg = 0x21, .osc_freq = 0x01a9 }, + { .mbps = 150, .reg = 0x31, .osc_freq = 0x01a9 }, + { .mbps = 160, .reg = 0x02, .osc_freq = 0x01a9 }, + { .mbps = 170, .reg = 0x12, .osc_freq = 0x01a9 }, + { .mbps = 180, .reg = 0x22, .osc_freq = 0x01a9 }, + { .mbps = 190, .reg = 0x32, .osc_freq = 0x01a9 }, + { .mbps = 205, .reg = 0x03, .osc_freq = 0x01a9 }, + { .mbps = 220, .reg = 0x13, .osc_freq = 0x01a9 }, + { .mbps = 235, .reg = 0x23, .osc_freq = 0x01a9 }, + { .mbps = 250, .reg = 0x33, .osc_freq = 0x01a9 }, + { .mbps = 275, .reg = 0x04, .osc_freq = 0x01a9 }, + { .mbps = 300, .reg = 0x14, .osc_freq = 0x01a9 }, + { .mbps = 325, .reg = 0x25, .osc_freq = 0x01a9 }, + { .mbps = 350, .reg = 0x35, .osc_freq = 0x01a9 }, + { .mbps = 400, .reg = 0x05, .osc_freq = 0x01a9 }, + { .mbps = 450, .reg = 0x16, .osc_freq = 0x01a9 }, + { .mbps = 500, .reg = 0x26, .osc_freq = 0x01a9 }, + { .mbps = 550, .reg = 0x37, .osc_freq = 0x01a9 }, + { .mbps = 600, .reg = 0x07, .osc_freq = 0x01a9 }, + { .mbps = 650, .reg = 0x18, .osc_freq = 0x01a9 }, + { .mbps = 700, .reg = 0x28, .osc_freq = 0x01a9 }, + { .mbps = 750, .reg = 0x39, .osc_freq = 0x01a9 }, + { .mbps = 800, .reg = 0x09, .osc_freq = 0x01a9 }, + { .mbps = 850, .reg = 0x19, .osc_freq = 0x01a9 }, + { .mbps = 900, .reg = 0x29, .osc_freq = 0x01a9 }, + { .mbps = 950, .reg = 0x3a, .osc_freq = 0x01a9 }, + { .mbps = 1000, .reg = 0x0a, .osc_freq = 0x01a9 }, + { .mbps = 1050, .reg = 0x1a, .osc_freq = 0x01a9 }, + { .mbps = 1100, .reg = 0x2a, .osc_freq = 0x01a9 }, + { .mbps = 1150, .reg = 0x3b, .osc_freq = 0x01a9 }, + { .mbps = 1200, .reg = 0x0b, .osc_freq = 0x01a9 }, + { .mbps = 1250, .reg = 0x1b, .osc_freq = 0x01a9 }, + { .mbps = 1300, .reg = 0x2b, .osc_freq = 0x01a9 }, + { .mbps = 1350, .reg = 0x3c, .osc_freq = 0x01a9 }, + { .mbps = 1400, .reg = 0x0c, .osc_freq = 0x01a9 }, + { .mbps = 1450, .reg = 0x1c, .osc_freq = 0x01a9 }, + { .mbps = 1500, .reg = 0x2c, .osc_freq = 0x01a9 }, + { .mbps = 1550, .reg = 0x3d, .osc_freq = 0x0108 }, + { .mbps = 1600, .reg = 0x0d, .osc_freq = 0x0110 }, + { .mbps = 1650, .reg = 0x1d, .osc_freq = 0x0119 }, + { .mbps = 1700, .reg = 0x2e, .osc_freq = 0x0121 }, + { .mbps = 1750, .reg = 0x3e, .osc_freq = 0x012a }, + { .mbps = 1800, .reg = 0x0e, .osc_freq = 0x0132 }, + { .mbps = 1850, .reg = 0x1e, .osc_freq = 0x013b }, + { .mbps = 1900, .reg = 0x2f, .osc_freq = 0x0143 }, + { .mbps = 1950, .reg = 0x3f, .osc_freq = 0x014c }, + { .mbps = 2000, .reg = 0x0f, .osc_freq = 0x0154 }, + { .mbps = 2050, .reg = 0x40, .osc_freq = 0x015d }, + { .mbps = 2100, .reg = 0x41, .osc_freq = 0x0165 }, + { .mbps = 2150, .reg = 0x42, .osc_freq = 0x016e }, + { .mbps = 2200, .reg = 0x43, .osc_freq = 0x0176 }, + { .mbps = 2250, .reg = 0x44, .osc_freq = 0x017f }, + { .mbps = 2300, .reg = 0x45, .osc_freq = 0x0187 }, + { .mbps = 2350, .reg = 0x46, .osc_freq = 0x0190 }, + { .mbps = 2400, .reg = 0x47, .osc_freq = 0x0198 }, + { .mbps = 2450, .reg = 0x48, .osc_freq = 0x01a1 }, + { .mbps = 2500, .reg = 0x49, .osc_freq = 0x01a9 }, + { /* sentinel */ }, +}; + /* PHY ESC Error Monitor */ #define PHEERM_REG 0x74 @@ -575,6 +674,21 @@ static const struct rcar_csi2_format *rcsi2_code_to_fmt(unsigned int code) return NULL; } +struct rcsi2_cphy_line_order { + enum v4l2_mbus_csi2_cphy_line_orders_type order; + u16 cfg; + u16 ctrl29; +}; + +static const struct rcsi2_cphy_line_order rcsi2_cphy_line_orders[] = { + { .order = V4L2_MBUS_CSI2_CPHY_LINE_ORDER_ABC, .cfg = 0x0, .ctrl29 = 0x0 }, + { .order = V4L2_MBUS_CSI2_CPHY_LINE_ORDER_ACB, .cfg = 0xa, .ctrl29 = 0x1 }, + { .order = V4L2_MBUS_CSI2_CPHY_LINE_ORDER_BAC, .cfg = 0xc, .ctrl29 = 0x1 }, + { .order = V4L2_MBUS_CSI2_CPHY_LINE_ORDER_BCA, .cfg = 0x5, .ctrl29 = 0x0 }, + { .order = V4L2_MBUS_CSI2_CPHY_LINE_ORDER_CAB, .cfg = 0x3, .ctrl29 = 0x0 }, + { .order = V4L2_MBUS_CSI2_CPHY_LINE_ORDER_CBA, .cfg = 0x9, .ctrl29 = 0x1 } +}; + enum rcar_csi2_pads { RCAR_CSI2_SINK, RCAR_CSI2_SOURCE_VC0, @@ -584,12 +698,19 @@ enum rcar_csi2_pads { NR_OF_RCAR_CSI2_PAD, }; +struct rcsi2_register_layout { + unsigned int phtw; + unsigned int phypll; +}; + struct rcar_csi2_info { + const struct rcsi2_register_layout *regs; int (*init_phtw)(struct rcar_csi2 *priv, unsigned int mbps); int (*phy_post_init)(struct rcar_csi2 *priv); - int (*start_receiver)(struct rcar_csi2 *priv); + int (*start_receiver)(struct rcar_csi2 *priv, + struct v4l2_subdev_state *state); void (*enter_standby)(struct rcar_csi2 *priv); - const struct rcsi2_mbps_reg *hsfreqrange; + const struct rcsi2_mbps_info *hsfreqrange; unsigned int csi0clkfreqrange; unsigned int num_channels; bool clear_ulps; @@ -613,13 +734,12 @@ struct rcar_csi2 { int channel_vc[4]; - struct mutex lock; /* Protects mf and stream_count. */ - struct v4l2_mbus_framefmt mf; int stream_count; bool cphy; unsigned short lanes; unsigned char lane_swap[4]; + enum v4l2_mbus_csi2_cphy_line_orders_type line_orders[3]; }; static inline struct rcar_csi2 *sd_to_csi2(struct v4l2_subdev *sd) @@ -632,6 +752,16 @@ static inline struct rcar_csi2 *notifier_to_csi2(struct v4l2_async_notifier *n) return container_of(n, struct rcar_csi2, notifier); } +static unsigned int rcsi2_num_pads(const struct rcar_csi2 *priv) +{ + /* Used together with R-Car ISP: one sink and one source pad. */ + if (priv->info->use_isp) + return 2; + + /* Used together with R-Car VIN: one sink and four source pads. */ + return 5; +} + static u32 rcsi2_read(struct rcar_csi2 *priv, unsigned int reg) { return ioread32(priv->base + reg); @@ -642,11 +772,88 @@ static void rcsi2_write(struct rcar_csi2 *priv, unsigned int reg, u32 data) iowrite32(data, priv->base + reg); } +static u16 rcsi2_read16(struct rcar_csi2 *priv, unsigned int reg) +{ + return ioread16(priv->base + reg); +} + static void rcsi2_write16(struct rcar_csi2 *priv, unsigned int reg, u16 data) { iowrite16(data, priv->base + reg); } +static void rcsi2_modify16(struct rcar_csi2 *priv, unsigned int reg, u16 data, u16 mask) +{ + u16 val; + + val = rcsi2_read16(priv, reg) & ~mask; + rcsi2_write16(priv, reg, val | data); +} + +static int rcsi2_phtw_write(struct rcar_csi2 *priv, u8 data, u8 code) +{ + unsigned int timeout; + + rcsi2_write(priv, priv->info->regs->phtw, + PHTW_DWEN | PHTW_TESTDIN_DATA(data) | + PHTW_CWEN | PHTW_TESTDIN_CODE(code)); + + /* Wait for DWEN and CWEN to be cleared by hardware. */ + for (timeout = 0; timeout <= 20; timeout++) { + if (!(rcsi2_read(priv, priv->info->regs->phtw) & (PHTW_DWEN | PHTW_CWEN))) + return 0; + + usleep_range(1000, 2000); + } + + dev_err(priv->dev, "Timeout waiting for PHTW_DWEN and/or PHTW_CWEN\n"); + + return -ETIMEDOUT; +} + +static int rcsi2_phtw_write_array(struct rcar_csi2 *priv, + const struct phtw_value *values, + unsigned int size) +{ + int ret; + + for (unsigned int i = 0; i < size; i++) { + ret = rcsi2_phtw_write(priv, values[i].data, values[i].code); + if (ret) + return ret; + } + + return 0; +} + +static const struct rcsi2_mbps_info * +rcsi2_mbps_to_info(struct rcar_csi2 *priv, + const struct rcsi2_mbps_info *infotable, unsigned int mbps) +{ + const struct rcsi2_mbps_info *info; + const struct rcsi2_mbps_info *prev = NULL; + + if (mbps < infotable->mbps) + dev_warn(priv->dev, "%u Mbps less than min PHY speed %u Mbps", + mbps, infotable->mbps); + + for (info = infotable; info->mbps != 0; info++) { + if (info->mbps >= mbps) + break; + prev = info; + } + + if (!info->mbps) { + dev_err(priv->dev, "Unsupported PHY speed (%u Mbps)", mbps); + return NULL; + } + + if (prev && ((mbps - prev->mbps) <= (info->mbps - mbps))) + info = prev; + + return info; +} + static void rcsi2_enter_standby_gen3(struct rcar_csi2 *priv) { rcsi2_write(priv, PHYCNT_REG, 0); @@ -699,29 +906,13 @@ static int rcsi2_wait_phy_start(struct rcar_csi2 *priv, static int rcsi2_set_phypll(struct rcar_csi2 *priv, unsigned int mbps) { - const struct rcsi2_mbps_reg *hsfreq; - const struct rcsi2_mbps_reg *hsfreq_prev = NULL; - - if (mbps < priv->info->hsfreqrange->mbps) - dev_warn(priv->dev, "%u Mbps less than min PHY speed %u Mbps", - mbps, priv->info->hsfreqrange->mbps); + const struct rcsi2_mbps_info *info; - for (hsfreq = priv->info->hsfreqrange; hsfreq->mbps != 0; hsfreq++) { - if (hsfreq->mbps >= mbps) - break; - hsfreq_prev = hsfreq; - } - - if (!hsfreq->mbps) { - dev_err(priv->dev, "Unsupported PHY speed (%u Mbps)", mbps); + info = rcsi2_mbps_to_info(priv, priv->info->hsfreqrange, mbps); + if (!info) return -ERANGE; - } - if (hsfreq_prev && - ((mbps - hsfreq_prev->mbps) <= (hsfreq->mbps - mbps))) - hsfreq = hsfreq_prev; - - rcsi2_write(priv, PHYPLL_REG, PHYPLL_HSFREQRANGE(hsfreq->reg)); + rcsi2_write(priv, priv->info->regs->phypll, PHYPLL_HSFREQRANGE(info->reg)); return 0; } @@ -808,20 +999,25 @@ static int rcsi2_get_active_lanes(struct rcar_csi2 *priv, return 0; } -static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv) +static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv, + struct v4l2_subdev_state *state) { const struct rcar_csi2_format *format; u32 phycnt, vcdt = 0, vcdt2 = 0, fld = 0; + const struct v4l2_mbus_framefmt *fmt; unsigned int lanes; unsigned int i; int mbps, ret; + /* Use the format on the sink pad to compute the receiver config. */ + fmt = v4l2_subdev_state_get_format(state, RCAR_CSI2_SINK); + dev_dbg(priv->dev, "Input size (%ux%u%c)\n", - priv->mf.width, priv->mf.height, - priv->mf.field == V4L2_FIELD_NONE ? 'p' : 'i'); + fmt->width, fmt->height, + fmt->field == V4L2_FIELD_NONE ? 'p' : 'i'); /* Code is validated in set_fmt. */ - format = rcsi2_code_to_fmt(priv->mf.code); + format = rcsi2_code_to_fmt(fmt->code); if (!format) return -EINVAL; @@ -849,11 +1045,11 @@ static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv) vcdt2 |= vcdt_part << ((i % 2) * 16); } - if (priv->mf.field == V4L2_FIELD_ALTERNATE) { + if (fmt->field == V4L2_FIELD_ALTERNATE) { fld = FLD_DET_SEL(1) | FLD_FLD_EN4 | FLD_FLD_EN3 | FLD_FLD_EN2 | FLD_FLD_EN; - if (priv->mf.height == 240) + if (fmt->height == 240) fld |= FLD_FLD_NUM(0); else fld |= FLD_FLD_NUM(1); @@ -947,6 +1143,26 @@ static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv) return 0; } +static void rsci2_set_line_order(struct rcar_csi2 *priv, + enum v4l2_mbus_csi2_cphy_line_orders_type order, + unsigned int cfgreg, unsigned int ctrlreg) +{ + const struct rcsi2_cphy_line_order *info = NULL; + + for (unsigned int i = 0; i < ARRAY_SIZE(rcsi2_cphy_line_orders); i++) { + if (rcsi2_cphy_line_orders[i].order == order) { + info = &rcsi2_cphy_line_orders[i]; + break; + } + } + + if (!info) + return; + + rcsi2_modify16(priv, cfgreg, info->cfg, 0x000f); + rcsi2_modify16(priv, ctrlreg, info->ctrl29, 0x0100); +} + static int rcsi2_wait_phy_start_v4h(struct rcar_csi2 *priv, u32 match) { unsigned int timeout; @@ -1024,12 +1240,18 @@ static int rcsi2_c_phy_setting_v4h(struct rcar_csi2 *priv, int msps) rcsi2_write16(priv, V4H_CORE_DIG_RW_TRIO1_REG(1), conf->trio1); rcsi2_write16(priv, V4H_CORE_DIG_RW_TRIO2_REG(1), conf->trio1); - /* - * Configure pin-swap. - * TODO: This registers is not documented yet, the values should depend - * on the 'clock-lanes' and 'data-lanes' devicetree properties. - */ - rcsi2_write16(priv, V4H_CORE_DIG_CLANE_1_RW_CFG_0_REG, 0xf5); + /* Configure data line order. */ + rsci2_set_line_order(priv, priv->line_orders[0], + V4H_CORE_DIG_CLANE_0_RW_CFG_0_REG, + V4H_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_REG(9)); + rsci2_set_line_order(priv, priv->line_orders[1], + V4H_CORE_DIG_CLANE_1_RW_CFG_0_REG, + V4H_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_REG(9)); + rsci2_set_line_order(priv, priv->line_orders[2], + V4H_CORE_DIG_CLANE_2_RW_CFG_0_REG, + V4H_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_REG(9)); + + /* TODO: This registers is not documented. */ rcsi2_write16(priv, V4H_CORE_DIG_CLANE_1_RW_HS_TX_6_REG, 0x5000); /* Leave Shutdown mode */ @@ -1049,15 +1271,18 @@ static int rcsi2_c_phy_setting_v4h(struct rcar_csi2 *priv, int msps) return 0; } -static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv) +static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv, + struct v4l2_subdev_state *state) { const struct rcar_csi2_format *format; + const struct v4l2_mbus_framefmt *fmt; unsigned int lanes; int msps; int ret; - /* Calculate parameters */ - format = rcsi2_code_to_fmt(priv->mf.code); + /* Use the format on the sink pad to compute the receiver config. */ + fmt = v4l2_subdev_state_get_format(state, RCAR_CSI2_SINK); + format = rcsi2_code_to_fmt(fmt->code); if (!format) return -EINVAL; @@ -1075,11 +1300,11 @@ static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv) rcsi2_write(priv, V4H_PHY_SHUTDOWNZ_REG, 0); /* PHY static setting */ - rcsi2_write(priv, V4H_PHY_EN_REG, BIT(0)); + rcsi2_write(priv, V4H_PHY_EN_REG, V4H_PHY_EN_ENABLE_CLK); rcsi2_write(priv, V4H_FLDC_REG, 0); rcsi2_write(priv, V4H_FLDD_REG, 0); rcsi2_write(priv, V4H_IDIC_REG, 0); - rcsi2_write(priv, V4H_PHY_MODE_REG, BIT(0)); + rcsi2_write(priv, V4H_PHY_MODE_REG, V4H_PHY_MODE_CPHY); rcsi2_write(priv, V4H_N_LANES_REG, lanes - 1); /* Reset CSI2 */ @@ -1114,7 +1339,201 @@ static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv) return 0; } -static int rcsi2_start(struct rcar_csi2 *priv) +static int rcsi2_d_phy_setting_v4m(struct rcar_csi2 *priv, int data_rate) +{ + unsigned int timeout; + int ret; + + static const struct phtw_value step1[] = { + { .data = 0x00, .code = 0x00 }, + { .data = 0x00, .code = 0x1e }, + }; + + /* Shutdown and reset PHY. */ + rcsi2_write(priv, V4H_DPHY_RSTZ_REG, BIT(0)); + rcsi2_write(priv, V4H_PHY_SHUTDOWNZ_REG, BIT(0)); + + /* Start internal calibration (POR). */ + ret = rcsi2_phtw_write_array(priv, step1, ARRAY_SIZE(step1)); + if (ret) + return ret; + + /* Wait for POR to complete. */ + for (timeout = 10; timeout > 0; timeout--) { + if ((rcsi2_read(priv, V4M_PHTR_REG) & 0xf0000) == 0x70000) + break; + usleep_range(1000, 2000); + } + + if (!timeout) { + dev_err(priv->dev, "D-PHY calibration failed\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int rcsi2_set_osc_freq(struct rcar_csi2 *priv, unsigned int mbps) +{ + const struct rcsi2_mbps_info *info; + struct phtw_value steps[] = { + { .data = 0x00, .code = 0x00 }, + { .code = 0xe2 }, /* Data filled in below. */ + { .code = 0xe3 }, /* Data filled in below. */ + { .data = 0x01, .code = 0xe4 }, + }; + + info = rcsi2_mbps_to_info(priv, priv->info->hsfreqrange, mbps); + if (!info) + return -ERANGE; + + /* Fill in data for command. */ + steps[1].data = (info->osc_freq & 0x00ff) >> 0; + steps[2].data = (info->osc_freq & 0x0f00) >> 8; + + return rcsi2_phtw_write_array(priv, steps, ARRAY_SIZE(steps)); +} + +static int rcsi2_init_common_v4m(struct rcar_csi2 *priv, unsigned int mbps) +{ + int ret; + + static const struct phtw_value step1[] = { + { .data = 0x00, .code = 0x00 }, + { .data = 0x3c, .code = 0x08 }, + }; + + static const struct phtw_value step2[] = { + { .data = 0x00, .code = 0x00 }, + { .data = 0x80, .code = 0xe0 }, + { .data = 0x31, .code = 0xe1 }, + { .data = 0x06, .code = 0x00 }, + { .data = 0x11, .code = 0x11 }, + { .data = 0x08, .code = 0x00 }, + { .data = 0x11, .code = 0x11 }, + { .data = 0x0a, .code = 0x00 }, + { .data = 0x11, .code = 0x11 }, + { .data = 0x0c, .code = 0x00 }, + { .data = 0x11, .code = 0x11 }, + { .data = 0x01, .code = 0x00 }, + { .data = 0x31, .code = 0xaa }, + { .data = 0x05, .code = 0x00 }, + { .data = 0x05, .code = 0x09 }, + { .data = 0x07, .code = 0x00 }, + { .data = 0x05, .code = 0x09 }, + { .data = 0x09, .code = 0x00 }, + { .data = 0x05, .code = 0x09 }, + { .data = 0x0b, .code = 0x00 }, + { .data = 0x05, .code = 0x09 }, + }; + + static const struct phtw_value step3[] = { + { .data = 0x01, .code = 0x00 }, + { .data = 0x06, .code = 0xab }, + }; + + if (priv->info->hsfreqrange) { + ret = rcsi2_set_phypll(priv, mbps); + if (ret) + return ret; + + ret = rcsi2_set_osc_freq(priv, mbps); + if (ret) + return ret; + } + + if (mbps <= 1500) { + ret = rcsi2_phtw_write_array(priv, step1, ARRAY_SIZE(step1)); + if (ret) + return ret; + } + + if (priv->info->csi0clkfreqrange) + rcsi2_write(priv, V4M_CSI0CLKFCPR_REG, + CSI0CLKFREQRANGE(priv->info->csi0clkfreqrange)); + + rcsi2_write(priv, V4H_PHY_EN_REG, V4H_PHY_EN_ENABLE_CLK | + V4H_PHY_EN_ENABLE_0 | V4H_PHY_EN_ENABLE_1 | + V4H_PHY_EN_ENABLE_2 | V4H_PHY_EN_ENABLE_3); + + if (mbps > 1500) { + ret = rcsi2_phtw_write_array(priv, step2, ARRAY_SIZE(step2)); + if (ret) + return ret; + } + + return rcsi2_phtw_write_array(priv, step3, ARRAY_SIZE(step3)); +} + +static int rcsi2_start_receiver_v4m(struct rcar_csi2 *priv, + struct v4l2_subdev_state *state) +{ + const struct rcar_csi2_format *format; + const struct v4l2_mbus_framefmt *fmt; + unsigned int lanes; + int mbps; + int ret; + + /* Calculate parameters */ + fmt = v4l2_subdev_state_get_format(state, RCAR_CSI2_SINK); + format = rcsi2_code_to_fmt(fmt->code); + if (!format) + return -EINVAL; + + ret = rcsi2_get_active_lanes(priv, &lanes); + if (ret) + return ret; + + mbps = rcsi2_calc_mbps(priv, format->bpp, lanes); + if (mbps < 0) + return mbps; + + /* Reset LINK and PHY */ + rcsi2_write(priv, V4H_CSI2_RESETN_REG, 0); + rcsi2_write(priv, V4H_DPHY_RSTZ_REG, 0); + rcsi2_write(priv, V4H_PHY_SHUTDOWNZ_REG, 0); + rcsi2_write(priv, V4M_PHTC_REG, PHTC_TESTCLR); + + /* PHY static setting */ + rcsi2_write(priv, V4H_PHY_EN_REG, V4H_PHY_EN_ENABLE_CLK); + rcsi2_write(priv, V4H_FLDC_REG, 0); + rcsi2_write(priv, V4H_FLDD_REG, 0); + rcsi2_write(priv, V4H_IDIC_REG, 0); + rcsi2_write(priv, V4H_PHY_MODE_REG, V4H_PHY_MODE_DPHY); + rcsi2_write(priv, V4H_N_LANES_REG, lanes - 1); + + rcsi2_write(priv, V4M_FRXM_REG, + V4M_FRXM_FORCERXMODE_0 | V4M_FRXM_FORCERXMODE_1 | + V4M_FRXM_FORCERXMODE_2 | V4M_FRXM_FORCERXMODE_3); + rcsi2_write(priv, V4M_OVR1_REG, + V4M_OVR1_FORCERXMODE_0 | V4M_OVR1_FORCERXMODE_1 | + V4M_OVR1_FORCERXMODE_2 | V4M_OVR1_FORCERXMODE_3); + + /* Reset CSI2 */ + rcsi2_write(priv, V4M_PHTC_REG, 0); + rcsi2_write(priv, V4H_CSI2_RESETN_REG, BIT(0)); + + /* Common settings */ + ret = rcsi2_init_common_v4m(priv, mbps); + if (ret) + return ret; + + /* D-PHY settings */ + ret = rcsi2_d_phy_setting_v4m(priv, mbps); + if (ret) + return ret; + + rcsi2_wait_phy_start_v4h(priv, V4H_ST_PHYST_ST_STOPSTATE_0 | + V4H_ST_PHYST_ST_STOPSTATE_1 | + V4H_ST_PHYST_ST_STOPSTATE_2 | + V4H_ST_PHYST_ST_STOPSTATE_3); + + rcsi2_write(priv, V4M_FRXM_REG, 0); + + return 0; +} + +static int rcsi2_start(struct rcar_csi2 *priv, struct v4l2_subdev_state *state) { int ret; @@ -1122,7 +1541,7 @@ static int rcsi2_start(struct rcar_csi2 *priv) if (ret < 0) return ret; - ret = priv->info->start_receiver(priv); + ret = priv->info->start_receiver(priv, state); if (ret) { rcsi2_enter_standby(priv); return ret; @@ -1146,17 +1565,16 @@ static void rcsi2_stop(struct rcar_csi2 *priv) static int rcsi2_s_stream(struct v4l2_subdev *sd, int enable) { struct rcar_csi2 *priv = sd_to_csi2(sd); + struct v4l2_subdev_state *state; int ret = 0; - mutex_lock(&priv->lock); + if (!priv->remote) + return -ENODEV; - if (!priv->remote) { - ret = -ENODEV; - goto out; - } + state = v4l2_subdev_lock_and_get_active_state(&priv->subdev); if (enable && priv->stream_count == 0) { - ret = rcsi2_start(priv); + ret = rcsi2_start(priv, state); if (ret) goto out; } else if (!enable && priv->stream_count == 1) { @@ -1165,49 +1583,29 @@ static int rcsi2_s_stream(struct v4l2_subdev *sd, int enable) priv->stream_count += enable ? 1 : -1; out: - mutex_unlock(&priv->lock); + v4l2_subdev_unlock_state(state); return ret; } static int rcsi2_set_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_state *state, struct v4l2_subdev_format *format) { struct rcar_csi2 *priv = sd_to_csi2(sd); - struct v4l2_mbus_framefmt *framefmt; + unsigned int num_pads = rcsi2_num_pads(priv); - mutex_lock(&priv->lock); + if (format->pad > RCAR_CSI2_SINK) + return v4l2_subdev_get_fmt(sd, state, format); if (!rcsi2_code_to_fmt(format->format.code)) format->format.code = rcar_csi2_formats[0].code; - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - priv->mf = format->format; - } else { - framefmt = v4l2_subdev_state_get_format(sd_state, 0); - *framefmt = format->format; - } + *v4l2_subdev_state_get_format(state, format->pad) = format->format; - mutex_unlock(&priv->lock); - - return 0; -} - -static int rcsi2_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) -{ - struct rcar_csi2 *priv = sd_to_csi2(sd); - - mutex_lock(&priv->lock); - - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - format->format = priv->mf; - else - format->format = *v4l2_subdev_state_get_format(sd_state, 0); - - mutex_unlock(&priv->lock); + /* Propagate the format to the source pads. */ + for (unsigned int i = RCAR_CSI2_SOURCE_VC0; i < num_pads; i++) + *v4l2_subdev_state_get_format(state, i) = format->format; return 0; } @@ -1218,7 +1616,7 @@ static const struct v4l2_subdev_video_ops rcar_csi2_video_ops = { static const struct v4l2_subdev_pad_ops rcar_csi2_pad_ops = { .set_fmt = rcsi2_set_pad_format, - .get_fmt = rcsi2_get_pad_format, + .get_fmt = v4l2_subdev_get_fmt, }; static const struct v4l2_subdev_ops rcar_csi2_subdev_ops = { @@ -1226,6 +1624,33 @@ static const struct v4l2_subdev_ops rcar_csi2_subdev_ops = { .pad = &rcar_csi2_pad_ops, }; +static int rcsi2_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct rcar_csi2 *priv = sd_to_csi2(sd); + unsigned int num_pads = rcsi2_num_pads(priv); + + static const struct v4l2_mbus_framefmt rcar_csi2_default_fmt = { + .width = 1920, + .height = 1080, + .code = MEDIA_BUS_FMT_RGB888_1X24, + .colorspace = V4L2_COLORSPACE_SRGB, + .field = V4L2_FIELD_NONE, + .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT, + .quantization = V4L2_QUANTIZATION_DEFAULT, + .xfer_func = V4L2_XFER_FUNC_DEFAULT, + }; + + for (unsigned int i = RCAR_CSI2_SINK; i < num_pads; i++) + *v4l2_subdev_state_get_format(state, i) = rcar_csi2_default_fmt; + + return 0; +} + +static const struct v4l2_subdev_internal_ops rcar_csi2_internal_ops = { + .init_state = rcsi2_init_state, +}; + static irqreturn_t rcsi2_irq(int irq, void *data) { struct rcar_csi2 *priv = data; @@ -1251,14 +1676,17 @@ static irqreturn_t rcsi2_irq(int irq, void *data) static irqreturn_t rcsi2_irq_thread(int irq, void *data) { + struct v4l2_subdev_state *state; struct rcar_csi2 *priv = data; - mutex_lock(&priv->lock); + state = v4l2_subdev_lock_and_get_active_state(&priv->subdev); + rcsi2_stop(priv); usleep_range(1000, 2000); - if (rcsi2_start(priv)) + if (rcsi2_start(priv, state)) dev_warn(priv->dev, "Failed to restart CSI-2 receiver\n"); - mutex_unlock(&priv->lock); + + v4l2_subdev_unlock_state(state); return IRQ_HANDLED; } @@ -1366,6 +1794,9 @@ static int rcsi2_parse_v4l2(struct rcar_csi2 *priv, } } + for (i = 0; i < ARRAY_SIZE(priv->line_orders); i++) + priv->line_orders[i] = vep->bus.mipi_csi2.line_orders[i]; + return 0; } @@ -1425,64 +1856,16 @@ static int rcsi2_parse_dt(struct rcar_csi2 *priv) * NOTE: Magic values are from the datasheet and lack documentation. */ -static int rcsi2_phtw_write(struct rcar_csi2 *priv, u16 data, u16 code) -{ - unsigned int timeout; - - rcsi2_write(priv, PHTW_REG, - PHTW_DWEN | PHTW_TESTDIN_DATA(data) | - PHTW_CWEN | PHTW_TESTDIN_CODE(code)); - - /* Wait for DWEN and CWEN to be cleared by hardware. */ - for (timeout = 0; timeout <= 20; timeout++) { - if (!(rcsi2_read(priv, PHTW_REG) & (PHTW_DWEN | PHTW_CWEN))) - return 0; - - usleep_range(1000, 2000); - } - - dev_err(priv->dev, "Timeout waiting for PHTW_DWEN and/or PHTW_CWEN\n"); - - return -ETIMEDOUT; -} - -static int rcsi2_phtw_write_array(struct rcar_csi2 *priv, - const struct phtw_value *values) -{ - const struct phtw_value *value; - int ret; - - for (value = values; value->data || value->code; value++) { - ret = rcsi2_phtw_write(priv, value->data, value->code); - if (ret) - return ret; - } - - return 0; -} - static int rcsi2_phtw_write_mbps(struct rcar_csi2 *priv, unsigned int mbps, - const struct rcsi2_mbps_reg *values, u16 code) + const struct rcsi2_mbps_info *values, u8 code) { - const struct rcsi2_mbps_reg *value; - const struct rcsi2_mbps_reg *prev_value = NULL; + const struct rcsi2_mbps_info *info; - for (value = values; value->mbps; value++) { - if (value->mbps >= mbps) - break; - prev_value = value; - } - - if (prev_value && - ((mbps - prev_value->mbps) <= (value->mbps - mbps))) - value = prev_value; - - if (!value->mbps) { - dev_err(priv->dev, "Unsupported PHY speed (%u Mbps)", mbps); + info = rcsi2_mbps_to_info(priv, values, mbps); + if (!info) return -ERANGE; - } - return rcsi2_phtw_write(priv, value->reg, code); + return rcsi2_phtw_write(priv, info->reg, code); } static int __rcsi2_init_phtw_h3_v3h_m3n(struct rcar_csi2 *priv, @@ -1494,7 +1877,6 @@ static int __rcsi2_init_phtw_h3_v3h_m3n(struct rcar_csi2 *priv, { .data = 0x11, .code = 0xe4 }, { .data = 0x01, .code = 0xe5 }, { .data = 0x10, .code = 0x04 }, - { /* sentinel */ }, }; static const struct phtw_value step2[] = { @@ -1503,12 +1885,11 @@ static int __rcsi2_init_phtw_h3_v3h_m3n(struct rcar_csi2 *priv, { .data = 0x4b, .code = 0xac }, { .data = 0x03, .code = 0x00 }, { .data = 0x80, .code = 0x07 }, - { /* sentinel */ }, }; int ret; - ret = rcsi2_phtw_write_array(priv, step1); + ret = rcsi2_phtw_write_array(priv, step1, ARRAY_SIZE(step1)); if (ret) return ret; @@ -1523,7 +1904,7 @@ static int __rcsi2_init_phtw_h3_v3h_m3n(struct rcar_csi2 *priv, return ret; } - return rcsi2_phtw_write_array(priv, step2); + return rcsi2_phtw_write_array(priv, step2, ARRAY_SIZE(step2)); } static int rcsi2_init_phtw_h3_v3h_m3n(struct rcar_csi2 *priv, unsigned int mbps) @@ -1549,10 +1930,9 @@ static int rcsi2_phy_post_init_v3m_e3(struct rcar_csi2 *priv) { .data = 0xee, .code = 0x54 }, { .data = 0xee, .code = 0x84 }, { .data = 0xee, .code = 0x94 }, - { /* sentinel */ }, }; - return rcsi2_phtw_write_array(priv, step1); + return rcsi2_phtw_write_array(priv, step1, ARRAY_SIZE(step1)); } static int rcsi2_init_phtw_v3u(struct rcar_csi2 *priv, @@ -1561,20 +1941,17 @@ static int rcsi2_init_phtw_v3u(struct rcar_csi2 *priv, /* In case of 1500Mbps or less */ static const struct phtw_value step1[] = { { .data = 0xcc, .code = 0xe2 }, - { /* sentinel */ }, }; static const struct phtw_value step2[] = { { .data = 0x01, .code = 0xe3 }, { .data = 0x11, .code = 0xe4 }, { .data = 0x01, .code = 0xe5 }, - { /* sentinel */ }, }; /* In case of 1500Mbps or less */ static const struct phtw_value step3[] = { { .data = 0x38, .code = 0x08 }, - { /* sentinel */ }, }; static const struct phtw_value step4[] = { @@ -1582,29 +1959,28 @@ static int rcsi2_init_phtw_v3u(struct rcar_csi2 *priv, { .data = 0x4b, .code = 0xac }, { .data = 0x03, .code = 0x00 }, { .data = 0x80, .code = 0x07 }, - { /* sentinel */ }, }; int ret; if (mbps != 0 && mbps <= 1500) - ret = rcsi2_phtw_write_array(priv, step1); + ret = rcsi2_phtw_write_array(priv, step1, ARRAY_SIZE(step1)); else ret = rcsi2_phtw_write_mbps(priv, mbps, phtw_mbps_v3u, 0xe2); if (ret) return ret; - ret = rcsi2_phtw_write_array(priv, step2); + ret = rcsi2_phtw_write_array(priv, step2, ARRAY_SIZE(step2)); if (ret) return ret; if (mbps != 0 && mbps <= 1500) { - ret = rcsi2_phtw_write_array(priv, step3); + ret = rcsi2_phtw_write_array(priv, step3, ARRAY_SIZE(step3)); if (ret) return ret; } - ret = rcsi2_phtw_write_array(priv, step4); + ret = rcsi2_phtw_write_array(priv, step4, ARRAY_SIZE(step4)); if (ret) return ret; @@ -1688,7 +2064,13 @@ static int rcsi2_probe_resources(struct rcar_csi2 *priv, return PTR_ERR_OR_ZERO(priv->rstc); } +static const struct rcsi2_register_layout rcsi2_registers_gen3 = { + .phtw = PHTW_REG, + .phypll = PHYPLL_REG, +}; + static const struct rcar_csi2_info rcar_csi2_info_r8a7795 = { + .regs = &rcsi2_registers_gen3, .init_phtw = rcsi2_init_phtw_h3_v3h_m3n, .start_receiver = rcsi2_start_receiver_gen3, .enter_standby = rcsi2_enter_standby_gen3, @@ -1700,6 +2082,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a7795 = { }; static const struct rcar_csi2_info rcar_csi2_info_r8a7795es2 = { + .regs = &rcsi2_registers_gen3, .init_phtw = rcsi2_init_phtw_h3es2, .start_receiver = rcsi2_start_receiver_gen3, .enter_standby = rcsi2_enter_standby_gen3, @@ -1711,6 +2094,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a7795es2 = { }; static const struct rcar_csi2_info rcar_csi2_info_r8a7796 = { + .regs = &rcsi2_registers_gen3, .start_receiver = rcsi2_start_receiver_gen3, .enter_standby = rcsi2_enter_standby_gen3, .hsfreqrange = hsfreqrange_m3w, @@ -1719,6 +2103,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a7796 = { }; static const struct rcar_csi2_info rcar_csi2_info_r8a77961 = { + .regs = &rcsi2_registers_gen3, .start_receiver = rcsi2_start_receiver_gen3, .enter_standby = rcsi2_enter_standby_gen3, .hsfreqrange = hsfreqrange_m3w, @@ -1727,6 +2112,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a77961 = { }; static const struct rcar_csi2_info rcar_csi2_info_r8a77965 = { + .regs = &rcsi2_registers_gen3, .init_phtw = rcsi2_init_phtw_h3_v3h_m3n, .start_receiver = rcsi2_start_receiver_gen3, .enter_standby = rcsi2_enter_standby_gen3, @@ -1738,6 +2124,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a77965 = { }; static const struct rcar_csi2_info rcar_csi2_info_r8a77970 = { + .regs = &rcsi2_registers_gen3, .init_phtw = rcsi2_init_phtw_v3m_e3, .phy_post_init = rcsi2_phy_post_init_v3m_e3, .start_receiver = rcsi2_start_receiver_gen3, @@ -1747,6 +2134,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a77970 = { }; static const struct rcar_csi2_info rcar_csi2_info_r8a77980 = { + .regs = &rcsi2_registers_gen3, .init_phtw = rcsi2_init_phtw_h3_v3h_m3n, .start_receiver = rcsi2_start_receiver_gen3, .enter_standby = rcsi2_enter_standby_gen3, @@ -1757,6 +2145,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a77980 = { }; static const struct rcar_csi2_info rcar_csi2_info_r8a77990 = { + .regs = &rcsi2_registers_gen3, .init_phtw = rcsi2_init_phtw_v3m_e3, .phy_post_init = rcsi2_phy_post_init_v3m_e3, .start_receiver = rcsi2_start_receiver_gen3, @@ -1766,6 +2155,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a77990 = { }; static const struct rcar_csi2_info rcar_csi2_info_r8a779a0 = { + .regs = &rcsi2_registers_gen3, .init_phtw = rcsi2_init_phtw_v3u, .start_receiver = rcsi2_start_receiver_gen3, .enter_standby = rcsi2_enter_standby_gen3, @@ -1777,11 +2167,26 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a779a0 = { }; static const struct rcar_csi2_info rcar_csi2_info_r8a779g0 = { + .regs = &rcsi2_registers_gen3, .start_receiver = rcsi2_start_receiver_v4h, .use_isp = true, .support_cphy = true, }; +static const struct rcsi2_register_layout rcsi2_registers_v4m = { + .phtw = V4M_PHTW_REG, + .phypll = V4M_PHYPLL_REG, +}; + +static const struct rcar_csi2_info rcar_csi2_info_r8a779h0 = { + .regs = &rcsi2_registers_v4m, + .start_receiver = rcsi2_start_receiver_v4m, + .hsfreqrange = hsfreqrange_v4m, + .csi0clkfreqrange = 0x0c, + .use_isp = true, + .support_dphy = true, +}; + static const struct of_device_id rcar_csi2_of_table[] = { { .compatible = "renesas,r8a774a1-csi2", @@ -1835,6 +2240,10 @@ static const struct of_device_id rcar_csi2_of_table[] = { .compatible = "renesas,r8a779g0-csi2", .data = &rcar_csi2_info_r8a779g0, }, + { + .compatible = "renesas,r8a779h0-csi2", + .data = &rcar_csi2_info_r8a779h0, + }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, rcar_csi2_of_table); @@ -1870,23 +2279,23 @@ static int rcsi2_probe(struct platform_device *pdev) priv->dev = &pdev->dev; - mutex_init(&priv->lock); priv->stream_count = 0; ret = rcsi2_probe_resources(priv, pdev); if (ret) { dev_err(priv->dev, "Failed to get resources\n"); - goto error_mutex; + return ret; } platform_set_drvdata(pdev, priv); ret = rcsi2_parse_dt(priv); if (ret) - goto error_mutex; + return ret; priv->subdev.owner = THIS_MODULE; priv->subdev.dev = &pdev->dev; + priv->subdev.internal_ops = &rcar_csi2_internal_ops; v4l2_subdev_init(&priv->subdev, &rcar_csi2_subdev_ops); v4l2_set_subdevdata(&priv->subdev, &pdev->dev); snprintf(priv->subdev.name, sizeof(priv->subdev.name), "%s %s", @@ -1896,7 +2305,7 @@ static int rcsi2_probe(struct platform_device *pdev) priv->subdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; priv->subdev.entity.ops = &rcar_csi2_entity_ops; - num_pads = priv->info->use_isp ? 2 : NR_OF_RCAR_CSI2_PAD; + num_pads = rcsi2_num_pads(priv); priv->pads[RCAR_CSI2_SINK].flags = MEDIA_PAD_FL_SINK; for (i = RCAR_CSI2_SOURCE_VC0; i < num_pads; i++) @@ -1912,19 +2321,25 @@ static int rcsi2_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); + ret = v4l2_subdev_init_finalize(&priv->subdev); + if (ret) + goto error_pm_runtime; + ret = v4l2_async_register_subdev(&priv->subdev); if (ret < 0) - goto error_async; + goto error_subdev; dev_info(priv->dev, "%d lanes found\n", priv->lanes); return 0; +error_subdev: + v4l2_subdev_cleanup(&priv->subdev); +error_pm_runtime: + pm_runtime_disable(&pdev->dev); error_async: v4l2_async_nf_unregister(&priv->notifier); v4l2_async_nf_cleanup(&priv->notifier); -error_mutex: - mutex_destroy(&priv->lock); return ret; } @@ -1936,14 +2351,13 @@ static void rcsi2_remove(struct platform_device *pdev) v4l2_async_nf_unregister(&priv->notifier); v4l2_async_nf_cleanup(&priv->notifier); v4l2_async_unregister_subdev(&priv->subdev); + v4l2_subdev_cleanup(&priv->subdev); pm_runtime_disable(&pdev->dev); - - mutex_destroy(&priv->lock); } static struct platform_driver rcar_csi2_pdrv = { - .remove_new = rcsi2_remove, + .remove = rcsi2_remove, .probe = rcsi2_probe, .driver = { .name = "rcar-csi2", diff --git a/drivers/media/platform/renesas/rcar-fcp.c b/drivers/media/platform/renesas/rcar-fcp.c index bcef7b87da7c..cee9bbce4e3a 100644 --- a/drivers/media/platform/renesas/rcar-fcp.c +++ b/drivers/media/platform/renesas/rcar-fcp.c @@ -164,7 +164,7 @@ MODULE_DEVICE_TABLE(of, rcar_fcp_of_match); static struct platform_driver rcar_fcp_platform_driver = { .probe = rcar_fcp_probe, - .remove_new = rcar_fcp_remove, + .remove = rcar_fcp_remove, .driver = { .name = "rcar-fcp", .of_match_table = rcar_fcp_of_match, diff --git a/drivers/media/platform/renesas/rcar-isp.c b/drivers/media/platform/renesas/rcar-isp.c index 530d65fc546b..c515278e3be5 100644 --- a/drivers/media/platform/renesas/rcar-isp.c +++ b/drivers/media/platform/renesas/rcar-isp.c @@ -431,7 +431,9 @@ static int risp_probe_resources(struct rcar_isp *isp, static const struct of_device_id risp_of_id_table[] = { { .compatible = "renesas,r8a779a0-isp" }, { .compatible = "renesas,r8a779g0-isp" }, - { /* sentinel */ }, + /* Keep above for compatibility with old DTB files. */ + { .compatible = "renesas,rcar-gen4-isp" }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, risp_of_id_table); @@ -518,10 +520,11 @@ static void risp_remove(struct platform_device *pdev) static struct platform_driver rcar_isp_driver = { .driver = { .name = "rcar-isp", + .suppress_bind_attrs = true, .of_match_table = risp_of_id_table, }, .probe = risp_probe, - .remove_new = risp_remove, + .remove = risp_remove, }; module_platform_driver(rcar_isp_driver); diff --git a/drivers/media/platform/renesas/rcar-vin/Kconfig b/drivers/media/platform/renesas/rcar-vin/Kconfig index de55fe63d84c..2ec857ab83cb 100644 --- a/drivers/media/platform/renesas/rcar-vin/Kconfig +++ b/drivers/media/platform/renesas/rcar-vin/Kconfig @@ -1,20 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -config VIDEO_RCAR_CSI2 - tristate "R-Car MIPI CSI-2 Receiver" - depends on V4L_PLATFORM_DRIVERS - depends on VIDEO_DEV && OF - depends on ARCH_RENESAS || COMPILE_TEST - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select RESET_CONTROLLER - select V4L2_FWNODE - help - Support for Renesas R-Car MIPI CSI-2 receiver. - Supports R-Car Gen3 and RZ/G2 SoCs. - - To compile this driver as a module, choose M here: the - module will be called rcar-csi2. - config VIDEO_RCAR_VIN tristate "R-Car Video Input (VIN) Driver" depends on V4L_PLATFORM_DRIVERS diff --git a/drivers/media/platform/renesas/rcar-vin/Makefile b/drivers/media/platform/renesas/rcar-vin/Makefile index 00d809f5d2c1..5938ad6290c8 100644 --- a/drivers/media/platform/renesas/rcar-vin/Makefile +++ b/drivers/media/platform/renesas/rcar-vin/Makefile @@ -1,5 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 rcar-vin-objs = rcar-core.o rcar-dma.o rcar-v4l2.o -obj-$(CONFIG_VIDEO_RCAR_CSI2) += rcar-csi2.o obj-$(CONFIG_VIDEO_RCAR_VIN) += rcar-vin.o diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-core.c b/drivers/media/platform/renesas/rcar-vin/rcar-core.c index 809c3a38cc4a..ddfb18e6e7a4 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-core.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-core.c @@ -1274,20 +1274,12 @@ static const struct rvin_info rcar_info_r8a77995 = { .scaler = rvin_scaler_gen3, }; -static const struct rvin_info rcar_info_r8a779a0 = { - .model = RCAR_GEN3, - .use_mc = true, - .use_isp = true, - .nv12 = true, - .max_width = 4096, - .max_height = 4096, -}; - -static const struct rvin_info rcar_info_r8a779g0 = { +static const struct rvin_info rcar_info_gen4 = { .model = RCAR_GEN3, .use_mc = true, .use_isp = true, .nv12 = true, + .raw10 = true, .max_width = 4096, .max_height = 4096, }; @@ -1354,12 +1346,18 @@ static const struct of_device_id rvin_of_id_table[] = { .data = &rcar_info_r8a77995, }, { + /* Keep to be compatible with old DTS files. */ .compatible = "renesas,vin-r8a779a0", - .data = &rcar_info_r8a779a0, + .data = &rcar_info_gen4, }, { + /* Keep to be compatible with old DTS files. */ .compatible = "renesas,vin-r8a779g0", - .data = &rcar_info_r8a779g0, + .data = &rcar_info_gen4, + }, + { + .compatible = "renesas,rcar-gen4-vin", + .data = &rcar_info_gen4, }, { /* Sentinel */ }, }; @@ -1446,7 +1444,7 @@ static struct platform_driver rcar_vin_driver = { .of_match_table = rvin_of_id_table, }, .probe = rcar_vin_probe, - .remove_new = rcar_vin_remove, + .remove = rcar_vin_remove, }; module_platform_driver(rcar_vin_driver); diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c index e2c40abc6d3d..8773998101ff 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c @@ -123,7 +123,9 @@ /* Video n Data Mode Register bits */ #define VNDMR_A8BIT(n) (((n) & 0xff) << 24) #define VNDMR_A8BIT_MASK (0xff << 24) +#define VNDMR_RMODE_RAW10 (2 << 19) #define VNDMR_YMODE_Y8 (1 << 12) +#define VNDMR_YC_THR (1 << 11) #define VNDMR_EXRGB (1 << 8) #define VNDMR_BPSM (1 << 4) #define VNDMR_ABIT (1 << 2) @@ -742,12 +744,22 @@ static int rvin_setup(struct rvin_dev *vin) */ switch (vin->mbus_code) { case MEDIA_BUS_FMT_YUYV8_1X16: - /* BT.601/BT.1358 16bit YCbCr422 */ - vnmc |= VNMC_INF_YUV16; + if (vin->is_csi) + /* YCbCr422 8-bit */ + vnmc |= VNMC_INF_YUV8_BT601; + else + /* BT.601/BT.1358 16bit YCbCr422 */ + vnmc |= VNMC_INF_YUV16; input_is_yuv = true; break; case MEDIA_BUS_FMT_UYVY8_1X16: - vnmc |= VNMC_INF_YUV16 | VNMC_YCAL; + if (vin->is_csi) + /* YCbCr422 8-bit */ + vnmc |= VNMC_INF_YUV8_BT601; + else + /* BT.601/BT.1358 16bit YCbCr422 */ + vnmc |= VNMC_INF_YUV16; + vnmc |= VNMC_YCAL; input_is_yuv = true; break; case MEDIA_BUS_FMT_UYVY8_2X8: @@ -780,6 +792,12 @@ static int rvin_setup(struct rvin_dev *vin) case MEDIA_BUS_FMT_Y8_1X8: vnmc |= VNMC_INF_RAW8; break; + case MEDIA_BUS_FMT_SBGGR10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SRGGB10_1X10: + vnmc |= VNMC_INF_RGB666; + break; default: break; } @@ -888,6 +906,12 @@ static int rvin_setup(struct rvin_dev *vin) dmr = 0; } break; + case V4L2_PIX_FMT_SBGGR10: + case V4L2_PIX_FMT_SGBRG10: + case V4L2_PIX_FMT_SGRBG10: + case V4L2_PIX_FMT_SRGGB10: + dmr = VNDMR_RMODE_RAW10 | VNDMR_YC_THR; + break; default: vin_err(vin, "Invalid pixelformat (0x%x)\n", vin->format.pixelformat); @@ -1270,6 +1294,22 @@ static int rvin_mc_validate_format(struct rvin_dev *vin, struct v4l2_subdev *sd, if (vin->format.pixelformat != V4L2_PIX_FMT_GREY) return -EPIPE; break; + case MEDIA_BUS_FMT_SBGGR10_1X10: + if (vin->format.pixelformat != V4L2_PIX_FMT_SBGGR10) + return -EPIPE; + break; + case MEDIA_BUS_FMT_SGBRG10_1X10: + if (vin->format.pixelformat != V4L2_PIX_FMT_SGBRG10) + return -EPIPE; + break; + case MEDIA_BUS_FMT_SGRBG10_1X10: + if (vin->format.pixelformat != V4L2_PIX_FMT_SGRBG10) + return -EPIPE; + break; + case MEDIA_BUS_FMT_SRGGB10_1X10: + if (vin->format.pixelformat != V4L2_PIX_FMT_SRGGB10) + return -EPIPE; + break; default: return -EPIPE; } @@ -1519,8 +1559,6 @@ static const struct vb2_ops rvin_qops = { .buf_queue = rvin_buffer_queue, .start_streaming = rvin_start_streaming_vq, .stop_streaming = rvin_stop_streaming_vq, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; void rvin_dma_unregister(struct rvin_dev *vin) diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c b/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c index 073f70c6ac68..756fdfdbce61 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c @@ -86,6 +86,22 @@ static const struct rvin_video_format rvin_formats[] = { .fourcc = V4L2_PIX_FMT_GREY, .bpp = 1, }, + { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .bpp = 4, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG10, + .bpp = 4, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG10, + .bpp = 4, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB10, + .bpp = 4, + }, }; const struct rvin_video_format *rvin_format_from_pixel(struct rvin_dev *vin, @@ -106,6 +122,13 @@ const struct rvin_video_format *rvin_format_from_pixel(struct rvin_dev *vin, if (!vin->info->nv12 || !(BIT(vin->id) & 0x3333)) return NULL; break; + case V4L2_PIX_FMT_SBGGR10: + case V4L2_PIX_FMT_SGBRG10: + case V4L2_PIX_FMT_SGRBG10: + case V4L2_PIX_FMT_SRGGB10: + if (!vin->info->raw10) + return NULL; + break; default: break; } @@ -407,6 +430,26 @@ static int rvin_enum_fmt_vid_cap(struct file *file, void *priv, return -EINVAL; f->pixelformat = V4L2_PIX_FMT_SRGGB8; return 0; + case MEDIA_BUS_FMT_SBGGR10_1X10: + if (f->index) + return -EINVAL; + f->pixelformat = V4L2_PIX_FMT_SBGGR10; + return 0; + case MEDIA_BUS_FMT_SGBRG10_1X10: + if (f->index) + return -EINVAL; + f->pixelformat = V4L2_PIX_FMT_SGBRG10; + return 0; + case MEDIA_BUS_FMT_SGRBG10_1X10: + if (f->index) + return -EINVAL; + f->pixelformat = V4L2_PIX_FMT_SGRBG10; + return 0; + case MEDIA_BUS_FMT_SRGGB10_1X10: + if (f->index) + return -EINVAL; + f->pixelformat = V4L2_PIX_FMT_SRGGB10; + return 0; default: return -EINVAL; } @@ -730,7 +773,8 @@ static int rvin_s_dv_timings(struct file *file, void *priv_fh, struct v4l2_subdev *sd = vin_to_source(vin); int ret; - ret = v4l2_subdev_call(sd, video, s_dv_timings, timings); + ret = v4l2_subdev_call(sd, pad, s_dv_timings, + vin->parallel.sink_pad, timings); if (ret) return ret; @@ -744,7 +788,8 @@ static int rvin_g_dv_timings(struct file *file, void *priv_fh, struct rvin_dev *vin = video_drvdata(file); struct v4l2_subdev *sd = vin_to_source(vin); - return v4l2_subdev_call(sd, video, g_dv_timings, timings); + return v4l2_subdev_call(sd, pad, g_dv_timings, + vin->parallel.sink_pad, timings); } static int rvin_query_dv_timings(struct file *file, void *priv_fh, @@ -753,7 +798,8 @@ static int rvin_query_dv_timings(struct file *file, void *priv_fh, struct rvin_dev *vin = video_drvdata(file); struct v4l2_subdev *sd = vin_to_source(vin); - return v4l2_subdev_call(sd, video, query_dv_timings, timings); + return v4l2_subdev_call(sd, pad, query_dv_timings, + vin->parallel.sink_pad, timings); } static int rvin_dv_timings_cap(struct file *file, void *priv_fh, diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-vin.h b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h index 792336dada44..f87d4bc9e53e 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-vin.h +++ b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h @@ -59,7 +59,7 @@ enum rvin_isp_id { #define RVIN_REMOTES_MAX \ (((unsigned int)RVIN_CSI_MAX) > ((unsigned int)RVIN_ISP_MAX) ? \ - RVIN_CSI_MAX : RVIN_ISP_MAX) + (unsigned int)RVIN_CSI_MAX : (unsigned int)RVIN_ISP_MAX) /** * enum rvin_dma_state - DMA states @@ -151,7 +151,8 @@ struct rvin_group_route { * @model: VIN model * @use_mc: use media controller instead of controlling subdevice * @use_isp: the VIN is connected to the ISP and not to the CSI-2 - * @nv12: support outputing NV12 pixel format + * @nv12: support outputting NV12 pixel format + * @raw10: support outputting RAW10 pixel format * @max_width: max input width the VIN supports * @max_height: max input height the VIN supports * @routes: list of possible routes from the CSI-2 recivers to @@ -163,6 +164,7 @@ struct rvin_info { bool use_mc; bool use_isp; bool nv12; + bool raw10; unsigned int max_width; unsigned int max_height; diff --git a/drivers/media/platform/renesas/rcar_drif.c b/drivers/media/platform/renesas/rcar_drif.c index f21d05054341..fc8b6bbef793 100644 --- a/drivers/media/platform/renesas/rcar_drif.c +++ b/drivers/media/platform/renesas/rcar_drif.c @@ -861,8 +861,6 @@ static const struct vb2_ops rcar_drif_vb2_ops = { .buf_queue = rcar_drif_buf_queue, .start_streaming = rcar_drif_start_streaming, .stop_streaming = rcar_drif_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; static int rcar_drif_querycap(struct file *file, void *fh, @@ -1071,7 +1069,6 @@ static int rcar_drif_sdr_register(struct rcar_drif_sdr *sdr) sdr->vdev->release = video_device_release; sdr->vdev->lock = &sdr->v4l2_mutex; sdr->vdev->queue = &sdr->vb_queue; - sdr->vdev->queue->lock = &sdr->vb_queue_mutex; sdr->vdev->ctrl_handler = &sdr->ctrl_hdl; sdr->vdev->v4l2_dev = &sdr->v4l2_dev; sdr->vdev->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER | @@ -1316,6 +1313,7 @@ static int rcar_drif_sdr_probe(struct rcar_drif_sdr *sdr) sdr->vb_queue.ops = &rcar_drif_vb2_ops; sdr->vb_queue.mem_ops = &vb2_vmalloc_memops; sdr->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + sdr->vb_queue.lock = &sdr->vb_queue_mutex; /* Init videobuf2 queue */ ret = vb2_queue_init(&sdr->vb_queue); @@ -1475,7 +1473,7 @@ static struct platform_driver rcar_drif_driver = { .pm = &rcar_drif_pm_ops, }, .probe = rcar_drif_probe, - .remove_new = rcar_drif_remove, + .remove = rcar_drif_remove, }; module_platform_driver(rcar_drif_driver); diff --git a/drivers/media/platform/renesas/rcar_fdp1.c b/drivers/media/platform/renesas/rcar_fdp1.c index a2565b269f3b..5d453a7a8988 100644 --- a/drivers/media/platform/renesas/rcar_fdp1.c +++ b/drivers/media/platform/renesas/rcar_fdp1.c @@ -2032,8 +2032,6 @@ static const struct vb2_ops fdp1_qops = { .buf_queue = fdp1_buf_queue, .start_streaming = fdp1_start_streaming, .stop_streaming = fdp1_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; static int queue_init(void *priv, struct vb2_queue *src_vq, @@ -2444,7 +2442,7 @@ MODULE_DEVICE_TABLE(of, fdp1_dt_ids); static struct platform_driver fdp1_pdrv = { .probe = fdp1_probe, - .remove_new = fdp1_remove, + .remove = fdp1_remove, .driver = { .name = DRIVER_NAME, .of_match_table = fdp1_dt_ids, diff --git a/drivers/media/platform/renesas/rcar_jpu.c b/drivers/media/platform/renesas/rcar_jpu.c index fff349e45067..81038df71bb5 100644 --- a/drivers/media/platform/renesas/rcar_jpu.c +++ b/drivers/media/platform/renesas/rcar_jpu.c @@ -14,7 +14,7 @@ * 3) V4L2_CID_JPEG_ACTIVE_MARKER */ -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <linux/clk.h> #include <linux/err.h> #include <linux/interrupt.h> @@ -1171,8 +1171,6 @@ static const struct vb2_ops jpu_qops = { .buf_finish = jpu_buf_finish, .start_streaming = jpu_start_streaming, .stop_streaming = jpu_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; static int jpu_queue_init(void *priv, struct vb2_queue *src_vq, @@ -1736,7 +1734,7 @@ static const struct dev_pm_ops jpu_pm_ops = { static struct platform_driver jpu_driver = { .probe = jpu_probe, - .remove_new = jpu_remove, + .remove = jpu_remove, .driver = { .of_match_table = jpu_dt_ids, .name = DRV_NAME, diff --git a/drivers/media/platform/renesas/renesas-ceu.c b/drivers/media/platform/renesas/renesas-ceu.c index 167760276796..8cceafe491b1 100644 --- a/drivers/media/platform/renesas/renesas-ceu.c +++ b/drivers/media/platform/renesas/renesas-ceu.c @@ -761,8 +761,6 @@ static const struct vb2_ops ceu_vb2_ops = { .queue_setup = ceu_vb2_setup, .buf_queue = ceu_vb2_queue, .buf_prepare = ceu_vb2_prepare, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .start_streaming = ceu_start_streaming, .stop_streaming = ceu_stop_streaming, }; @@ -1723,7 +1721,7 @@ static struct platform_driver ceu_driver = { .of_match_table = of_match_ptr(ceu_of_match), }, .probe = ceu_probe, - .remove_new = ceu_remove, + .remove = ceu_remove, }; module_platform_driver(ceu_driver); diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c index 280efd2a8185..89be584a4988 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c @@ -72,7 +72,6 @@ static int rzg2l_cru_group_notify_complete(struct v4l2_async_notifier *notifier) source->name, sink->name); return ret; } - cru->csi.channel = 0; cru->ip.remote = cru->csi.subdev; /* Create media device link between CRU IP <-> CRU OUTPUT */ @@ -209,7 +208,7 @@ static int rzg2l_cru_media_init(struct rzg2l_cru_dev *cru) const struct of_device_id *match; int ret; - cru->pad.flags = MEDIA_PAD_FL_SINK; + cru->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; ret = media_entity_pads_init(&cru->vdev.entity, 1, &cru->pad); if (ret) return ret; @@ -242,7 +241,7 @@ static int rzg2l_cru_media_init(struct rzg2l_cru_dev *cru) static int rzg2l_cru_probe(struct platform_device *pdev) { struct rzg2l_cru_dev *cru; - int ret; + int irq, ret; cru = devm_kzalloc(&pdev->dev, sizeof(*cru), GFP_KERNEL); if (!cru) @@ -270,9 +269,14 @@ static int rzg2l_cru_probe(struct platform_device *pdev) cru->dev = &pdev->dev; cru->info = of_device_get_match_data(&pdev->dev); - cru->image_conv_irq = platform_get_irq(pdev, 0); - if (cru->image_conv_irq < 0) - return cru->image_conv_irq; + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(&pdev->dev, irq, rzg2l_cru_irq, 0, + KBUILD_MODNAME, cru); + if (ret) + return dev_err_probe(&pdev->dev, ret, "failed to request irq\n"); platform_set_drvdata(pdev, cru); @@ -325,7 +329,7 @@ static struct platform_driver rzg2l_cru_driver = { .of_match_table = rzg2l_cru_of_id_table, }, .probe = rzg2l_cru_probe, - .remove_new = rzg2l_cru_remove, + .remove = rzg2l_cru_remove, }; module_platform_driver(rzg2l_cru_driver); diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h new file mode 100644 index 000000000000..1c9f22118a5d --- /dev/null +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * rzg2l-cru-regs.h--RZ/G2L (and alike SoCs) CRU Registers Definitions + * + * Copyright (C) 2024 Renesas Electronics Corp. + */ + +#ifndef __RZG2L_CRU_REGS_H__ +#define __RZG2L_CRU_REGS_H__ + +/* HW CRU Registers Definition */ + +/* CRU Control Register */ +#define CRUnCTRL 0x0 +#define CRUnCTRL_VINSEL(x) ((x) << 0) + +/* CRU Interrupt Enable Register */ +#define CRUnIE 0x4 +#define CRUnIE_EFE BIT(17) + +/* CRU Interrupt Status Register */ +#define CRUnINTS 0x8 +#define CRUnINTS_SFS BIT(16) + +/* CRU Reset Register */ +#define CRUnRST 0xc +#define CRUnRST_VRESETN BIT(0) + +/* Memory Bank Base Address (Lower) Register for CRU Image Data */ +#define AMnMBxADDRL(x) (0x100 + ((x) * 8)) + +/* Memory Bank Base Address (Higher) Register for CRU Image Data */ +#define AMnMBxADDRH(x) (0x104 + ((x) * 8)) + +/* Memory Bank Enable Register for CRU Image Data */ +#define AMnMBVALID 0x148 +#define AMnMBVALID_MBVALID(x) GENMASK(x, 0) + +/* Memory Bank Status Register for CRU Image Data */ +#define AMnMBS 0x14c +#define AMnMBS_MBSTS 0x7 + +/* AXI Master Transfer Setting Register for CRU Image Data */ +#define AMnAXIATTR 0x158 +#define AMnAXIATTR_AXILEN_MASK GENMASK(3, 0) +#define AMnAXIATTR_AXILEN (0xf) + +/* AXI Master FIFO Pointer Register for CRU Image Data */ +#define AMnFIFOPNTR 0x168 +#define AMnFIFOPNTR_FIFOWPNTR GENMASK(7, 0) +#define AMnFIFOPNTR_FIFORPNTR_Y GENMASK(23, 16) + +/* AXI Master Transfer Stop Register for CRU Image Data */ +#define AMnAXISTP 0x174 +#define AMnAXISTP_AXI_STOP BIT(0) + +/* AXI Master Transfer Stop Status Register for CRU Image Data */ +#define AMnAXISTPACK 0x178 +#define AMnAXISTPACK_AXI_STOP_ACK BIT(0) + +/* CRU Image Processing Enable Register */ +#define ICnEN 0x200 +#define ICnEN_ICEN BIT(0) + +/* CRU Image Processing Main Control Register */ +#define ICnMC 0x208 +#define ICnMC_CSCTHR BIT(5) +#define ICnMC_INF(x) ((x) << 16) +#define ICnMC_VCSEL(x) ((x) << 22) +#define ICnMC_INF_MASK GENMASK(21, 16) + +/* CRU Module Status Register */ +#define ICnMS 0x254 +#define ICnMS_IA BIT(2) + +/* CRU Data Output Mode Register */ +#define ICnDMR 0x26c +#define ICnDMR_YCMODE_UYVY (1 << 4) + +#endif /* __RZG2L_CRU_REGS_H__ */ diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h index 811603f18af0..8b898ce05b84 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h @@ -8,6 +8,7 @@ #ifndef __RZG2L_CRU__ #define __RZG2L_CRU__ +#include <linux/irqreturn.h> #include <linux/reset.h> #include <media/v4l2-async.h> @@ -30,6 +31,11 @@ #define RZG2L_CRU_MIN_INPUT_HEIGHT 240 #define RZG2L_CRU_MAX_INPUT_HEIGHT 4095 +enum rzg2l_csi2_pads { + RZG2L_CRU_IP_SINK = 0, + RZG2L_CRU_IP_SOURCE, +}; + /** * enum rzg2l_cru_dma_state - DMA states * @RZG2L_CRU_DMA_STOPPED: No operation in progress @@ -47,7 +53,6 @@ enum rzg2l_cru_dma_state { struct rzg2l_cru_csi { struct v4l2_async_connection *asd; struct v4l2_subdev *subdev; - u32 channel; }; struct rzg2l_cru_ip { @@ -58,6 +63,24 @@ struct rzg2l_cru_ip { }; /** + * struct rzg2l_cru_ip_format - CRU IP format + * @code: Media bus code + * @datatype: MIPI CSI2 data type + * @format: 4CC format identifier (V4L2_PIX_FMT_*) + * @icndmr: ICnDMR register value + * @bpp: bytes per pixel + * @yuv: Flag to indicate whether the format is YUV-based. + */ +struct rzg2l_cru_ip_format { + u32 code; + u32 datatype; + u32 format; + u32 icndmr; + u8 bpp; + bool yuv; +}; + +/** * struct rzg2l_cru_dev - Renesas CRU device structure * @dev: (OF) device * @base: device I/O register space remapped to virtual memory @@ -68,8 +91,6 @@ struct rzg2l_cru_ip { * * @vclk: CRU Main clock * - * @image_conv_irq: Holds image conversion interrupt number - * * @vdev: V4L2 video device associated with CRU * @v4l2_dev: V4L2 device * @num_buf: Holds the current number of buffers enabled @@ -105,8 +126,6 @@ struct rzg2l_cru_dev { struct clk *vclk; - int image_conv_irq; - struct video_device vdev; struct v4l2_device v4l2_dev; u8 num_buf; @@ -133,9 +152,6 @@ struct rzg2l_cru_dev { struct v4l2_pix_format format; }; -void rzg2l_cru_vclk_unprepare(struct rzg2l_cru_dev *cru); -int rzg2l_cru_vclk_prepare(struct rzg2l_cru_dev *cru); - int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru); void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru); @@ -144,6 +160,7 @@ void rzg2l_cru_dma_unregister(struct rzg2l_cru_dev *cru); int rzg2l_cru_video_register(struct rzg2l_cru_dev *cru); void rzg2l_cru_video_unregister(struct rzg2l_cru_dev *cru); +irqreturn_t rzg2l_cru_irq(int irq, void *data); const struct v4l2_format_info *rzg2l_cru_format_from_pixel(u32 format); @@ -151,4 +168,8 @@ int rzg2l_cru_ip_subdev_register(struct rzg2l_cru_dev *cru); void rzg2l_cru_ip_subdev_unregister(struct rzg2l_cru_dev *cru); struct v4l2_mbus_framefmt *rzg2l_cru_ip_get_src_fmt(struct rzg2l_cru_dev *cru); +const struct rzg2l_cru_ip_format *rzg2l_cru_ip_code_to_fmt(unsigned int code); +const struct rzg2l_cru_ip_format *rzg2l_cru_ip_format_to_fmt(u32 format); +const struct rzg2l_cru_ip_format *rzg2l_cru_ip_index_to_fmt(u32 index); + #endif diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c index d20f4eff93a4..881e910dce02 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c @@ -108,6 +108,7 @@ struct rzg2l_csi2 { struct reset_control *presetn; struct reset_control *cmn_rstb; struct clk *sysclk; + struct clk *vclk; unsigned long vclk_rate; struct v4l2_subdev subdev; @@ -182,12 +183,15 @@ static const struct rzg2l_csi2_timings rzg2l_csi2_global_timings[] = { struct rzg2l_csi2_format { u32 code; - unsigned int datatype; unsigned int bpp; }; static const struct rzg2l_csi2_format rzg2l_csi2_formats[] = { - { .code = MEDIA_BUS_FMT_UYVY8_1X16, .datatype = 0x1e, .bpp = 16 }, + { .code = MEDIA_BUS_FMT_UYVY8_1X16, .bpp = 16 }, + { .code = MEDIA_BUS_FMT_SBGGR8_1X8, .bpp = 8, }, + { .code = MEDIA_BUS_FMT_SGBRG8_1X8, .bpp = 8, }, + { .code = MEDIA_BUS_FMT_SGRBG8_1X8, .bpp = 8, }, + { .code = MEDIA_BUS_FMT_SRGGB8_1X8, .bpp = 8, }, }; static inline struct rzg2l_csi2 *sd_to_csi2(struct v4l2_subdev *sd) @@ -361,7 +365,7 @@ static int rzg2l_csi2_dphy_setting(struct v4l2_subdev *sd, bool on) return rzg2l_csi2_dphy_disable(csi2); } -static void rzg2l_csi2_mipi_link_enable(struct rzg2l_csi2 *csi2) +static int rzg2l_csi2_mipi_link_enable(struct rzg2l_csi2 *csi2) { unsigned long vclk_rate = csi2->vclk_rate / HZ_PER_MHZ; u32 frrskw, frrclk, frrskw_coeff, frrclk_coeff; @@ -386,11 +390,15 @@ static void rzg2l_csi2_mipi_link_enable(struct rzg2l_csi2 *csi2) rzg2l_csi2_write(csi2, CSI2nDTEL, 0xf778ff0f); rzg2l_csi2_write(csi2, CSI2nDTEH, 0x00ffff1f); + clk_disable_unprepare(csi2->vclk); + /* Enable LINK reception */ rzg2l_csi2_write(csi2, CSI2nMCT3, CSI2nMCT3_RXEN); + + return clk_prepare_enable(csi2->vclk); } -static void rzg2l_csi2_mipi_link_disable(struct rzg2l_csi2 *csi2) +static int rzg2l_csi2_mipi_link_disable(struct rzg2l_csi2 *csi2) { unsigned int timeout = VSRSTS_RETRIES; @@ -409,18 +417,21 @@ static void rzg2l_csi2_mipi_link_disable(struct rzg2l_csi2 *csi2) if (!timeout) dev_err(csi2->dev, "Clearing CSI2nRTST.VSRSTS timed out\n"); + + return 0; } static int rzg2l_csi2_mipi_link_setting(struct v4l2_subdev *sd, bool on) { struct rzg2l_csi2 *csi2 = sd_to_csi2(sd); + int ret; if (on) - rzg2l_csi2_mipi_link_enable(csi2); + ret = rzg2l_csi2_mipi_link_enable(csi2); else - rzg2l_csi2_mipi_link_disable(csi2); + ret = rzg2l_csi2_mipi_link_disable(csi2); - return 0; + return ret; } static int rzg2l_csi2_s_stream(struct v4l2_subdev *sd, int enable) @@ -566,6 +577,9 @@ static int rzg2l_csi2_enum_frame_size(struct v4l2_subdev *sd, if (fse->index != 0) return -EINVAL; + if (!rzg2l_csi2_code_to_fmt(fse->code)) + return -EINVAL; + fse->min_width = RZG2L_CSI2_MIN_WIDTH; fse->min_height = RZG2L_CSI2_MIN_HEIGHT; fse->max_width = RZG2L_CSI2_MAX_WIDTH; @@ -574,6 +588,25 @@ static int rzg2l_csi2_enum_frame_size(struct v4l2_subdev *sd, return 0; } +static int rzg2l_csi2_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) +{ + struct rzg2l_csi2 *csi2 = sd_to_csi2(sd); + struct media_pad *remote_pad; + + if (!csi2->remote_source) + return -ENODEV; + + remote_pad = media_pad_remote_pad_unique(&csi2->pads[RZG2L_CSI2_SINK]); + if (IS_ERR(remote_pad)) { + dev_err(csi2->dev, "can't get source pad of %s (%ld)\n", + csi2->remote_source->name, PTR_ERR(remote_pad)); + return PTR_ERR(remote_pad); + } + return v4l2_subdev_call(csi2->remote_source, pad, get_frame_desc, + remote_pad->index, fd); +} + static const struct v4l2_subdev_video_ops rzg2l_csi2_video_ops = { .s_stream = rzg2l_csi2_s_stream, .pre_streamon = rzg2l_csi2_pre_streamon, @@ -585,6 +618,7 @@ static const struct v4l2_subdev_pad_ops rzg2l_csi2_pad_ops = { .enum_frame_size = rzg2l_csi2_enum_frame_size, .set_fmt = rzg2l_csi2_set_format, .get_fmt = v4l2_subdev_get_fmt, + .get_frame_desc = rzg2l_csi2_get_frame_desc, }; static const struct v4l2_subdev_ops rzg2l_csi2_subdev_ops = { @@ -731,7 +765,6 @@ static const struct media_entity_operations rzg2l_csi2_entity_ops = { static int rzg2l_csi2_probe(struct platform_device *pdev) { struct rzg2l_csi2 *csi2; - struct clk *vclk; int ret; csi2 = devm_kzalloc(&pdev->dev, sizeof(*csi2), GFP_KERNEL); @@ -757,12 +790,11 @@ static int rzg2l_csi2_probe(struct platform_device *pdev) return dev_err_probe(&pdev->dev, PTR_ERR(csi2->sysclk), "Failed to get system clk\n"); - vclk = clk_get(&pdev->dev, "video"); - if (IS_ERR(vclk)) - return dev_err_probe(&pdev->dev, PTR_ERR(vclk), + csi2->vclk = devm_clk_get(&pdev->dev, "video"); + if (IS_ERR(csi2->vclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(csi2->vclk), "Failed to get video clock\n"); - csi2->vclk_rate = clk_get_rate(vclk); - clk_put(vclk); + csi2->vclk_rate = clk_get_rate(csi2->vclk); csi2->dev = &pdev->dev; @@ -789,14 +821,17 @@ static int rzg2l_csi2_probe(struct platform_device *pdev) csi2->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; csi2->subdev.entity.ops = &rzg2l_csi2_entity_ops; - csi2->pads[RZG2L_CSI2_SINK].flags = MEDIA_PAD_FL_SINK; + csi2->pads[RZG2L_CSI2_SINK].flags = MEDIA_PAD_FL_SINK | + MEDIA_PAD_FL_MUST_CONNECT; /* * TODO: RZ/G2L CSI2 supports 4 virtual channels, as virtual * channels should be implemented by streams API which is under * development lets hardcode to VC0 for now. */ - csi2->pads[RZG2L_CSI2_SOURCE].flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_pads_init(&csi2->subdev.entity, 2, csi2->pads); + csi2->pads[RZG2L_CSI2_SOURCE].flags = MEDIA_PAD_FL_SOURCE | + MEDIA_PAD_FL_MUST_CONNECT; + ret = media_entity_pads_init(&csi2->subdev.entity, ARRAY_SIZE(csi2->pads), + csi2->pads); if (ret) goto error_pm; @@ -834,7 +869,7 @@ static void rzg2l_csi2_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } -static int __maybe_unused rzg2l_csi2_pm_runtime_suspend(struct device *dev) +static int rzg2l_csi2_pm_runtime_suspend(struct device *dev) { struct rzg2l_csi2 *csi2 = dev_get_drvdata(dev); @@ -843,7 +878,7 @@ static int __maybe_unused rzg2l_csi2_pm_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused rzg2l_csi2_pm_runtime_resume(struct device *dev) +static int rzg2l_csi2_pm_runtime_resume(struct device *dev) { struct rzg2l_csi2 *csi2 = dev_get_drvdata(dev); @@ -851,21 +886,23 @@ static int __maybe_unused rzg2l_csi2_pm_runtime_resume(struct device *dev) } static const struct dev_pm_ops rzg2l_csi2_pm_ops = { - SET_RUNTIME_PM_OPS(rzg2l_csi2_pm_runtime_suspend, rzg2l_csi2_pm_runtime_resume, NULL) + RUNTIME_PM_OPS(rzg2l_csi2_pm_runtime_suspend, + rzg2l_csi2_pm_runtime_resume, NULL) }; static const struct of_device_id rzg2l_csi2_of_table[] = { { .compatible = "renesas,rzg2l-csi2", }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, rzg2l_csi2_of_table); static struct platform_driver rzg2l_csi2_pdrv = { - .remove_new = rzg2l_csi2_remove, + .remove = rzg2l_csi2_remove, .probe = rzg2l_csi2_probe, .driver = { .name = "rzg2l-csi2", .of_match_table = rzg2l_csi2_of_table, - .pm = &rzg2l_csi2_pm_ops, + .pm = pm_ptr(&rzg2l_csi2_pm_ops), }, }; diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c index 9f351a05893e..76a2b451f1da 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c @@ -5,24 +5,56 @@ * Copyright (C) 2022 Renesas Electronics Corp. */ -#include "rzg2l-cru.h" +#include <linux/delay.h> +#include <media/mipi-csi2.h> -struct rzg2l_cru_ip_format { - u32 code; - unsigned int datatype; - unsigned int bpp; -}; +#include "rzg2l-cru.h" +#include "rzg2l-cru-regs.h" static const struct rzg2l_cru_ip_format rzg2l_cru_ip_formats[] = { - { .code = MEDIA_BUS_FMT_UYVY8_1X16, .datatype = 0x1e, .bpp = 16 }, + { + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .datatype = MIPI_CSI2_DT_YUV422_8B, + .format = V4L2_PIX_FMT_UYVY, + .bpp = 2, + .icndmr = ICnDMR_YCMODE_UYVY, + .yuv = true, + }, + { + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .format = V4L2_PIX_FMT_SBGGR8, + .datatype = MIPI_CSI2_DT_RAW8, + .bpp = 1, + .icndmr = 0, + .yuv = false, + }, + { + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .format = V4L2_PIX_FMT_SGBRG8, + .datatype = MIPI_CSI2_DT_RAW8, + .bpp = 1, + .icndmr = 0, + .yuv = false, + }, + { + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .format = V4L2_PIX_FMT_SGRBG8, + .datatype = MIPI_CSI2_DT_RAW8, + .bpp = 1, + .icndmr = 0, + .yuv = false, + }, + { + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .format = V4L2_PIX_FMT_SRGGB8, + .datatype = MIPI_CSI2_DT_RAW8, + .bpp = 1, + .icndmr = 0, + .yuv = false, + }, }; -enum rzg2l_csi2_pads { - RZG2L_CRU_IP_SINK = 0, - RZG2L_CRU_IP_SOURCE, -}; - -static const struct rzg2l_cru_ip_format *rzg2l_cru_ip_code_to_fmt(unsigned int code) +const struct rzg2l_cru_ip_format *rzg2l_cru_ip_code_to_fmt(unsigned int code) { unsigned int i; @@ -33,6 +65,26 @@ static const struct rzg2l_cru_ip_format *rzg2l_cru_ip_code_to_fmt(unsigned int c return NULL; } +const struct rzg2l_cru_ip_format *rzg2l_cru_ip_format_to_fmt(u32 format) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(rzg2l_cru_ip_formats); i++) { + if (rzg2l_cru_ip_formats[i].format == format) + return &rzg2l_cru_ip_formats[i]; + } + + return NULL; +} + +const struct rzg2l_cru_ip_format *rzg2l_cru_ip_index_to_fmt(u32 index) +{ + if (index >= ARRAY_SIZE(rzg2l_cru_ip_formats)) + return NULL; + + return &rzg2l_cru_ip_formats[index]; +} + struct v4l2_mbus_framefmt *rzg2l_cru_ip_get_src_fmt(struct rzg2l_cru_dev *cru) { struct v4l2_subdev_state *state; @@ -71,26 +123,17 @@ static int rzg2l_cru_ip_s_stream(struct v4l2_subdev *sd, int enable) if (ret) return ret; + fsleep(1000); + ret = rzg2l_cru_start_image_processing(cru); if (ret) { v4l2_subdev_call(cru->ip.remote, video, post_streamoff); return ret; } - rzg2l_cru_vclk_unprepare(cru); - ret = v4l2_subdev_call(cru->ip.remote, video, s_stream, enable); - if (ret == -ENOIOCTLCMD) - ret = 0; - if (!ret) { - ret = rzg2l_cru_vclk_prepare(cru); - if (!ret) - return 0; - } else { - /* enable back vclk so that s_stream in error path disables it */ - if (rzg2l_cru_vclk_prepare(cru)) - dev_err(cru->dev, "Failed to enable vclk\n"); - } + if (!ret || ret == -ENOIOCTLCMD) + return 0; s_stream_ret = ret; @@ -157,7 +200,7 @@ static int rzg2l_cru_ip_enum_frame_size(struct v4l2_subdev *sd, if (fse->index != 0) return -EINVAL; - if (fse->code != MEDIA_BUS_FMT_UYVY8_1X16) + if (!rzg2l_cru_ip_code_to_fmt(fse->code)) return -EINVAL; fse->min_width = RZG2L_CRU_MIN_INPUT_WIDTH; @@ -225,8 +268,10 @@ int rzg2l_cru_ip_subdev_register(struct rzg2l_cru_dev *cru) ip->subdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; ip->subdev.entity.ops = &rzg2l_cru_ip_entity_ops; - ip->pads[0].flags = MEDIA_PAD_FL_SINK; - ip->pads[1].flags = MEDIA_PAD_FL_SOURCE; + ip->pads[RZG2L_CRU_IP_SINK].flags = MEDIA_PAD_FL_SINK | + MEDIA_PAD_FL_MUST_CONNECT; + ip->pads[RZG2L_CRU_IP_SOURCE].flags = MEDIA_PAD_FL_SOURCE | + MEDIA_PAD_FL_MUST_CONNECT; ret = media_entity_pads_init(&ip->subdev.entity, 2, ip->pads); if (ret) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index d0ffa90bc656..cd69c8a686d3 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -15,75 +15,12 @@ #include <linux/delay.h> #include <linux/pm_runtime.h> +#include <media/mipi-csi2.h> #include <media/v4l2-ioctl.h> #include <media/videobuf2-dma-contig.h> #include "rzg2l-cru.h" - -/* HW CRU Registers Definition */ - -/* CRU Control Register */ -#define CRUnCTRL 0x0 -#define CRUnCTRL_VINSEL(x) ((x) << 0) - -/* CRU Interrupt Enable Register */ -#define CRUnIE 0x4 -#define CRUnIE_EFE BIT(17) - -/* CRU Interrupt Status Register */ -#define CRUnINTS 0x8 -#define CRUnINTS_SFS BIT(16) - -/* CRU Reset Register */ -#define CRUnRST 0xc -#define CRUnRST_VRESETN BIT(0) - -/* Memory Bank Base Address (Lower) Register for CRU Image Data */ -#define AMnMBxADDRL(x) (0x100 + ((x) * 8)) - -/* Memory Bank Base Address (Higher) Register for CRU Image Data */ -#define AMnMBxADDRH(x) (0x104 + ((x) * 8)) - -/* Memory Bank Enable Register for CRU Image Data */ -#define AMnMBVALID 0x148 -#define AMnMBVALID_MBVALID(x) GENMASK(x, 0) - -/* Memory Bank Status Register for CRU Image Data */ -#define AMnMBS 0x14c -#define AMnMBS_MBSTS 0x7 - -/* AXI Master FIFO Pointer Register for CRU Image Data */ -#define AMnFIFOPNTR 0x168 -#define AMnFIFOPNTR_FIFOWPNTR GENMASK(7, 0) -#define AMnFIFOPNTR_FIFORPNTR_Y GENMASK(23, 16) - -/* AXI Master Transfer Stop Register for CRU Image Data */ -#define AMnAXISTP 0x174 -#define AMnAXISTP_AXI_STOP BIT(0) - -/* AXI Master Transfer Stop Status Register for CRU Image Data */ -#define AMnAXISTPACK 0x178 -#define AMnAXISTPACK_AXI_STOP_ACK BIT(0) - -/* CRU Image Processing Enable Register */ -#define ICnEN 0x200 -#define ICnEN_ICEN BIT(0) - -/* CRU Image Processing Main Control Register */ -#define ICnMC 0x208 -#define ICnMC_CSCTHR BIT(5) -#define ICnMC_INF_YUV8_422 (0x1e << 16) -#define ICnMC_INF_USER (0x30 << 16) -#define ICnMC_VCSEL(x) ((x) << 22) -#define ICnMC_INF_MASK GENMASK(21, 16) - -/* CRU Module Status Register */ -#define ICnMS 0x254 -#define ICnMS_IA BIT(2) - -/* CRU Data Output Mode Register */ -#define ICnDMR 0x26c -#define ICnDMR_YCMODE_UYVY (1 << 4) +#include "rzg2l-cru-regs.h" #define RZG2L_TIMEOUT_MS 100 #define RZG2L_RETRIES 10 @@ -184,46 +121,6 @@ static void rzg2l_cru_buffer_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&cru->qlock, flags); } -static int rzg2l_cru_mc_validate_format(struct rzg2l_cru_dev *cru, - struct v4l2_subdev *sd, - struct media_pad *pad) -{ - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - - fmt.pad = pad->index; - if (v4l2_subdev_call_state_active(sd, pad, get_fmt, &fmt)) - return -EPIPE; - - switch (fmt.format.code) { - case MEDIA_BUS_FMT_UYVY8_1X16: - break; - default: - return -EPIPE; - } - - switch (fmt.format.field) { - case V4L2_FIELD_TOP: - case V4L2_FIELD_BOTTOM: - case V4L2_FIELD_NONE: - case V4L2_FIELD_INTERLACED_TB: - case V4L2_FIELD_INTERLACED_BT: - case V4L2_FIELD_INTERLACED: - case V4L2_FIELD_SEQ_TB: - case V4L2_FIELD_SEQ_BT: - break; - default: - return -EPIPE; - } - - if (fmt.format.width != cru->format.width || - fmt.format.height != cru->format.height) - return -EPIPE; - - return 0; -} - static void rzg2l_cru_set_slot_addr(struct rzg2l_cru_dev *cru, int slot, dma_addr_t addr) { @@ -278,6 +175,7 @@ static void rzg2l_cru_fill_hw_slot(struct rzg2l_cru_dev *cru, int slot) static void rzg2l_cru_initialize_axi(struct rzg2l_cru_dev *cru) { unsigned int slot; + u32 amnaxiattr; /* * Set image data memory banks. @@ -287,55 +185,47 @@ static void rzg2l_cru_initialize_axi(struct rzg2l_cru_dev *cru) for (slot = 0; slot < cru->num_buf; slot++) rzg2l_cru_fill_hw_slot(cru, slot); + + /* Set AXI burst max length to recommended setting */ + amnaxiattr = rzg2l_cru_read(cru, AMnAXIATTR) & ~AMnAXIATTR_AXILEN_MASK; + amnaxiattr |= AMnAXIATTR_AXILEN; + rzg2l_cru_write(cru, AMnAXIATTR, amnaxiattr); } -static void rzg2l_cru_csi2_setup(struct rzg2l_cru_dev *cru, bool *input_is_yuv, - struct v4l2_mbus_framefmt *ip_sd_fmt) +static void rzg2l_cru_csi2_setup(struct rzg2l_cru_dev *cru, + const struct rzg2l_cru_ip_format *ip_fmt, + u8 csi_vc) { - u32 icnmc; - - switch (ip_sd_fmt->code) { - case MEDIA_BUS_FMT_UYVY8_1X16: - icnmc = ICnMC_INF_YUV8_422; - *input_is_yuv = true; - break; - default: - *input_is_yuv = false; - icnmc = ICnMC_INF_USER; - break; - } + u32 icnmc = ICnMC_INF(ip_fmt->datatype); icnmc |= (rzg2l_cru_read(cru, ICnMC) & ~ICnMC_INF_MASK); /* Set virtual channel CSI2 */ - icnmc |= ICnMC_VCSEL(cru->csi.channel); + icnmc |= ICnMC_VCSEL(csi_vc); rzg2l_cru_write(cru, ICnMC, icnmc); } static int rzg2l_cru_initialize_image_conv(struct rzg2l_cru_dev *cru, - struct v4l2_mbus_framefmt *ip_sd_fmt) + struct v4l2_mbus_framefmt *ip_sd_fmt, + u8 csi_vc) { - bool output_is_yuv = false; - bool input_is_yuv = false; - u32 icndmr; + const struct rzg2l_cru_ip_format *cru_video_fmt; + const struct rzg2l_cru_ip_format *cru_ip_fmt; - rzg2l_cru_csi2_setup(cru, &input_is_yuv, ip_sd_fmt); + cru_ip_fmt = rzg2l_cru_ip_code_to_fmt(ip_sd_fmt->code); + rzg2l_cru_csi2_setup(cru, cru_ip_fmt, csi_vc); /* Output format */ - switch (cru->format.pixelformat) { - case V4L2_PIX_FMT_UYVY: - icndmr = ICnDMR_YCMODE_UYVY; - output_is_yuv = true; - break; - default: + cru_video_fmt = rzg2l_cru_ip_format_to_fmt(cru->format.pixelformat); + if (!cru_video_fmt) { dev_err(cru->dev, "Invalid pixelformat (0x%x)\n", cru->format.pixelformat); return -EINVAL; } /* If input and output use same colorspace, do bypass mode */ - if (output_is_yuv == input_is_yuv) + if (cru_ip_fmt->yuv == cru_video_fmt->yuv) rzg2l_cru_write(cru, ICnMC, rzg2l_cru_read(cru, ICnMC) | ICnMC_CSCTHR); else @@ -343,7 +233,7 @@ static int rzg2l_cru_initialize_image_conv(struct rzg2l_cru_dev *cru, rzg2l_cru_read(cru, ICnMC) & (~ICnMC_CSCTHR)); /* Set output data format */ - rzg2l_cru_write(cru, ICnDMR, icndmr); + rzg2l_cru_write(cru, ICnDMR, cru_video_fmt->icndmr); return 0; } @@ -422,20 +312,48 @@ void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru) spin_unlock_irqrestore(&cru->qlock, flags); } +static int rzg2l_cru_get_virtual_channel(struct rzg2l_cru_dev *cru) +{ + struct v4l2_mbus_frame_desc fd = { }; + struct media_pad *remote_pad; + int ret; + + remote_pad = media_pad_remote_pad_unique(&cru->ip.pads[RZG2L_CRU_IP_SINK]); + ret = v4l2_subdev_call(cru->ip.remote, pad, get_frame_desc, remote_pad->index, &fd); + if (ret < 0 && ret != -ENOIOCTLCMD) { + dev_err(cru->dev, "get_frame_desc failed on IP remote subdev\n"); + return ret; + } + /* If remote subdev does not implement .get_frame_desc default to VC0. */ + if (ret == -ENOIOCTLCMD) + return 0; + + if (fd.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { + dev_err(cru->dev, "get_frame_desc returned invalid bus type %d\n", fd.type); + return -EINVAL; + } + + if (!fd.num_entries) { + dev_err(cru->dev, "get_frame_desc returned zero entries\n"); + return -EINVAL; + } + + return fd.entry[0].bus.csi2.vc; +} + int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru) { struct v4l2_mbus_framefmt *fmt = rzg2l_cru_ip_get_src_fmt(cru); unsigned long flags; + u8 csi_vc; int ret; - spin_lock_irqsave(&cru->qlock, flags); - - /* Initialize image convert */ - ret = rzg2l_cru_initialize_image_conv(cru, fmt); - if (ret) { - spin_unlock_irqrestore(&cru->qlock, flags); + ret = rzg2l_cru_get_virtual_channel(cru); + if (ret < 0) return ret; - } + csi_vc = ret; + + spin_lock_irqsave(&cru->qlock, flags); /* Select a video input */ rzg2l_cru_write(cru, CRUnCTRL, CRUnCTRL_VINSEL(0)); @@ -450,6 +368,13 @@ int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru) /* Initialize the AXI master */ rzg2l_cru_initialize_axi(cru); + /* Initialize image convert */ + ret = rzg2l_cru_initialize_image_conv(cru, fmt, csi_vc); + if (ret) { + spin_unlock_irqrestore(&cru->qlock, flags); + return ret; + } + /* Enable interrupt */ rzg2l_cru_write(cru, CRUnIE, CRUnIE_EFE); @@ -461,16 +386,6 @@ int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru) return 0; } -void rzg2l_cru_vclk_unprepare(struct rzg2l_cru_dev *cru) -{ - clk_disable_unprepare(cru->vclk); -} - -int rzg2l_cru_vclk_prepare(struct rzg2l_cru_dev *cru) -{ - return clk_prepare_enable(cru->vclk); -} - static int rzg2l_cru_set_stream(struct rzg2l_cru_dev *cru, int on) { struct media_pipeline *pipe; @@ -499,39 +414,20 @@ static int rzg2l_cru_set_stream(struct rzg2l_cru_dev *cru, int on) video_device_pipeline_stop(&cru->vdev); - pm_runtime_put_sync(cru->dev); - clk_disable_unprepare(cru->vclk); - return stream_off_ret; } - ret = pm_runtime_resume_and_get(cru->dev); - if (ret) - return ret; - - ret = clk_prepare_enable(cru->vclk); - if (ret) - goto err_pm_put; - - ret = rzg2l_cru_mc_validate_format(cru, sd, pad); - if (ret) - goto err_vclk_disable; - pipe = media_entity_pipeline(&sd->entity) ? : &cru->vdev.pipe; ret = video_device_pipeline_start(&cru->vdev, pipe); if (ret) - goto err_vclk_disable; + return ret; ret = v4l2_subdev_call(sd, video, pre_streamon, 0); - if (ret == -ENOIOCTLCMD) - ret = 0; - if (ret) + if (ret && ret != -ENOIOCTLCMD) goto pipe_line_stop; ret = v4l2_subdev_call(sd, video, s_stream, 1); - if (ret == -ENOIOCTLCMD) - ret = 0; - if (ret) + if (ret && ret != -ENOIOCTLCMD) goto err_s_stream; return 0; @@ -542,12 +438,6 @@ err_s_stream: pipe_line_stop: video_device_pipeline_stop(&cru->vdev); -err_vclk_disable: - clk_disable_unprepare(cru->vclk); - -err_pm_put: - pm_runtime_put_sync(cru->dev); - return ret; } @@ -558,7 +448,7 @@ static void rzg2l_cru_stop_streaming(struct rzg2l_cru_dev *cru) rzg2l_cru_set_stream(cru, 0); } -static irqreturn_t rzg2l_cru_irq(int irq, void *data) +irqreturn_t rzg2l_cru_irq(int irq, void *data) { struct rzg2l_cru_dev *cru = data; unsigned int handled = 0; @@ -646,35 +536,36 @@ static int rzg2l_cru_start_streaming_vq(struct vb2_queue *vq, unsigned int count struct rzg2l_cru_dev *cru = vb2_get_drv_priv(vq); int ret; + ret = pm_runtime_resume_and_get(cru->dev); + if (ret) + return ret; + + ret = clk_prepare_enable(cru->vclk); + if (ret) + goto err_pm_put; + /* Release reset state */ ret = reset_control_deassert(cru->aresetn); if (ret) { dev_err(cru->dev, "failed to deassert aresetn\n"); - return ret; + goto err_vclk_disable; } ret = reset_control_deassert(cru->presetn); if (ret) { reset_control_assert(cru->aresetn); dev_err(cru->dev, "failed to deassert presetn\n"); - return ret; + goto assert_aresetn; } - ret = request_irq(cru->image_conv_irq, rzg2l_cru_irq, - IRQF_SHARED, KBUILD_MODNAME, cru); - if (ret) { - dev_err(cru->dev, "failed to request irq\n"); - goto assert_resets; - } - - /* Allocate scratch buffer. */ + /* Allocate scratch buffer */ cru->scratch = dma_alloc_coherent(cru->dev, cru->format.sizeimage, &cru->scratch_phys, GFP_KERNEL); if (!cru->scratch) { return_unused_buffers(cru, VB2_BUF_STATE_QUEUED); dev_err(cru->dev, "Failed to allocate scratch buffer\n"); ret = -ENOMEM; - goto free_image_conv_irq; + goto assert_presetn; } cru->sequence = 0; @@ -693,13 +584,18 @@ out: if (ret) dma_free_coherent(cru->dev, cru->format.sizeimage, cru->scratch, cru->scratch_phys); -free_image_conv_irq: - free_irq(cru->image_conv_irq, cru); - -assert_resets: +assert_presetn: reset_control_assert(cru->presetn); + +assert_aresetn: reset_control_assert(cru->aresetn); +err_vclk_disable: + clk_disable_unprepare(cru->vclk); + +err_pm_put: + pm_runtime_put_sync(cru->dev); + return ret; } @@ -713,10 +609,11 @@ static void rzg2l_cru_stop_streaming_vq(struct vb2_queue *vq) dma_free_coherent(cru->dev, cru->format.sizeimage, cru->scratch, cru->scratch_phys); - free_irq(cru->image_conv_irq, cru); - reset_control_assert(cru->presetn); - return_unused_buffers(cru, VB2_BUF_STATE_ERROR); + + reset_control_assert(cru->presetn); + clk_disable_unprepare(cru->vclk); + pm_runtime_put_sync(cru->dev); } static const struct vb2_ops rzg2l_cru_qops = { @@ -725,8 +622,6 @@ static const struct vb2_ops rzg2l_cru_qops = { .buf_queue = rzg2l_cru_buffer_queue, .start_streaming = rzg2l_cru_start_streaming_vq, .stop_streaming = rzg2l_cru_stop_streaming_vq, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; void rzg2l_cru_dma_unregister(struct rzg2l_cru_dev *cru) @@ -788,46 +683,16 @@ error: * V4L2 stuff */ -static const struct v4l2_format_info rzg2l_cru_formats[] = { - { - .format = V4L2_PIX_FMT_UYVY, - .bpp[0] = 2, - }, -}; - -const struct v4l2_format_info *rzg2l_cru_format_from_pixel(u32 format) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(rzg2l_cru_formats); i++) - if (rzg2l_cru_formats[i].format == format) - return rzg2l_cru_formats + i; - - return NULL; -} - -static u32 rzg2l_cru_format_bytesperline(struct v4l2_pix_format *pix) -{ - const struct v4l2_format_info *fmt; - - fmt = rzg2l_cru_format_from_pixel(pix->pixelformat); - - if (WARN_ON(!fmt)) - return -EINVAL; - - return pix->width * fmt->bpp[0]; -} - -static u32 rzg2l_cru_format_sizeimage(struct v4l2_pix_format *pix) -{ - return pix->bytesperline * pix->height; -} - static void rzg2l_cru_format_align(struct rzg2l_cru_dev *cru, struct v4l2_pix_format *pix) { - if (!rzg2l_cru_format_from_pixel(pix->pixelformat)) + const struct rzg2l_cru_ip_format *fmt; + + fmt = rzg2l_cru_ip_format_to_fmt(pix->pixelformat); + if (!fmt) { pix->pixelformat = RZG2L_CRU_DEFAULT_FORMAT; + fmt = rzg2l_cru_ip_format_to_fmt(pix->pixelformat); + } switch (pix->field) { case V4L2_FIELD_TOP: @@ -846,8 +711,8 @@ static void rzg2l_cru_format_align(struct rzg2l_cru_dev *cru, v4l_bound_align_image(&pix->width, 320, RZG2L_CRU_MAX_INPUT_WIDTH, 1, &pix->height, 240, RZG2L_CRU_MAX_INPUT_HEIGHT, 2, 0); - pix->bytesperline = rzg2l_cru_format_bytesperline(pix); - pix->sizeimage = rzg2l_cru_format_sizeimage(pix); + pix->bytesperline = pix->width * fmt->bpp; + pix->sizeimage = pix->bytesperline * pix->height; dev_dbg(cru->dev, "Format %ux%u bpl: %u size: %u\n", pix->width, pix->height, pix->bytesperline, pix->sizeimage); @@ -918,10 +783,13 @@ static int rzg2l_cru_g_fmt_vid_cap(struct file *file, void *priv, static int rzg2l_cru_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - if (f->index >= ARRAY_SIZE(rzg2l_cru_formats)) + const struct rzg2l_cru_ip_format *fmt; + + fmt = rzg2l_cru_ip_index_to_fmt(f->index); + if (!fmt) return -EINVAL; - f->pixelformat = rzg2l_cru_formats[f->index].format; + f->pixelformat = fmt->format; return 0; } @@ -997,6 +865,43 @@ static const struct v4l2_file_operations rzg2l_cru_fops = { .read = vb2_fop_read, }; +/* ----------------------------------------------------------------------------- + * Media entity operations + */ + +static int rzg2l_cru_video_link_validate(struct media_link *link) +{ + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + const struct rzg2l_cru_ip_format *video_fmt; + struct v4l2_subdev *subdev; + struct rzg2l_cru_dev *cru; + int ret; + + subdev = media_entity_to_v4l2_subdev(link->source->entity); + fmt.pad = link->source->index; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); + if (ret < 0) + return ret == -ENOIOCTLCMD ? -EINVAL : ret; + + cru = container_of(media_entity_to_video_device(link->sink->entity), + struct rzg2l_cru_dev, vdev); + video_fmt = rzg2l_cru_ip_format_to_fmt(cru->format.pixelformat); + + if (fmt.format.width != cru->format.width || + fmt.format.height != cru->format.height || + fmt.format.field != cru->format.field || + video_fmt->code != fmt.format.code) + return -EPIPE; + + return 0; +} + +static const struct media_entity_operations rzg2l_cru_video_media_ops = { + .link_validate = rzg2l_cru_video_link_validate, +}; + static void rzg2l_cru_v4l2_init(struct rzg2l_cru_dev *cru) { struct video_device *vdev = &cru->vdev; @@ -1008,6 +913,7 @@ static void rzg2l_cru_v4l2_init(struct rzg2l_cru_dev *cru) vdev->lock = &cru->lock; vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; vdev->device_caps |= V4L2_CAP_IO_MC; + vdev->entity.ops = &rzg2l_cru_video_media_ops; vdev->fops = &rzg2l_cru_fops; vdev->ioctl_ops = &rzg2l_cru_ioctl_ops; diff --git a/drivers/media/platform/renesas/sh_vou.c b/drivers/media/platform/renesas/sh_vou.c index 1e74dd601c2b..4ad7ae188d5b 100644 --- a/drivers/media/platform/renesas/sh_vou.c +++ b/drivers/media/platform/renesas/sh_vou.c @@ -360,8 +360,6 @@ static const struct vb2_ops sh_vou_qops = { .buf_queue = sh_vou_buf_queue, .start_streaming = sh_vou_start_streaming, .stop_streaming = sh_vou_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; /* Video IOCTLs */ @@ -1359,7 +1357,7 @@ static void sh_vou_remove(struct platform_device *pdev) } static struct platform_driver sh_vou = { - .remove_new = sh_vou_remove, + .remove = sh_vou_remove, .driver = { .name = "sh-vou", }, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_brx.c b/drivers/media/platform/renesas/vsp1/vsp1_brx.c index a8535c6e2c46..5dee0490c593 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_brx.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_brx.c @@ -96,13 +96,6 @@ static int brx_enum_frame_size(struct v4l2_subdev *subdev, return 0; } -static struct v4l2_rect *brx_get_compose(struct vsp1_brx *brx, - struct v4l2_subdev_state *sd_state, - unsigned int pad) -{ - return v4l2_subdev_state_get_compose(sd_state, pad); -} - static void brx_try_format(struct vsp1_brx *brx, struct v4l2_subdev_state *sd_state, unsigned int pad, struct v4l2_mbus_framefmt *fmt) @@ -119,8 +112,8 @@ static void brx_try_format(struct vsp1_brx *brx, default: /* The BRx can't perform format conversion. */ - format = vsp1_entity_get_pad_format(&brx->entity, sd_state, - BRX_PAD_SINK(0)); + format = v4l2_subdev_state_get_format(sd_state, + BRX_PAD_SINK(0)); fmt->code = format->code; break; } @@ -150,14 +143,14 @@ static int brx_set_format(struct v4l2_subdev *subdev, brx_try_format(brx, state, fmt->pad, &fmt->format); - format = vsp1_entity_get_pad_format(&brx->entity, state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); *format = fmt->format; /* Reset the compose rectangle. */ if (fmt->pad != brx->entity.source_pad) { struct v4l2_rect *compose; - compose = brx_get_compose(brx, state, fmt->pad); + compose = v4l2_subdev_state_get_compose(state, fmt->pad); compose->left = 0; compose->top = 0; compose->width = format->width; @@ -169,8 +162,7 @@ static int brx_set_format(struct v4l2_subdev *subdev, unsigned int i; for (i = 0; i <= brx->entity.source_pad; ++i) { - format = vsp1_entity_get_pad_format(&brx->entity, - state, i); + format = v4l2_subdev_state_get_format(state, i); format->code = fmt->format.code; } } @@ -205,7 +197,7 @@ static int brx_get_selection(struct v4l2_subdev *subdev, return -EINVAL; mutex_lock(&brx->entity.lock); - sel->r = *brx_get_compose(brx, state, sel->pad); + sel->r = *v4l2_subdev_state_get_compose(state, sel->pad); mutex_unlock(&brx->entity.lock); return 0; @@ -242,8 +234,7 @@ static int brx_set_selection(struct v4l2_subdev *subdev, * The compose rectangle top left corner must be inside the output * frame. */ - format = vsp1_entity_get_pad_format(&brx->entity, state, - brx->entity.source_pad); + format = v4l2_subdev_state_get_format(state, brx->entity.source_pad); sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1); sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1); @@ -251,11 +242,11 @@ static int brx_set_selection(struct v4l2_subdev *subdev, * Scaling isn't supported, the compose rectangle size must be identical * to the sink format size. */ - format = vsp1_entity_get_pad_format(&brx->entity, state, sel->pad); + format = v4l2_subdev_state_get_format(state, sel->pad); sel->r.width = format->width; sel->r.height = format->height; - compose = brx_get_compose(brx, state, sel->pad); + compose = v4l2_subdev_state_get_compose(state, sel->pad); *compose = sel->r; done: @@ -281,6 +272,7 @@ static const struct v4l2_subdev_ops brx_ops = { */ static void brx_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -290,8 +282,7 @@ static void brx_configure_stream(struct vsp1_entity *entity, unsigned int flags; unsigned int i; - format = vsp1_entity_get_pad_format(&brx->entity, brx->entity.state, - brx->entity.source_pad); + format = v4l2_subdev_state_get_format(state, brx->entity.source_pad); /* * The hardware is extremely flexible but we have no userspace API to diff --git a/drivers/media/platform/renesas/vsp1/vsp1_clu.c b/drivers/media/platform/renesas/vsp1/vsp1_clu.c index 625776a9bda4..98645bd2a983 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_clu.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_clu.c @@ -170,6 +170,7 @@ static const struct v4l2_subdev_ops clu_ops = { */ static void clu_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -181,8 +182,7 @@ static void clu_configure_stream(struct vsp1_entity *entity, * The yuv_mode can't be changed during streaming. Cache it internally * for future runtime configuration calls. */ - format = vsp1_entity_get_pad_format(&clu->entity, clu->entity.state, - CLU_PAD_SINK); + format = v4l2_subdev_state_get_format(state, CLU_PAD_SINK); clu->yuv_mode = format->code == MEDIA_BUS_FMT_AYUV8_1X32; } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c index 9b087bd8df7d..b5d1f238f7be 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c @@ -317,7 +317,10 @@ static int vsp1_du_pipeline_setup_brx(struct vsp1_device *vsp1, list_add_tail(&released_brx->list_pipe, &pipe->entities); - /* Add the BRx to the pipeline. */ + /* + * Add the BRx to the pipeline, inserting it just before the + * WPF. + */ dev_dbg(vsp1->dev, "%s: pipe %u: acquired %s\n", __func__, pipe->lif->index, BRX_NAME(brx)); @@ -326,7 +329,8 @@ static int vsp1_du_pipeline_setup_brx(struct vsp1_device *vsp1, pipe->brx->sink = &pipe->output->entity; pipe->brx->sink_pad = 0; - list_add_tail(&pipe->brx->list_pipe, &pipe->entities); + list_add_tail(&pipe->brx->list_pipe, + &pipe->output->entity.list_pipe); } /* @@ -420,7 +424,7 @@ static int vsp1_du_pipeline_setup_inputs(struct vsp1_device *vsp1, if (!rpf->entity.pipe) { rpf->entity.pipe = pipe; - list_add_tail(&rpf->entity.list_pipe, &pipe->entities); + list_add(&rpf->entity.list_pipe, &pipe->entities); } brx->inputs[i].rpf = rpf; @@ -546,6 +550,9 @@ static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe) struct vsp1_dl_body *dlb; unsigned int dl_flags = 0; + vsp1_pipeline_calculate_partition(pipe, &pipe->part_table[0], + drm_pipe->width, 0); + if (drm_pipe->force_brx_release) dl_flags |= VSP1_DL_FRAME_END_INTERNAL; if (pipe->output->writeback) @@ -567,9 +574,11 @@ static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe) } vsp1_entity_route_setup(entity, pipe, dlb); - vsp1_entity_configure_stream(entity, pipe, dl, dlb); + vsp1_entity_configure_stream(entity, entity->state, pipe, + dl, dlb); vsp1_entity_configure_frame(entity, pipe, dl, dlb); - vsp1_entity_configure_partition(entity, pipe, dl, dlb); + vsp1_entity_configure_partition(entity, pipe, + &pipe->part_table[0], dl, dlb); } vsp1_dl_list_commit(dl, dl_flags); @@ -733,6 +742,8 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, if (ret < 0) goto unlock; + vsp1_pipeline_dump(pipe, "LIF setup"); + /* Enable the VSP1. */ ret = vsp1_device_get(vsp1); if (ret < 0) @@ -906,6 +917,9 @@ void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index, } vsp1_du_pipeline_setup_inputs(vsp1, pipe); + + vsp1_pipeline_dump(pipe, "atomic update"); + vsp1_du_pipeline_configure(pipe); done: @@ -959,6 +973,9 @@ int vsp1_drm_init(struct vsp1_device *vsp1) vsp1_pipeline_init(pipe); + pipe->partitions = 1; + pipe->part_table = &drm_pipe->partition; + pipe->frame_end = vsp1_du_pipeline_frame_end; /* diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.h b/drivers/media/platform/renesas/vsp1/vsp1_drm.h index ab8b7e3161a2..3fd95b53f27e 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.h @@ -20,6 +20,7 @@ /** * struct vsp1_drm_pipeline - State for the API exposed to the DRM driver * @pipe: the VSP1 pipeline used for display + * @partition: the pre-calculated partition used by the pipeline * @width: output display width * @height: output display height * @force_brx_release: when set, release the BRx during the next reconfiguration @@ -31,6 +32,7 @@ */ struct vsp1_drm_pipeline { struct vsp1_pipeline pipe; + struct vsp1_partition partition; unsigned int width; unsigned int height; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drv.c b/drivers/media/platform/renesas/vsp1/vsp1_drv.c index 1aac44d68731..9fc6bf624a52 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drv.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drv.c @@ -1005,7 +1005,7 @@ MODULE_DEVICE_TABLE(of, vsp1_of_match); static struct platform_driver vsp1_platform_driver = { .probe = vsp1_probe, - .remove_new = vsp1_remove, + .remove = vsp1_remove, .driver = { .name = "vsp1", .pm = &vsp1_pm_ops, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c index 0a5a7f9cc870..8b8945bd8f10 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c @@ -70,12 +70,13 @@ void vsp1_entity_route_setup(struct vsp1_entity *entity, } void vsp1_entity_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { if (entity->ops->configure_stream) - entity->ops->configure_stream(entity, pipe, dl, dlb); + entity->ops->configure_stream(entity, state, pipe, dl, dlb); } void vsp1_entity_configure_frame(struct vsp1_entity *entity, @@ -89,11 +90,13 @@ void vsp1_entity_configure_frame(struct vsp1_entity *entity, void vsp1_entity_configure_partition(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + const struct vsp1_partition *partition, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { if (entity->ops->configure_partition) - entity->ops->configure_partition(entity, pipe, dl, dlb); + entity->ops->configure_partition(entity, pipe, partition, + dl, dlb); } /* ----------------------------------------------------------------------------- @@ -127,49 +130,6 @@ vsp1_entity_get_state(struct vsp1_entity *entity, } } -/** - * vsp1_entity_get_pad_format - Get a pad format from storage for an entity - * @entity: the entity - * @sd_state: the state storage - * @pad: the pad number - * - * Return the format stored in the given configuration for an entity's pad. The - * configuration can be an ACTIVE or TRY configuration. - */ -struct v4l2_mbus_framefmt * -vsp1_entity_get_pad_format(struct vsp1_entity *entity, - struct v4l2_subdev_state *sd_state, - unsigned int pad) -{ - return v4l2_subdev_state_get_format(sd_state, pad); -} - -/** - * vsp1_entity_get_pad_selection - Get a pad selection from storage for entity - * @entity: the entity - * @sd_state: the state storage - * @pad: the pad number - * @target: the selection target - * - * Return the selection rectangle stored in the given configuration for an - * entity's pad. The configuration can be an ACTIVE or TRY configuration. The - * selection target can be COMPOSE or CROP. - */ -struct v4l2_rect * -vsp1_entity_get_pad_selection(struct vsp1_entity *entity, - struct v4l2_subdev_state *sd_state, - unsigned int pad, unsigned int target) -{ - switch (target) { - case V4L2_SEL_TGT_COMPOSE: - return v4l2_subdev_state_get_compose(sd_state, pad); - case V4L2_SEL_TGT_CROP: - return v4l2_subdev_state_get_crop(sd_state, pad); - default: - return NULL; - } -} - /* * vsp1_subdev_get_pad_format - Subdev pad get_fmt handler * @subdev: V4L2 subdevice @@ -191,7 +151,7 @@ int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev, return -EINVAL; mutex_lock(&entity->lock); - fmt->format = *vsp1_entity_get_pad_format(entity, state, fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(state, fmt->pad); mutex_unlock(&entity->lock); return 0; @@ -238,7 +198,7 @@ int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev, return -EINVAL; mutex_lock(&entity->lock); - format = vsp1_entity_get_pad_format(entity, state, 0); + format = v4l2_subdev_state_get_format(state, 0); code->code = format->code; mutex_unlock(&entity->lock); } @@ -276,7 +236,7 @@ int vsp1_subdev_enum_frame_size(struct v4l2_subdev *subdev, if (!state) return -EINVAL; - format = vsp1_entity_get_pad_format(entity, state, fse->pad); + format = v4l2_subdev_state_get_format(state, fse->pad); mutex_lock(&entity->lock); @@ -346,7 +306,7 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, goto done; } - format = vsp1_entity_get_pad_format(entity, state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); if (fmt->pad == entity->source_pad) { /* The output format can't be modified. */ @@ -374,19 +334,17 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, fmt->format = *format; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(entity, state, entity->source_pad); + format = v4l2_subdev_state_get_format(state, entity->source_pad); *format = fmt->format; /* Reset the crop and compose rectangles. */ - selection = vsp1_entity_get_pad_selection(entity, state, fmt->pad, - V4L2_SEL_TGT_CROP); + selection = v4l2_subdev_state_get_crop(state, fmt->pad); selection->left = 0; selection->top = 0; selection->width = format->width; selection->height = format->height; - selection = vsp1_entity_get_pad_selection(entity, state, fmt->pad, - V4L2_SEL_TGT_COMPOSE); + selection = v4l2_subdev_state_get_compose(state, fmt->pad); selection->left = 0; selection->top = 0; selection->width = format->width; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.h b/drivers/media/platform/renesas/vsp1/vsp1_entity.h index 735f32dde4b5..1bcc9e27dfdc 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.h @@ -19,7 +19,6 @@ struct vsp1_dl_body; struct vsp1_dl_list; struct vsp1_pipeline; struct vsp1_partition; -struct vsp1_partition_window; enum vsp1_entity_type { VSP1_ENTITY_BRS, @@ -78,19 +77,30 @@ struct vsp1_route { * configuration. */ struct vsp1_entity_operations { - void (*destroy)(struct vsp1_entity *); - void (*configure_stream)(struct vsp1_entity *, struct vsp1_pipeline *, - struct vsp1_dl_list *, struct vsp1_dl_body *); - void (*configure_frame)(struct vsp1_entity *, struct vsp1_pipeline *, - struct vsp1_dl_list *, struct vsp1_dl_body *); - void (*configure_partition)(struct vsp1_entity *, - struct vsp1_pipeline *, - struct vsp1_dl_list *, - struct vsp1_dl_body *); - unsigned int (*max_width)(struct vsp1_entity *, struct vsp1_pipeline *); - void (*partition)(struct vsp1_entity *, struct vsp1_pipeline *, - struct vsp1_partition *, unsigned int, - struct vsp1_partition_window *); + void (*destroy)(struct vsp1_entity *entity); + void (*configure_stream)(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb); + void (*configure_frame)(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb); + void (*configure_partition)(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + const struct vsp1_partition *partition, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb); + unsigned int (*max_width)(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, + struct vsp1_pipeline *pipe); + void (*partition)(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, + struct vsp1_pipeline *pipe, + struct vsp1_partition *partition, + unsigned int index, + struct v4l2_rect *window); }; struct vsp1_entity { @@ -138,20 +148,13 @@ struct v4l2_subdev_state * vsp1_entity_get_state(struct vsp1_entity *entity, struct v4l2_subdev_state *sd_state, enum v4l2_subdev_format_whence which); -struct v4l2_mbus_framefmt * -vsp1_entity_get_pad_format(struct vsp1_entity *entity, - struct v4l2_subdev_state *sd_state, - unsigned int pad); -struct v4l2_rect * -vsp1_entity_get_pad_selection(struct vsp1_entity *entity, - struct v4l2_subdev_state *sd_state, - unsigned int pad, unsigned int target); void vsp1_entity_route_setup(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, struct vsp1_dl_body *dlb); void vsp1_entity_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb); @@ -163,6 +166,7 @@ void vsp1_entity_configure_frame(struct vsp1_entity *entity, void vsp1_entity_configure_partition(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + const struct vsp1_partition *partition, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hgo.c b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c index 40c571a987ef..2c8ce7175a4e 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hgo.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c @@ -130,6 +130,7 @@ static const struct v4l2_ctrl_config hgo_num_bins_control = { */ static void hgo_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -140,11 +141,8 @@ static void hgo_configure_stream(struct vsp1_entity *entity, unsigned int hratio; unsigned int vratio; - crop = vsp1_entity_get_pad_selection(entity, entity->state, - HISTO_PAD_SINK, V4L2_SEL_TGT_CROP); - compose = vsp1_entity_get_pad_selection(entity, entity->state, - HISTO_PAD_SINK, - V4L2_SEL_TGT_COMPOSE); + crop = v4l2_subdev_state_get_crop(state, HISTO_PAD_SINK); + compose = v4l2_subdev_state_get_compose(state, HISTO_PAD_SINK); vsp1_hgo_write(hgo, dlb, VI6_HGO_REGRST, VI6_HGO_REGRST_RCLEA); @@ -194,6 +192,16 @@ struct vsp1_hgo *vsp1_hgo_create(struct vsp1_device *vsp1) if (hgo == NULL) return ERR_PTR(-ENOMEM); + /* Initialize the video device and queue for statistics data. */ + ret = vsp1_histogram_init(vsp1, &hgo->histo, VSP1_ENTITY_HGO, "hgo", + &hgo_entity_ops, hgo_mbus_formats, + ARRAY_SIZE(hgo_mbus_formats), + HGO_DATA_SIZE, V4L2_META_FMT_VSP1_HGO); + if (ret < 0) { + vsp1_entity_destroy(&hgo->histo.entity); + return ERR_PTR(ret); + } + /* Initialize the control handler. */ v4l2_ctrl_handler_init(&hgo->ctrls.handler, vsp1->info->gen >= 3 ? 2 : 1); @@ -209,15 +217,5 @@ struct vsp1_hgo *vsp1_hgo_create(struct vsp1_device *vsp1) hgo->histo.entity.subdev.ctrl_handler = &hgo->ctrls.handler; - /* Initialize the video device and queue for statistics data. */ - ret = vsp1_histogram_init(vsp1, &hgo->histo, VSP1_ENTITY_HGO, "hgo", - &hgo_entity_ops, hgo_mbus_formats, - ARRAY_SIZE(hgo_mbus_formats), - HGO_DATA_SIZE, V4L2_META_FMT_VSP1_HGO); - if (ret < 0) { - vsp1_entity_destroy(&hgo->histo.entity); - return ERR_PTR(ret); - } - return hgo; } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hgt.c b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c index 8281b86874ab..858f330d44fa 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hgt.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c @@ -126,6 +126,7 @@ static const struct v4l2_ctrl_config hgt_hue_areas = { */ static void hgt_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -139,11 +140,8 @@ static void hgt_configure_stream(struct vsp1_entity *entity, u8 upper; unsigned int i; - crop = vsp1_entity_get_pad_selection(entity, entity->state, - HISTO_PAD_SINK, V4L2_SEL_TGT_CROP); - compose = vsp1_entity_get_pad_selection(entity, entity->state, - HISTO_PAD_SINK, - V4L2_SEL_TGT_COMPOSE); + crop = v4l2_subdev_state_get_crop(state, HISTO_PAD_SINK); + compose = v4l2_subdev_state_get_compose(state, HISTO_PAD_SINK); vsp1_hgt_write(hgt, dlb, VI6_HGT_REGRST, VI6_HGT_REGRST_RCLEA); @@ -193,12 +191,6 @@ struct vsp1_hgt *vsp1_hgt_create(struct vsp1_device *vsp1) if (hgt == NULL) return ERR_PTR(-ENOMEM); - /* Initialize the control handler. */ - v4l2_ctrl_handler_init(&hgt->ctrls, 1); - v4l2_ctrl_new_custom(&hgt->ctrls, &hgt_hue_areas, NULL); - - hgt->histo.entity.subdev.ctrl_handler = &hgt->ctrls; - /* Initialize the video device and queue for statistics data. */ ret = vsp1_histogram_init(vsp1, &hgt->histo, VSP1_ENTITY_HGT, "hgt", &hgt_entity_ops, hgt_mbus_formats, @@ -209,6 +201,12 @@ struct vsp1_hgt *vsp1_hgt_create(struct vsp1_device *vsp1) return ERR_PTR(ret); } + /* Initialize the control handler. */ + v4l2_ctrl_handler_init(&hgt->ctrls, 1); + v4l2_ctrl_new_custom(&hgt->ctrls, &hgt_hue_areas, NULL); + + hgt->histo.entity.subdev.ctrl_handler = &hgt->ctrls; + v4l2_ctrl_handler_setup(&hgt->ctrls); return hgt; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_histo.c b/drivers/media/platform/renesas/vsp1/vsp1_histo.c index 71155282ca11..c762202877ba 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_histo.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_histo.c @@ -36,9 +36,8 @@ struct vsp1_histogram_buffer * vsp1_histogram_buffer_get(struct vsp1_histogram *histo) { struct vsp1_histogram_buffer *buf = NULL; - unsigned long flags; - spin_lock_irqsave(&histo->irqlock, flags); + spin_lock(&histo->irqlock); if (list_empty(&histo->irqqueue)) goto done; @@ -49,7 +48,7 @@ vsp1_histogram_buffer_get(struct vsp1_histogram *histo) histo->readout = true; done: - spin_unlock_irqrestore(&histo->irqlock, flags); + spin_unlock(&histo->irqlock); return buf; } @@ -58,7 +57,6 @@ void vsp1_histogram_buffer_complete(struct vsp1_histogram *histo, size_t size) { struct vsp1_pipeline *pipe = histo->entity.pipe; - unsigned long flags; /* * The pipeline pointer is guaranteed to be valid as this function is @@ -70,10 +68,10 @@ void vsp1_histogram_buffer_complete(struct vsp1_histogram *histo, vb2_set_plane_payload(&buf->buf.vb2_buf, 0, size); vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE); - spin_lock_irqsave(&histo->irqlock, flags); + spin_lock(&histo->irqlock); histo->readout = false; wake_up(&histo->wait_queue); - spin_unlock_irqrestore(&histo->irqlock, flags); + spin_unlock(&histo->irqlock); } /* ----------------------------------------------------------------------------- @@ -124,11 +122,10 @@ static void histo_buffer_queue(struct vb2_buffer *vb) struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vsp1_histogram *histo = vb2_get_drv_priv(vb->vb2_queue); struct vsp1_histogram_buffer *buf = to_vsp1_histogram_buffer(vbuf); - unsigned long flags; - spin_lock_irqsave(&histo->irqlock, flags); + spin_lock_irq(&histo->irqlock); list_add_tail(&buf->queue, &histo->irqqueue); - spin_unlock_irqrestore(&histo->irqlock, flags); + spin_unlock_irq(&histo->irqlock); } static int histo_start_streaming(struct vb2_queue *vq, unsigned int count) @@ -140,9 +137,8 @@ static void histo_stop_streaming(struct vb2_queue *vq) { struct vsp1_histogram *histo = vb2_get_drv_priv(vq); struct vsp1_histogram_buffer *buffer; - unsigned long flags; - spin_lock_irqsave(&histo->irqlock, flags); + spin_lock_irq(&histo->irqlock); /* Remove all buffers from the IRQ queue. */ list_for_each_entry(buffer, &histo->irqqueue, queue) @@ -152,15 +148,13 @@ static void histo_stop_streaming(struct vb2_queue *vq) /* Wait for the buffer being read out (if any) to complete. */ wait_event_lock_irq(histo->wait_queue, !histo->readout, histo->irqlock); - spin_unlock_irqrestore(&histo->irqlock, flags); + spin_unlock_irq(&histo->irqlock); } static const struct vb2_ops histo_video_queue_qops = { .queue_setup = histo_queue_setup, .buf_prepare = histo_buffer_prepare, .buf_queue = histo_buffer_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .start_streaming = histo_start_streaming, .stop_streaming = histo_stop_streaming, }; @@ -222,9 +216,7 @@ static int histo_get_selection(struct v4l2_subdev *subdev, switch (sel->target) { case V4L2_SEL_TGT_COMPOSE_BOUNDS: case V4L2_SEL_TGT_COMPOSE_DEFAULT: - crop = vsp1_entity_get_pad_selection(&histo->entity, state, - HISTO_PAD_SINK, - V4L2_SEL_TGT_CROP); + crop = v4l2_subdev_state_get_crop(state, HISTO_PAD_SINK); sel->r.left = 0; sel->r.top = 0; sel->r.width = crop->width; @@ -233,8 +225,7 @@ static int histo_get_selection(struct v4l2_subdev *subdev, case V4L2_SEL_TGT_CROP_BOUNDS: case V4L2_SEL_TGT_CROP_DEFAULT: - format = vsp1_entity_get_pad_format(&histo->entity, state, - HISTO_PAD_SINK); + format = v4l2_subdev_state_get_format(state, HISTO_PAD_SINK); sel->r.left = 0; sel->r.top = 0; sel->r.width = format->width; @@ -242,9 +233,11 @@ static int histo_get_selection(struct v4l2_subdev *subdev, break; case V4L2_SEL_TGT_COMPOSE: + sel->r = *v4l2_subdev_state_get_compose(state, sel->pad); + break; + case V4L2_SEL_TGT_CROP: - sel->r = *vsp1_entity_get_pad_selection(&histo->entity, state, - sel->pad, sel->target); + sel->r = *v4l2_subdev_state_get_crop(state, sel->pad); break; default: @@ -261,13 +254,10 @@ static int histo_set_crop(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { - struct vsp1_histogram *histo = subdev_to_histo(subdev); struct v4l2_mbus_framefmt *format; - struct v4l2_rect *selection; /* The crop rectangle must be inside the input frame. */ - format = vsp1_entity_get_pad_format(&histo->entity, sd_state, - HISTO_PAD_SINK); + format = v4l2_subdev_state_get_format(sd_state, HISTO_PAD_SINK); sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1); sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1); sel->r.width = clamp_t(unsigned int, sel->r.width, HISTO_MIN_SIZE, @@ -276,14 +266,8 @@ static int histo_set_crop(struct v4l2_subdev *subdev, format->height - sel->r.top); /* Set the crop rectangle and reset the compose rectangle. */ - selection = vsp1_entity_get_pad_selection(&histo->entity, sd_state, - sel->pad, V4L2_SEL_TGT_CROP); - *selection = sel->r; - - selection = vsp1_entity_get_pad_selection(&histo->entity, sd_state, - sel->pad, - V4L2_SEL_TGT_COMPOSE); - *selection = sel->r; + *v4l2_subdev_state_get_crop(sd_state, sel->pad) = sel->r; + *v4l2_subdev_state_get_compose(sd_state, sel->pad) = sel->r; return 0; } @@ -292,7 +276,6 @@ static int histo_set_compose(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { - struct vsp1_histogram *histo = subdev_to_histo(subdev); struct v4l2_rect *compose; struct v4l2_rect *crop; unsigned int ratio; @@ -305,9 +288,7 @@ static int histo_set_compose(struct v4l2_subdev *subdev, sel->r.left = 0; sel->r.top = 0; - crop = vsp1_entity_get_pad_selection(&histo->entity, sd_state, - sel->pad, - V4L2_SEL_TGT_CROP); + crop = v4l2_subdev_state_get_crop(sd_state, sel->pad); /* * Clamp the width and height to acceptable values first and then @@ -332,9 +313,7 @@ static int histo_set_compose(struct v4l2_subdev *subdev, ratio = 1 << (crop->height * 2 / sel->r.height / 3); sel->r.height = crop->height / ratio; - compose = vsp1_entity_get_pad_selection(&histo->entity, sd_state, - sel->pad, - V4L2_SEL_TGT_COMPOSE); + compose = v4l2_subdev_state_get_compose(sd_state, sel->pad); *compose = sel->r; return 0; @@ -371,31 +350,22 @@ done: return ret; } -static int histo_get_format(struct v4l2_subdev *subdev, +static int histo_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { + struct vsp1_histogram *histo = subdev_to_histo(subdev); + if (fmt->pad == HISTO_PAD_SOURCE) { fmt->format.code = MEDIA_BUS_FMT_FIXED; fmt->format.width = 0; fmt->format.height = 0; fmt->format.field = V4L2_FIELD_NONE; fmt->format.colorspace = V4L2_COLORSPACE_RAW; + return 0; } - return vsp1_subdev_get_pad_format(subdev, sd_state, fmt); -} - -static int histo_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct vsp1_histogram *histo = subdev_to_histo(subdev); - - if (fmt->pad != HISTO_PAD_SINK) - return histo_get_format(subdev, sd_state, fmt); - return vsp1_subdev_set_pad_format(subdev, sd_state, fmt, histo->formats, histo->num_formats, HISTO_MIN_SIZE, HISTO_MIN_SIZE, @@ -405,7 +375,7 @@ static int histo_set_format(struct v4l2_subdev *subdev, static const struct v4l2_subdev_pad_ops histo_pad_ops = { .enum_mbus_code = histo_enum_mbus_code, .enum_frame_size = histo_enum_frame_size, - .get_fmt = histo_get_format, + .get_fmt = vsp1_subdev_get_pad_format, .set_fmt = histo_set_format, .get_selection = histo_get_selection, .set_selection = histo_set_selection, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c index bc1299c29ac9..8ba2a7c7305c 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c @@ -78,7 +78,7 @@ static int hsit_set_format(struct v4l2_subdev *subdev, goto done; } - format = vsp1_entity_get_pad_format(&hsit->entity, state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); if (fmt->pad == HSIT_PAD_SOURCE) { /* @@ -101,8 +101,7 @@ static int hsit_set_format(struct v4l2_subdev *subdev, fmt->format = *format; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&hsit->entity, state, - HSIT_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, HSIT_PAD_SOURCE); *format = fmt->format; format->code = hsit->inverse ? MEDIA_BUS_FMT_ARGB8888_1X32 : MEDIA_BUS_FMT_AHSV8888_1X32; @@ -128,6 +127,7 @@ static const struct v4l2_subdev_ops hsit_ops = { */ static void hsit_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_lif.c b/drivers/media/platform/renesas/vsp1/vsp1_lif.c index b1d21a54837b..b3d83d1c5306 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_lif.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_lif.c @@ -83,6 +83,7 @@ static const struct v4l2_subdev_ops lif_ops = { */ static void lif_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -93,8 +94,7 @@ static void lif_configure_stream(struct vsp1_entity *entity, unsigned int obth; unsigned int lbth; - format = vsp1_entity_get_pad_format(&lif->entity, lif->entity.state, - LIF_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, LIF_PAD_SOURCE); switch (entity->vsp1->version & VI6_IP_VERSION_MODEL_MASK) { case VI6_IP_VERSION_MODEL_VSPD_GEN2: diff --git a/drivers/media/platform/renesas/vsp1/vsp1_lut.c b/drivers/media/platform/renesas/vsp1/vsp1_lut.c index 451d24ab0b56..dd264e6532e0 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_lut.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_lut.c @@ -146,6 +146,7 @@ static const struct v4l2_subdev_ops lut_ops = { */ static void lut_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c index 68d05243c3ee..bb0739f684f3 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c @@ -301,6 +301,28 @@ void vsp1_pipeline_init(struct vsp1_pipeline *pipe) pipe->state = VSP1_PIPELINE_STOPPED; } +void __vsp1_pipeline_dump(struct _ddebug *dbg, struct vsp1_pipeline *pipe, + const char *msg) +{ + struct vsp1_device *vsp1 = pipe->output->entity.vsp1; + struct vsp1_entity *entity; + bool first = true; + + printk(KERN_DEBUG "%s: %s: pipe: ", dev_name(vsp1->dev), msg); + + list_for_each_entry(entity, &pipe->entities, list_pipe) { + const char *name; + + name = strchrnul(entity->subdev.name, ' '); + name = name ? name + 1 : entity->subdev.name; + + pr_cont("%s%s", first ? "" : ", ", name); + first = false; + } + + pr_cont("\n"); +} + /* Must be called with the pipe irqlock held. */ void vsp1_pipeline_run(struct vsp1_pipeline *pipe) { @@ -444,6 +466,10 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, vsp1_uds_set_alpha(pipe->uds, dlb, alpha); } +/* ----------------------------------------------------------------------------- + * VSP1 Partition Algorithm support + */ + /* * Propagate the partition calculations through the pipeline * @@ -452,17 +478,82 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, * source. Each entity must produce the partition required for the previous * entity in the pipeline. */ -void vsp1_pipeline_propagate_partition(struct vsp1_pipeline *pipe, - struct vsp1_partition *partition, - unsigned int index, - struct vsp1_partition_window *window) +static void vsp1_pipeline_propagate_partition(struct vsp1_pipeline *pipe, + struct vsp1_partition *partition, + unsigned int index, + struct v4l2_rect *window) { struct vsp1_entity *entity; list_for_each_entry_reverse(entity, &pipe->entities, list_pipe) { if (entity->ops->partition) - entity->ops->partition(entity, pipe, partition, index, - window); + entity->ops->partition(entity, entity->state, pipe, + partition, index, window); } } +/* + * vsp1_pipeline_calculate_partition - Calculate pipeline configuration for a + * partition + * + * @pipe: the pipeline + * @partition: partition that will hold the calculated values + * @div_size: pre-determined maximum partition division size + * @index: partition index + */ +void vsp1_pipeline_calculate_partition(struct vsp1_pipeline *pipe, + struct vsp1_partition *partition, + unsigned int div_size, + unsigned int index) +{ + const struct v4l2_mbus_framefmt *format; + struct v4l2_rect window; + unsigned int modulus; + + /* + * Partitions are computed on the size before rotation, use the format + * at the WPF sink. + */ + format = v4l2_subdev_state_get_format(pipe->output->entity.state, + RWPF_PAD_SINK); + + /* Initialise the partition with sane starting conditions. */ + window.left = index * div_size; + window.width = div_size; + window.top = 0; + window.height = format->height; + + modulus = format->width % div_size; + + /* + * We need to prevent the last partition from being smaller than the + * *minimum* width of the hardware capabilities. + * + * If the modulus is less than half of the partition size, + * the penultimate partition is reduced to half, which is added + * to the final partition: |1234|1234|1234|12|341| + * to prevent this: |1234|1234|1234|1234|1|. + */ + if (modulus) { + /* + * pipe->partitions is 1 based, whilst index is a 0 based index. + * Normalise this locally. + */ + unsigned int partitions = pipe->partitions - 1; + + if (modulus < div_size / 2) { + if (index == partitions - 1) { + /* Halve the penultimate partition. */ + window.width = div_size / 2; + } else if (index == partitions) { + /* Increase the final partition. */ + window.width = (div_size / 2) + modulus; + window.left -= div_size / 2; + } + } else if (index == partitions) { + window.width = modulus; + } + } + + vsp1_pipeline_propagate_partition(pipe, partition, index, &window); +} diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.h b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h index 674b5748d929..1ba7bdbad5a8 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h @@ -9,6 +9,7 @@ #ifndef __VSP1_PIPE_H__ #define __VSP1_PIPE_H__ +#include <linux/dynamic_debug.h> #include <linux/kref.h> #include <linux/list.h> #include <linux/spinlock.h> @@ -54,17 +55,6 @@ enum vsp1_pipeline_state { }; /* - * struct vsp1_partition_window - Partition window coordinates - * @left: horizontal coordinate of the partition start in pixels relative to the - * left edge of the image - * @width: partition width in pixels - */ -struct vsp1_partition_window { - unsigned int left; - unsigned int width; -}; - -/* * struct vsp1_partition - A description of a slice for the partition algorithm * @rpf: The RPF partition window configuration * @uds_sink: The UDS input partition window configuration @@ -73,11 +63,11 @@ struct vsp1_partition_window { * @wpf: The WPF partition window configuration */ struct vsp1_partition { - struct vsp1_partition_window rpf; - struct vsp1_partition_window uds_sink; - struct vsp1_partition_window uds_source; - struct vsp1_partition_window sru; - struct vsp1_partition_window wpf; + struct v4l2_rect rpf[VSP1_MAX_RPF]; + struct v4l2_rect uds_sink; + struct v4l2_rect uds_source; + struct v4l2_rect sru; + struct v4l2_rect wpf; }; /* @@ -106,7 +96,6 @@ struct vsp1_partition { * @configured: when false the @stream_config shall be written to the hardware * @interlaced: True when the pipeline is configured in interlaced mode * @partitions: The number of partitions used to process one frame - * @partition: The current partition for configuration to process * @part_table: The pre-calculated partitions used by the pipeline */ struct vsp1_pipeline { @@ -146,7 +135,6 @@ struct vsp1_pipeline { bool interlaced; unsigned int partitions; - struct vsp1_partition *partition; struct vsp1_partition *part_table; u32 underrun_count; @@ -155,6 +143,24 @@ struct vsp1_pipeline { void vsp1_pipeline_reset(struct vsp1_pipeline *pipe); void vsp1_pipeline_init(struct vsp1_pipeline *pipe); +void __vsp1_pipeline_dump(struct _ddebug *, struct vsp1_pipeline *pipe, + const char *msg); + +#if defined(CONFIG_DYNAMIC_DEBUG) || \ + (defined(CONFIG_DYNAMIC_DEBUG_CORE) && defined(DYNAMIC_DEBUG_MODULE)) +#define vsp1_pipeline_dump(pipe, msg) \ + _dynamic_func_call("vsp1_pipeline_dump()", __vsp1_pipeline_dump, pipe, msg) +#elif defined(DEBUG) +#define vsp1_pipeline_dump(pipe, msg) \ + __vsp1_pipeline_dump(NULL, pipe, msg) +#else +#define vsp1_pipeline_dump(pipe, msg) \ +({ \ + if (0) \ + __vsp1_pipeline_dump(NULL, pipe, msg); \ +}) +#endif + void vsp1_pipeline_run(struct vsp1_pipeline *pipe); bool vsp1_pipeline_stopped(struct vsp1_pipeline *pipe); int vsp1_pipeline_stop(struct vsp1_pipeline *pipe); @@ -166,10 +172,10 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, struct vsp1_dl_body *dlb, unsigned int alpha); -void vsp1_pipeline_propagate_partition(struct vsp1_pipeline *pipe, +void vsp1_pipeline_calculate_partition(struct vsp1_pipeline *pipe, struct vsp1_partition *partition, - unsigned int index, - struct vsp1_partition_window *window); + unsigned int div_size, + unsigned int index); const struct vsp1_format_info *vsp1_get_format_info(struct vsp1_device *vsp1, u32 fourcc); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c index c47579efc65f..5c8b3ba1bd3c 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c @@ -48,6 +48,7 @@ static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf, */ static void rpf_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -80,12 +81,8 @@ static void rpf_configure_stream(struct vsp1_entity *entity, vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_PSTRIDE, pstride); /* Format */ - sink_format = vsp1_entity_get_pad_format(&rpf->entity, - rpf->entity.state, - RWPF_PAD_SINK); - source_format = vsp1_entity_get_pad_format(&rpf->entity, - rpf->entity.state, - RWPF_PAD_SOURCE); + sink_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); + source_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE); infmt = VI6_RPF_INFMT_CIPM | (fmtinfo->hwfmt << VI6_RPF_INFMT_RDFMT_SHIFT); @@ -157,10 +154,8 @@ static void rpf_configure_stream(struct vsp1_entity *entity, if (pipe->brx) { const struct v4l2_rect *compose; - compose = vsp1_entity_get_pad_selection(pipe->brx, - pipe->brx->state, - rpf->brx_input, - V4L2_SEL_TGT_COMPOSE); + compose = v4l2_subdev_state_get_compose(pipe->brx->state, + rpf->brx_input); left = compose->left; top = compose->top; } @@ -284,6 +279,7 @@ static void rpf_configure_frame(struct vsp1_entity *entity, static void rpf_configure_partition(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + const struct vsp1_partition *partition, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { @@ -292,7 +288,7 @@ static void rpf_configure_partition(struct vsp1_entity *entity, struct vsp1_device *vsp1 = rpf->entity.vsp1; const struct vsp1_format_info *fmtinfo = rpf->fmtinfo; const struct v4l2_pix_format_mplane *format = &rpf->format; - struct v4l2_rect crop; + struct v4l2_rect crop = partition->rpf[rpf->entity.index]; /* * Source size and crop offsets. @@ -302,22 +298,6 @@ static void rpf_configure_partition(struct vsp1_entity *entity, * offsets are needed, as planes 2 and 3 always have identical * strides. */ - crop = *vsp1_rwpf_get_crop(rpf, rpf->entity.state); - - /* - * Partition Algorithm Control - * - * The partition algorithm can split this frame into multiple - * slices. We must scale our partition window based on the pipe - * configuration to match the destination partition window. - * To achieve this, we adjust our crop to provide a 'sub-crop' - * matching the expected partition window. Only 'left' and - * 'width' need to be adjusted. - */ - if (pipe->partitions > 1) { - crop.width = pipe->partition->rpf.width; - crop.left += pipe->partition->rpf.left; - } if (pipe->interlaced) { crop.height = round_down(crop.height / 2, fmtinfo->vsub); @@ -366,12 +346,30 @@ static void rpf_configure_partition(struct vsp1_entity *entity, } static void rpf_partition(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_partition *partition, unsigned int partition_idx, - struct vsp1_partition_window *window) + struct v4l2_rect *window) { - partition->rpf = *window; + struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev); + struct v4l2_rect *rpf_rect = &partition->rpf[rpf->entity.index]; + + /* + * Partition Algorithm Control + * + * The partition algorithm can split this frame into multiple slices. We + * must adjust our partition window based on the pipe configuration to + * match the destination partition window. To achieve this, we adjust + * our crop to provide a 'sub-crop' matching the expected partition + * window. + */ + *rpf_rect = *v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK); + + if (pipe->partitions > 1) { + rpf_rect->width = window->width; + rpf_rect->left += window->left; + } } static const struct vsp1_entity_operations rpf_entity_ops = { diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c index 09fb6ffa14e2..9d38203e73d0 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c @@ -16,12 +16,6 @@ #define RWPF_MIN_WIDTH 1 #define RWPF_MIN_HEIGHT 1 -struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, - struct v4l2_subdev_state *sd_state) -{ - return v4l2_subdev_state_get_crop(sd_state, RWPF_PAD_SINK); -} - /* ----------------------------------------------------------------------------- * V4L2 Subdevice Operations */ @@ -79,7 +73,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32) fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32; - format = vsp1_entity_get_pad_format(&rwpf->entity, state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); if (fmt->pad == RWPF_PAD_SOURCE) { /* @@ -105,7 +99,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_rect *crop; /* Update the sink crop rectangle. */ - crop = vsp1_rwpf_get_crop(rwpf, state); + crop = v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK); crop->left = 0; crop->top = 0; crop->width = fmt->format.width; @@ -113,8 +107,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, } /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&rwpf->entity, state, - RWPF_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE); *format = fmt->format; if (rwpf->flip.rotate) { @@ -153,12 +146,11 @@ static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, switch (sel->target) { case V4L2_SEL_TGT_CROP: - sel->r = *vsp1_rwpf_get_crop(rwpf, state); + sel->r = *v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK); break; case V4L2_SEL_TGT_CROP_BOUNDS: - format = vsp1_entity_get_pad_format(&rwpf->entity, state, - RWPF_PAD_SINK); + format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); sel->r.left = 0; sel->r.top = 0; sel->r.width = format->width; @@ -204,8 +196,7 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, } /* Make sure the crop rectangle is entirely contained in the image. */ - format = vsp1_entity_get_pad_format(&rwpf->entity, state, - RWPF_PAD_SINK); + format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); /* * Restrict the crop rectangle coordinates to multiples of 2 to avoid @@ -225,12 +216,11 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, sel->r.height = min_t(unsigned int, sel->r.height, format->height - sel->r.top); - crop = vsp1_rwpf_get_crop(rwpf, state); + crop = v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK); *crop = sel->r; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&rwpf->entity, state, - RWPF_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE); format->width = crop->width; format->height = crop->height; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h index e0d212c70b2f..5ac9f0a6fafc 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h @@ -85,7 +85,4 @@ int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf, unsigned int ncontrols); extern const struct v4l2_subdev_ops vsp1_rwpf_subdev_ops; -struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, - struct v4l2_subdev_state *sd_state); - #endif /* __VSP1_RWPF_H__ */ diff --git a/drivers/media/platform/renesas/vsp1/vsp1_sru.c b/drivers/media/platform/renesas/vsp1/vsp1_sru.c index 11e008aa9f20..1759ce642e6e 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_sru.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_sru.c @@ -131,7 +131,7 @@ static int sru_enum_frame_size(struct v4l2_subdev *subdev, if (!state) return -EINVAL; - format = vsp1_entity_get_pad_format(&sru->entity, state, SRU_PAD_SINK); + format = v4l2_subdev_state_get_format(state, SRU_PAD_SINK); mutex_lock(&sru->entity.lock); @@ -184,8 +184,7 @@ static void sru_try_format(struct vsp1_sru *sru, case SRU_PAD_SOURCE: /* The SRU can't perform format conversion. */ - format = vsp1_entity_get_pad_format(&sru->entity, sd_state, - SRU_PAD_SINK); + format = v4l2_subdev_state_get_format(sd_state, SRU_PAD_SINK); fmt->code = format->code; /* @@ -234,13 +233,12 @@ static int sru_set_format(struct v4l2_subdev *subdev, sru_try_format(sru, state, fmt->pad, &fmt->format); - format = vsp1_entity_get_pad_format(&sru->entity, state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); *format = fmt->format; if (fmt->pad == SRU_PAD_SINK) { /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&sru->entity, state, - SRU_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE); *format = fmt->format; sru_try_format(sru, state, SRU_PAD_SOURCE, format); @@ -267,6 +265,7 @@ static const struct v4l2_subdev_ops sru_ops = { */ static void sru_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -277,10 +276,8 @@ static void sru_configure_stream(struct vsp1_entity *entity, struct v4l2_mbus_framefmt *output; u32 ctrl0; - input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, - SRU_PAD_SINK); - output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, - SRU_PAD_SOURCE); + input = v4l2_subdev_state_get_format(state, SRU_PAD_SINK); + output = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE); if (input->code == MEDIA_BUS_FMT_ARGB8888_1X32) ctrl0 = VI6_SRU_CTRL0_PARAM2 | VI6_SRU_CTRL0_PARAM3 @@ -301,16 +298,14 @@ static void sru_configure_stream(struct vsp1_entity *entity, } static unsigned int sru_max_width(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe) { - struct vsp1_sru *sru = to_sru(&entity->subdev); struct v4l2_mbus_framefmt *input; struct v4l2_mbus_framefmt *output; - input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, - SRU_PAD_SINK); - output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, - SRU_PAD_SOURCE); + input = v4l2_subdev_state_get_format(state, SRU_PAD_SINK); + output = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE); /* * The maximum input width of the SRU is 288 input pixels, but 32 @@ -324,24 +319,24 @@ static unsigned int sru_max_width(struct vsp1_entity *entity, } static void sru_partition(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_partition *partition, unsigned int partition_idx, - struct vsp1_partition_window *window) + struct v4l2_rect *window) { - struct vsp1_sru *sru = to_sru(&entity->subdev); struct v4l2_mbus_framefmt *input; struct v4l2_mbus_framefmt *output; - input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, - SRU_PAD_SINK); - output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, - SRU_PAD_SOURCE); + input = v4l2_subdev_state_get_format(state, SRU_PAD_SINK); + output = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE); /* Adapt if SRUx2 is enabled. */ if (input->width != output->width) { window->width /= 2; window->left /= 2; + window->height /= 2; + window->top /= 2; } partition->sru = *window; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uds.c b/drivers/media/platform/renesas/vsp1/vsp1_uds.c index d89f1197b86c..c5a38478cf8c 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_uds.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uds.c @@ -136,7 +136,7 @@ static int uds_enum_frame_size(struct v4l2_subdev *subdev, if (!state) return -EINVAL; - format = vsp1_entity_get_pad_format(&uds->entity, state, UDS_PAD_SINK); + format = v4l2_subdev_state_get_format(state, UDS_PAD_SINK); mutex_lock(&uds->entity.lock); @@ -183,8 +183,7 @@ static void uds_try_format(struct vsp1_uds *uds, case UDS_PAD_SOURCE: /* The UDS scales but can't perform format conversion. */ - format = vsp1_entity_get_pad_format(&uds->entity, sd_state, - UDS_PAD_SINK); + format = v4l2_subdev_state_get_format(sd_state, UDS_PAD_SINK); fmt->code = format->code; uds_output_limits(format->width, &minimum, &maximum); @@ -217,13 +216,12 @@ static int uds_set_format(struct v4l2_subdev *subdev, uds_try_format(uds, state, fmt->pad, &fmt->format); - format = vsp1_entity_get_pad_format(&uds->entity, state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); *format = fmt->format; if (fmt->pad == UDS_PAD_SINK) { /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&uds->entity, state, - UDS_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, UDS_PAD_SOURCE); *format = fmt->format; uds_try_format(uds, state, UDS_PAD_SOURCE, format); @@ -254,6 +252,7 @@ static const struct v4l2_subdev_ops uds_ops = { */ static void uds_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -265,10 +264,8 @@ static void uds_configure_stream(struct vsp1_entity *entity, unsigned int vscale; bool multitap; - input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SINK); - output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SOURCE); + input = v4l2_subdev_state_get_format(state, UDS_PAD_SINK); + output = v4l2_subdev_state_get_format(state, UDS_PAD_SOURCE); hscale = uds_compute_ratio(input->width, output->width); vscale = uds_compute_ratio(input->height, output->height); @@ -303,15 +300,11 @@ static void uds_configure_stream(struct vsp1_entity *entity, static void uds_configure_partition(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + const struct vsp1_partition *partition, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { struct vsp1_uds *uds = to_uds(&entity->subdev); - struct vsp1_partition *partition = pipe->partition; - const struct v4l2_mbus_framefmt *output; - - output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SOURCE); /* Input size clipping. */ vsp1_uds_write(uds, dlb, VI6_UDS_HSZCLIP, VI6_UDS_HSZCLIP_HCEN | @@ -323,22 +316,20 @@ static void uds_configure_partition(struct vsp1_entity *entity, vsp1_uds_write(uds, dlb, VI6_UDS_CLIP_SIZE, (partition->uds_source.width << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) | - (output->height + (partition->uds_source.height << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT)); } static unsigned int uds_max_width(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe) { - struct vsp1_uds *uds = to_uds(&entity->subdev); const struct v4l2_mbus_framefmt *output; const struct v4l2_mbus_framefmt *input; unsigned int hscale; - input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SINK); - output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SOURCE); + input = v4l2_subdev_state_get_format(state, UDS_PAD_SINK); + output = v4l2_subdev_state_get_format(state, UDS_PAD_SOURCE); hscale = output->width / input->width; /* @@ -364,28 +355,26 @@ static unsigned int uds_max_width(struct vsp1_entity *entity, */ static void uds_partition(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_partition *partition, unsigned int partition_idx, - struct vsp1_partition_window *window) + struct v4l2_rect *window) { - struct vsp1_uds *uds = to_uds(&entity->subdev); const struct v4l2_mbus_framefmt *output; const struct v4l2_mbus_framefmt *input; - /* Initialise the partition state. */ - partition->uds_sink = *window; - partition->uds_source = *window; - - input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SINK); - output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SOURCE); + input = v4l2_subdev_state_get_format(state, UDS_PAD_SINK); + output = v4l2_subdev_state_get_format(state, UDS_PAD_SOURCE); partition->uds_sink.width = window->width * input->width / output->width; partition->uds_sink.left = window->left * input->width / output->width; + partition->uds_sink.height = input->height; + partition->uds_sink.top = 0; + + partition->uds_source = *window; *window = partition->uds_sink; } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uif.c b/drivers/media/platform/renesas/vsp1/vsp1_uif.c index f66936a28a2a..edaf28b544d2 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_uif.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uif.c @@ -104,8 +104,7 @@ static int uif_get_selection(struct v4l2_subdev *subdev, switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: case V4L2_SEL_TGT_CROP_DEFAULT: - format = vsp1_entity_get_pad_format(&uif->entity, state, - UIF_PAD_SINK); + format = v4l2_subdev_state_get_format(state, UIF_PAD_SINK); sel->r.left = 0; sel->r.top = 0; sel->r.width = format->width; @@ -113,8 +112,7 @@ static int uif_get_selection(struct v4l2_subdev *subdev, break; case V4L2_SEL_TGT_CROP: - sel->r = *vsp1_entity_get_pad_selection(&uif->entity, state, - sel->pad, sel->target); + sel->r = *v4l2_subdev_state_get_crop(state, sel->pad); break; default: @@ -150,7 +148,7 @@ static int uif_set_selection(struct v4l2_subdev *subdev, } /* The crop rectangle must be inside the input frame. */ - format = vsp1_entity_get_pad_format(&uif->entity, state, UIF_PAD_SINK); + format = v4l2_subdev_state_get_format(state, UIF_PAD_SINK); sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1); sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1); @@ -160,8 +158,7 @@ static int uif_set_selection(struct v4l2_subdev *subdev, format->height - sel->r.top); /* Store the crop rectangle. */ - selection = vsp1_entity_get_pad_selection(&uif->entity, state, - sel->pad, V4L2_SEL_TGT_CROP); + selection = v4l2_subdev_state_get_crop(state, sel->pad); *selection = sel->r; done: @@ -191,6 +188,7 @@ static const struct v4l2_subdev_ops uif_ops = { */ static void uif_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -203,8 +201,7 @@ static void uif_configure_stream(struct vsp1_entity *entity, vsp1_uif_write(uif, dlb, VI6_UIF_DISCOM_DOCMPMR, VI6_UIF_DISCOM_DOCMPMR_SEL(9)); - crop = vsp1_entity_get_pad_selection(entity, entity->state, - UIF_PAD_SINK, V4L2_SEL_TGT_CROP); + crop = v4l2_subdev_state_get_crop(state, UIF_PAD_SINK); left = crop->left; width = crop->width; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c index 5a9cb0e5640e..03f4efd6b82b 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_video.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c @@ -78,8 +78,14 @@ static int vsp1_video_verify_format(struct vsp1_video *video) if (video->rwpf->fmtinfo->mbus != fmt.format.code || video->rwpf->format.height != fmt.format.height || - video->rwpf->format.width != fmt.format.width) - return -EINVAL; + video->rwpf->format.width != fmt.format.width) { + dev_dbg(video->vsp1->dev, + "Format mismatch: 0x%04x/%ux%u != 0x%04x/%ux%u\n", + video->rwpf->fmtinfo->mbus, video->rwpf->format.width, + video->rwpf->format.height, fmt.format.code, + fmt.format.width, fmt.format.height); + return -EPIPE; + } return 0; } @@ -173,131 +179,6 @@ static int __vsp1_video_try_format(struct vsp1_video *video, } /* ----------------------------------------------------------------------------- - * VSP1 Partition Algorithm support - */ - -/** - * vsp1_video_calculate_partition - Calculate the active partition output window - * - * @pipe: the pipeline - * @partition: partition that will hold the calculated values - * @div_size: pre-determined maximum partition division size - * @index: partition index - */ -static void vsp1_video_calculate_partition(struct vsp1_pipeline *pipe, - struct vsp1_partition *partition, - unsigned int div_size, - unsigned int index) -{ - const struct v4l2_mbus_framefmt *format; - struct vsp1_partition_window window; - unsigned int modulus; - - /* - * Partitions are computed on the size before rotation, use the format - * at the WPF sink. - */ - format = vsp1_entity_get_pad_format(&pipe->output->entity, - pipe->output->entity.state, - RWPF_PAD_SINK); - - /* A single partition simply processes the output size in full. */ - if (pipe->partitions <= 1) { - window.left = 0; - window.width = format->width; - - vsp1_pipeline_propagate_partition(pipe, partition, index, - &window); - return; - } - - /* Initialise the partition with sane starting conditions. */ - window.left = index * div_size; - window.width = div_size; - - modulus = format->width % div_size; - - /* - * We need to prevent the last partition from being smaller than the - * *minimum* width of the hardware capabilities. - * - * If the modulus is less than half of the partition size, - * the penultimate partition is reduced to half, which is added - * to the final partition: |1234|1234|1234|12|341| - * to prevent this: |1234|1234|1234|1234|1|. - */ - if (modulus) { - /* - * pipe->partitions is 1 based, whilst index is a 0 based index. - * Normalise this locally. - */ - unsigned int partitions = pipe->partitions - 1; - - if (modulus < div_size / 2) { - if (index == partitions - 1) { - /* Halve the penultimate partition. */ - window.width = div_size / 2; - } else if (index == partitions) { - /* Increase the final partition. */ - window.width = (div_size / 2) + modulus; - window.left -= div_size / 2; - } - } else if (index == partitions) { - window.width = modulus; - } - } - - vsp1_pipeline_propagate_partition(pipe, partition, index, &window); -} - -static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe) -{ - struct vsp1_device *vsp1 = pipe->output->entity.vsp1; - const struct v4l2_mbus_framefmt *format; - struct vsp1_entity *entity; - unsigned int div_size; - unsigned int i; - - /* - * Partitions are computed on the size before rotation, use the format - * at the WPF sink. - */ - format = vsp1_entity_get_pad_format(&pipe->output->entity, - pipe->output->entity.state, - RWPF_PAD_SINK); - div_size = format->width; - - /* - * Only Gen3+ hardware requires image partitioning, Gen2 will operate - * with a single partition that covers the whole output. - */ - if (vsp1->info->gen >= 3) { - list_for_each_entry(entity, &pipe->entities, list_pipe) { - unsigned int entity_max; - - if (!entity->ops->max_width) - continue; - - entity_max = entity->ops->max_width(entity, pipe); - if (entity_max) - div_size = min(div_size, entity_max); - } - } - - pipe->partitions = DIV_ROUND_UP(format->width, div_size); - pipe->part_table = kcalloc(pipe->partitions, sizeof(*pipe->part_table), - GFP_KERNEL); - if (!pipe->part_table) - return -ENOMEM; - - for (i = 0; i < pipe->partitions; ++i) - vsp1_video_calculate_partition(pipe, &pipe->part_table[i], - div_size, i); - - return 0; -} - -/* ----------------------------------------------------------------------------- * Pipeline Management */ @@ -365,13 +246,12 @@ static void vsp1_video_pipeline_run_partition(struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, unsigned int partition) { + struct vsp1_partition *part = &pipe->part_table[partition]; struct vsp1_dl_body *dlb = vsp1_dl_list_get_body0(dl); struct vsp1_entity *entity; - pipe->partition = &pipe->part_table[partition]; - list_for_each_entry(entity, &pipe->entities, list_pipe) - vsp1_entity_configure_partition(entity, pipe, dl, dlb); + vsp1_entity_configure_partition(entity, pipe, part, dl, dlb); } static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe) @@ -646,11 +526,19 @@ static int vsp1_video_pipeline_build(struct vsp1_pipeline *pipe, static int vsp1_video_pipeline_init(struct vsp1_pipeline *pipe, struct vsp1_video *video) { + int ret; + vsp1_pipeline_init(pipe); pipe->frame_end = vsp1_video_pipeline_frame_end; - return vsp1_video_pipeline_build(pipe, video); + ret = vsp1_video_pipeline_build(pipe, video); + if (ret) + return ret; + + vsp1_pipeline_dump(pipe, "video"); + + return 0; } static struct vsp1_pipeline *vsp1_video_pipeline_get(struct vsp1_video *video) @@ -784,6 +672,54 @@ static void vsp1_video_buffer_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&pipe->irqlock, flags); } +static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe) +{ + struct vsp1_device *vsp1 = pipe->output->entity.vsp1; + const struct v4l2_mbus_framefmt *format; + struct vsp1_entity *entity; + unsigned int div_size; + unsigned int i; + + /* + * Partitions are computed on the size before rotation, use the format + * at the WPF sink. + */ + format = v4l2_subdev_state_get_format(pipe->output->entity.state, + RWPF_PAD_SINK); + div_size = format->width; + + /* + * Only Gen3+ hardware requires image partitioning, Gen2 will operate + * with a single partition that covers the whole output. + */ + if (vsp1->info->gen >= 3) { + list_for_each_entry(entity, &pipe->entities, list_pipe) { + unsigned int entity_max; + + if (!entity->ops->max_width) + continue; + + entity_max = entity->ops->max_width(entity, + entity->state, + pipe); + if (entity_max) + div_size = min(div_size, entity_max); + } + } + + pipe->partitions = DIV_ROUND_UP(format->width, div_size); + pipe->part_table = kcalloc(pipe->partitions, sizeof(*pipe->part_table), + GFP_KERNEL); + if (!pipe->part_table) + return -ENOMEM; + + for (i = 0; i < pipe->partitions; ++i) + vsp1_pipeline_calculate_partition(pipe, &pipe->part_table[i], + div_size, i); + + return 0; +} + static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe) { struct vsp1_entity *entity; @@ -826,7 +762,7 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe) list_for_each_entry(entity, &pipe->entities, list_pipe) { vsp1_entity_route_setup(entity, pipe, pipe->stream_config); - vsp1_entity_configure_stream(entity, pipe, NULL, + vsp1_entity_configure_stream(entity, entity->state, pipe, NULL, pipe->stream_config); } @@ -937,8 +873,6 @@ static const struct vb2_ops vsp1_video_queue_qops = { .queue_setup = vsp1_video_queue_setup, .buf_prepare = vsp1_video_buffer_prepare, .buf_queue = vsp1_video_buffer_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .start_streaming = vsp1_video_start_streaming, .stop_streaming = vsp1_video_stop_streaming, }; @@ -1146,6 +1080,27 @@ static const struct v4l2_file_operations vsp1_video_fops = { }; /* ----------------------------------------------------------------------------- + * Media entity operations + */ + +static int vsp1_video_link_validate(struct media_link *link) +{ + /* + * Ideally, link validation should be implemented here instead of + * calling vsp1_video_verify_format() in vsp1_video_streamon() + * manually. That would however break userspace that start one video + * device before configures formats on other video devices in the + * pipeline. This operation is just a no-op to silence the warnings + * from v4l2_subdev_link_validate(). + */ + return 0; +} + +static const struct media_entity_operations vsp1_video_media_ops = { + .link_validate = vsp1_video_link_validate, +}; + +/* ----------------------------------------------------------------------------- * Suspend and Resume */ @@ -1279,6 +1234,7 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1, /* ... and the video node... */ video->video.v4l2_dev = &video->vsp1->v4l2_dev; + video->video.entity.ops = &vsp1_video_media_ops; video->video.fops = &vsp1_video_fops; snprintf(video->video.name, sizeof(video->video.name), "%s %s", rwpf->entity.subdev.name, direction); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c index 9693aeab1cac..f176750ccd98 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c @@ -65,12 +65,10 @@ static int vsp1_wpf_set_rotation(struct vsp1_rwpf *wpf, unsigned int rotation) goto done; } - sink_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.state, - RWPF_PAD_SINK); - source_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.state, - RWPF_PAD_SOURCE); + sink_format = v4l2_subdev_state_get_format(wpf->entity.state, + RWPF_PAD_SINK); + source_format = v4l2_subdev_state_get_format(wpf->entity.state, + RWPF_PAD_SOURCE); mutex_lock(&wpf->entity.lock); @@ -231,6 +229,7 @@ static int wpf_configure_writeback_chain(struct vsp1_rwpf *wpf, } static void wpf_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -245,12 +244,8 @@ static void wpf_configure_stream(struct vsp1_entity *entity, u32 srcrpf = 0; int ret; - sink_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.state, - RWPF_PAD_SINK); - source_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.state, - RWPF_PAD_SOURCE); + sink_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); + source_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE); /* Format */ if (!pipe->lif || wpf->writeback) { @@ -367,13 +362,13 @@ static void wpf_configure_frame(struct vsp1_entity *entity, static void wpf_configure_partition(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + const struct vsp1_partition *partition, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); struct vsp1_device *vsp1 = wpf->entity.vsp1; struct vsp1_rwpf_memory mem = wpf->mem; - const struct v4l2_mbus_framefmt *sink_format; const struct v4l2_pix_format_mplane *format = &wpf->format; const struct vsp1_format_info *fmtinfo = wpf->fmtinfo; unsigned int width; @@ -383,21 +378,13 @@ static void wpf_configure_partition(struct vsp1_entity *entity, unsigned int flip; unsigned int i; - sink_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.state, - RWPF_PAD_SINK); - width = sink_format->width; - height = sink_format->height; - left = 0; - /* - * Cropping. The partition algorithm can split the image into - * multiple slices. + * Cropping. The partition algorithm can split the image into multiple + * slices. */ - if (pipe->partitions > 1) { - width = pipe->partition->wpf.width; - left = pipe->partition->wpf.left; - } + width = partition->wpf.width; + left = partition->wpf.left; + height = partition->wpf.height; vsp1_wpf_write(wpf, dlb, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN | (0 << VI6_WPF_SZCLIP_OFST_SHIFT) | @@ -508,6 +495,7 @@ static void wpf_configure_partition(struct vsp1_entity *entity, } static unsigned int wpf_max_width(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe) { struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); @@ -516,10 +504,11 @@ static unsigned int wpf_max_width(struct vsp1_entity *entity, } static void wpf_partition(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_partition *partition, unsigned int partition_idx, - struct vsp1_partition_window *window) + struct v4l2_rect *window) { partition->wpf = *window; } |