diff options
Diffstat (limited to 'drivers/media')
233 files changed, 9612 insertions, 5964 deletions
diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c index 8bf91b5a7d0e..41a79293ee02 100644 --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -1309,8 +1309,11 @@ static int cec_config_log_addr(struct cec_adapter *adap, * we assume that something is really weird and that it is not a * good idea to try and claim this logical address. */ - if (i == max_retries) + if (i == max_retries) { + dprintk(0, "polling for LA %u failed with tx_status=0x%04x\n", + log_addr, msg.tx_status); return 0; + } /* * Message not acknowledged, so this logical diff --git a/drivers/media/cec/platform/cros-ec/cros-ec-cec.c b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c index 8c8d8fc5e63e..3b583ed4da9d 100644 --- a/drivers/media/cec/platform/cros-ec/cros-ec-cec.c +++ b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c @@ -217,6 +217,10 @@ static const struct cec_dmi_match cec_dmi_match_table[] = { { "Google", "Fizz", "0000:00:02.0", "Port B" }, /* Google Brask */ { "Google", "Brask", "0000:00:02.0", "Port B" }, + /* Google Moli */ + { "Google", "Moli", "0000:00:02.0", "Port B" }, + /* Google Kinox */ + { "Google", "Kinox", "0000:00:02.0", "Port B" }, }; static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev, diff --git a/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c index 7607b516a7c4..9b7bcdce6e44 100644 --- a/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c +++ b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c @@ -266,6 +266,8 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc) case V4L2_PIX_FMT_XYUV32: case V4L2_PIX_FMT_VUYA32: case V4L2_PIX_FMT_VUYX32: + case V4L2_PIX_FMT_YUVA32: + case V4L2_PIX_FMT_YUVX32: tpg->color_enc = TGP_COLOR_ENC_YCBCR; break; case V4L2_PIX_FMT_YUV420M: @@ -412,6 +414,8 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc) case V4L2_PIX_FMT_XYUV32: case V4L2_PIX_FMT_VUYA32: case V4L2_PIX_FMT_VUYX32: + case V4L2_PIX_FMT_YUVA32: + case V4L2_PIX_FMT_YUVX32: case V4L2_PIX_FMT_HSV32: tpg->twopixelsize[0] = 2 * 4; break; @@ -1376,9 +1380,11 @@ static void gen_twopix(struct tpg_data *tpg, buf[0][offset + 3] = b_v; break; case V4L2_PIX_FMT_RGBX32: + case V4L2_PIX_FMT_YUVX32: alpha = 0; fallthrough; case V4L2_PIX_FMT_RGBA32: + case V4L2_PIX_FMT_YUVA32: buf[0][offset] = r_y_h; buf[0][offset + 1] = g_u_s; buf[0][offset + 2] = b_v; @@ -2402,6 +2408,44 @@ static void tpg_fill_plane_extras(const struct tpg_data *tpg, ((params->sav_eav_f ^ vact) << 1) | (hact ^ vact ^ params->sav_eav_f); } + if (tpg->insert_hdmi_video_guard_band) { + unsigned int i; + + switch (tpg->fourcc) { + case V4L2_PIX_FMT_BGR24: + case V4L2_PIX_FMT_RGB24: + for (i = 0; i < 3 * 4; i += 3) { + vbuf[i] = 0xab; + vbuf[i + 1] = 0x55; + vbuf[i + 2] = 0xab; + } + break; + case V4L2_PIX_FMT_RGB32: + case V4L2_PIX_FMT_ARGB32: + case V4L2_PIX_FMT_XRGB32: + case V4L2_PIX_FMT_BGRX32: + case V4L2_PIX_FMT_BGRA32: + for (i = 0; i < 4 * 4; i += 4) { + vbuf[i] = 0x00; + vbuf[i + 1] = 0xab; + vbuf[i + 2] = 0x55; + vbuf[i + 3] = 0xab; + } + break; + case V4L2_PIX_FMT_BGR32: + case V4L2_PIX_FMT_XBGR32: + case V4L2_PIX_FMT_ABGR32: + case V4L2_PIX_FMT_RGBX32: + case V4L2_PIX_FMT_RGBA32: + for (i = 0; i < 4 * 4; i += 4) { + vbuf[i] = 0xab; + vbuf[i + 1] = 0x55; + vbuf[i + 2] = 0xab; + vbuf[i + 3] = 0x00; + } + break; + } + } } static void tpg_fill_plane_pattern(const struct tpg_data *tpg, diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index 075d24ebf44c..f26cb8586bd4 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -638,6 +638,18 @@ int vb2_find_timestamp(const struct vb2_queue *q, u64 timestamp, } EXPORT_SYMBOL_GPL(vb2_find_timestamp); +struct vb2_buffer *vb2_find_buffer(struct vb2_queue *q, u64 timestamp) +{ + unsigned int i; + + for (i = 0; i < q->num_buffers; i++) + if (q->bufs[i]->copied_timestamp && + q->bufs[i]->timestamp == timestamp) + return vb2_get_buffer(q, i); + return NULL; +} +EXPORT_SYMBOL_GPL(vb2_find_buffer); + /* * vb2_querybuf() - query video buffer information * @q: videobuf queue diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 2b20aa6c37b1..7806d4b81716 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -34,6 +34,19 @@ config VIDEO_APTINA_PLL config VIDEO_CCS_PLL tristate +config VIDEO_AR0521 + tristate "ON Semiconductor AR0521 sensor support" + depends on I2C && VIDEO_DEV + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the ON Semiconductor + AR0521 camera. + + To compile this driver as a module, choose M here: the + module will be called ar0521. + config VIDEO_HI556 tristate "Hynix Hi-556 sensor support" depends on I2C && VIDEO_DEV @@ -75,8 +88,10 @@ config VIDEO_HI847 config VIDEO_IMX208 tristate "Sony IMX208 sensor support" - depends on I2C && VIDEO_DEV && VIDEO_V4L2_SUBDEV_API + depends on I2C && VIDEO_DEV depends on MEDIA_CAMERA_SUPPORT + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API help This is a Video4Linux2 sensor driver for the Sony IMX208 camera. @@ -1178,6 +1193,7 @@ config VIDEO_ISL7998X depends on OF_GPIO select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE help Support for Intersil ISL7998x analog to MIPI-CSI2 or BT.656 decoder. diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 3e1696963e7f..0a2933103dd9 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_VIDEO_ADV7842) += adv7842.o obj-$(CONFIG_VIDEO_AK7375) += ak7375.o obj-$(CONFIG_VIDEO_AK881X) += ak881x.o obj-$(CONFIG_VIDEO_APTINA_PLL) += aptina-pll.o +obj-$(CONFIG_VIDEO_AR0521) += ar0521.o obj-$(CONFIG_VIDEO_BT819) += bt819.o obj-$(CONFIG_VIDEO_BT856) += bt856.o obj-$(CONFIG_VIDEO_BT866) += bt866.o diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index e3a57c178c6b..5fde5243722d 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -43,6 +43,7 @@ #define ADV7180_INPUT_CONTROL_INSEL_MASK 0x0f #define ADV7182_REG_INPUT_VIDSEL 0x0002 +#define ADV7182_REG_INPUT_RESERVED BIT(2) #define ADV7180_REG_OUTPUT_CONTROL 0x0003 #define ADV7180_REG_EXTENDED_OUTPUT_CONTROL 0x0004 @@ -1060,7 +1061,9 @@ static int adv7182_init(struct adv7180_state *state) static int adv7182_set_std(struct adv7180_state *state, unsigned int std) { - return adv7180_write(state, ADV7182_REG_INPUT_VIDSEL, std << 4); + /* Failing to set the reserved bit can result in increased video noise */ + return adv7180_write(state, ADV7182_REG_INPUT_VIDSEL, + (std << 4) | ADV7182_REG_INPUT_RESERVED); } enum adv7182_input_type { diff --git a/drivers/media/i2c/adv7343_regs.h b/drivers/media/i2c/adv7343_regs.h index 2f04ce4b9118..e0357e6272e3 100644 --- a/drivers/media/i2c/adv7343_regs.h +++ b/drivers/media/i2c/adv7343_regs.h @@ -1,16 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * ADV7343 encoder related structure and register definitions * * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed .as is. WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef ADV7343_REGS_H diff --git a/drivers/media/i2c/adv7393_regs.h b/drivers/media/i2c/adv7393_regs.h index 78968330f0be..6eb8732b5324 100644 --- a/drivers/media/i2c/adv7393_regs.h +++ b/drivers/media/i2c/adv7393_regs.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * ADV7393 encoder related structure and register definitions * @@ -7,15 +8,6 @@ * Based on ADV7343 driver, * * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed .as is. WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef ADV7393_REGS_H diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h index 31bac06d46b5..d75eb3d8be5a 100644 --- a/drivers/media/i2c/adv748x/adv748x.h +++ b/drivers/media/i2c/adv748x/adv748x.h @@ -417,7 +417,7 @@ int adv748x_write_block(struct adv748x_state *state, int client_page, static inline struct v4l2_subdev *adv748x_get_remote_sd(struct media_pad *pad) { - pad = media_entity_remote_pad(pad); + pad = media_pad_remote_pad_first(pad); if (!pad) return NULL; diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index bb0c8fc6d383..497419a5cfdd 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -2505,9 +2505,8 @@ static void adv76xx_log_infoframes(struct v4l2_subdev *sd) union hdmi_infoframe frame; struct i2c_client *client = v4l2_get_subdevdata(sd); - if (adv76xx_read_infoframe(sd, i, &frame)) - return; - hdmi_infoframe_log(KERN_INFO, &client->dev, &frame); + if (!adv76xx_read_infoframe(sd, i, &frame)) + hdmi_infoframe_log(KERN_INFO, &client->dev, &frame); } } diff --git a/drivers/media/i2c/ar0521.c b/drivers/media/i2c/ar0521.c new file mode 100644 index 000000000000..c7bdfc69b9be --- /dev/null +++ b/drivers/media/i2c/ar0521.c @@ -0,0 +1,1061 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Sieć Badawcza Łukasiewicz + * - Przemysłowy Instytut Automatyki i Pomiarów PIAP + * Written by Krzysztof Hałasa + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/pm_runtime.h> + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> + +/* External clock (extclk) frequencies */ +#define AR0521_EXTCLK_MIN (10 * 1000 * 1000) +#define AR0521_EXTCLK_MAX (48 * 1000 * 1000) + +/* PLL and PLL2 */ +#define AR0521_PLL_MIN (320 * 1000 * 1000) +#define AR0521_PLL_MAX (1280 * 1000 * 1000) + +/* Effective pixel clocks, the registers may be DDR */ +#define AR0521_PIXEL_CLOCK_RATE (184 * 1000 * 1000) +#define AR0521_PIXEL_CLOCK_MIN (168 * 1000 * 1000) +#define AR0521_PIXEL_CLOCK_MAX (414 * 1000 * 1000) + +#define AR0521_WIDTH_MIN 8u +#define AR0521_WIDTH_MAX 2608u +#define AR0521_HEIGHT_MIN 8u +#define AR0521_HEIGHT_MAX 1958u + +#define AR0521_WIDTH_BLANKING_MIN 572u +#define AR0521_HEIGHT_BLANKING_MIN 38u /* must be even */ +#define AR0521_TOTAL_WIDTH_MIN 2968u + +/* AR0521 registers */ +#define AR0521_REG_VT_PIX_CLK_DIV 0x0300 +#define AR0521_REG_FRAME_LENGTH_LINES 0x0340 + +#define AR0521_REG_CHIP_ID 0x3000 +#define AR0521_REG_COARSE_INTEGRATION_TIME 0x3012 +#define AR0521_REG_ROW_SPEED 0x3016 +#define AR0521_REG_EXTRA_DELAY 0x3018 +#define AR0521_REG_RESET 0x301A +#define AR0521_REG_RESET_DEFAULTS 0x0238 +#define AR0521_REG_RESET_GROUP_PARAM_HOLD 0x8000 +#define AR0521_REG_RESET_STREAM BIT(2) +#define AR0521_REG_RESET_RESTART BIT(1) +#define AR0521_REG_RESET_INIT BIT(0) + +#define AR0521_REG_GREEN1_GAIN 0x3056 +#define AR0521_REG_BLUE_GAIN 0x3058 +#define AR0521_REG_RED_GAIN 0x305A +#define AR0521_REG_GREEN2_GAIN 0x305C +#define AR0521_REG_GLOBAL_GAIN 0x305E + +#define AR0521_REG_HISPI_TEST_MODE 0x3066 +#define AR0521_REG_HISPI_TEST_MODE_LP11 0x0004 + +#define AR0521_REG_TEST_PATTERN_MODE 0x3070 + +#define AR0521_REG_SERIAL_FORMAT 0x31AE +#define AR0521_REG_SERIAL_FORMAT_MIPI 0x0200 + +#define AR0521_REG_HISPI_CONTROL_STATUS 0x31C6 +#define AR0521_REG_HISPI_CONTROL_STATUS_FRAMER_TEST_MODE_ENABLE 0x80 + +#define be cpu_to_be16 + +static const char * const ar0521_supply_names[] = { + "vdd_io", /* I/O (1.8V) supply */ + "vdd", /* Core, PLL and MIPI (1.2V) supply */ + "vaa", /* Analog (2.7V) supply */ +}; + +struct ar0521_ctrls { + struct v4l2_ctrl_handler handler; + struct { + struct v4l2_ctrl *gain; + struct v4l2_ctrl *red_balance; + struct v4l2_ctrl *blue_balance; + }; + struct { + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + }; + struct v4l2_ctrl *pixrate; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *test_pattern; +}; + +struct ar0521_dev { + struct i2c_client *i2c_client; + struct v4l2_subdev sd; + struct media_pad pad; + struct clk *extclk; + u32 extclk_freq; + + struct regulator *supplies[ARRAY_SIZE(ar0521_supply_names)]; + struct gpio_desc *reset_gpio; + + /* lock to protect all members below */ + struct mutex lock; + + struct v4l2_mbus_framefmt fmt; + struct ar0521_ctrls ctrls; + unsigned int lane_count; + u16 total_width; + u16 total_height; + u16 pll_pre; + u16 pll_mult; + u16 pll_pre2; + u16 pll_mult2; + bool streaming; +}; + +static inline struct ar0521_dev *to_ar0521_dev(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ar0521_dev, sd); +} + +static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct ar0521_dev, + ctrls.handler)->sd; +} + +static u32 div64_round(u64 v, u32 d) +{ + return div_u64(v + (d >> 1), d); +} + +static u32 div64_round_up(u64 v, u32 d) +{ + return div_u64(v + d - 1, d); +} + +/* Data must be BE16, the first value is the register address */ +static int ar0521_write_regs(struct ar0521_dev *sensor, const __be16 *data, + unsigned int count) +{ + struct i2c_client *client = sensor->i2c_client; + struct i2c_msg msg; + int ret; + + msg.addr = client->addr; + msg.flags = client->flags; + msg.buf = (u8 *)data; + msg.len = count * sizeof(*data); + + ret = i2c_transfer(client->adapter, &msg, 1); + + if (ret < 0) { + v4l2_err(&sensor->sd, "%s: I2C write error\n", __func__); + return ret; + } + + return 0; +} + +static int ar0521_write_reg(struct ar0521_dev *sensor, u16 reg, u16 val) +{ + __be16 buf[2] = {be(reg), be(val)}; + + return ar0521_write_regs(sensor, buf, 2); +} + +static int ar0521_set_geometry(struct ar0521_dev *sensor) +{ + /* All dimensions are unsigned 12-bit integers */ + u16 x = (AR0521_WIDTH_MAX - sensor->fmt.width) / 2; + u16 y = ((AR0521_HEIGHT_MAX - sensor->fmt.height) / 2) & ~1; + __be16 regs[] = { + be(AR0521_REG_FRAME_LENGTH_LINES), + be(sensor->total_height), + be(sensor->total_width), + be(x), + be(y), + be(x + sensor->fmt.width - 1), + be(y + sensor->fmt.height - 1), + be(sensor->fmt.width), + be(sensor->fmt.height) + }; + + return ar0521_write_regs(sensor, regs, ARRAY_SIZE(regs)); +} + +static int ar0521_set_gains(struct ar0521_dev *sensor) +{ + int green = sensor->ctrls.gain->val; + int red = max(green + sensor->ctrls.red_balance->val, 0); + int blue = max(green + sensor->ctrls.blue_balance->val, 0); + unsigned int gain = min(red, min(green, blue)); + unsigned int analog = min(gain, 64u); /* range is 0 - 127 */ + __be16 regs[5]; + + red = min(red - analog + 64, 511u); + green = min(green - analog + 64, 511u); + blue = min(blue - analog + 64, 511u); + regs[0] = be(AR0521_REG_GREEN1_GAIN); + regs[1] = be(green << 7 | analog); + regs[2] = be(blue << 7 | analog); + regs[3] = be(red << 7 | analog); + regs[4] = be(green << 7 | analog); + + return ar0521_write_regs(sensor, regs, ARRAY_SIZE(regs)); +} + +static u32 calc_pll(struct ar0521_dev *sensor, int num, u32 freq, u16 *pre_ptr, + u16 *mult_ptr) +{ + u16 pre = 1, mult = 1, new_pre; + u32 pll = AR0521_PLL_MAX + 1; + + for (new_pre = 1; new_pre < 64; new_pre++) { + u32 new_pll; + u32 new_mult = div64_round_up((u64)freq * new_pre, + sensor->extclk_freq); + + if (new_mult < 32) + continue; /* Minimum value */ + if (new_mult > 254) + break; /* Maximum, larger pre won't work either */ + if (sensor->extclk_freq * (u64)new_mult < AR0521_PLL_MIN * + new_pre) + continue; + if (sensor->extclk_freq * (u64)new_mult > AR0521_PLL_MAX * + new_pre) + break; /* Larger pre won't work either */ + new_pll = div64_round_up(sensor->extclk_freq * (u64)new_mult, + new_pre); + if (new_pll < pll) { + pll = new_pll; + pre = new_pre; + mult = new_mult; + } + } + + pll = div64_round(sensor->extclk_freq * (u64)mult, pre); + *pre_ptr = pre; + *mult_ptr = mult; + return pll; +} + +#define DIV 4 +static void ar0521_calc_mode(struct ar0521_dev *sensor) +{ + unsigned int speed_mod = 4 / sensor->lane_count; /* 1 with 4 DDR lanes */ + u16 total_width = max(sensor->fmt.width + AR0521_WIDTH_BLANKING_MIN, + AR0521_TOTAL_WIDTH_MIN); + u16 total_height = sensor->fmt.height + AR0521_HEIGHT_BLANKING_MIN; + + /* Calculate approximate pixel clock first */ + u64 pix_clk = AR0521_PIXEL_CLOCK_RATE; + + /* PLL1 drives pixel clock - dual rate */ + pix_clk = calc_pll(sensor, 1, pix_clk * (DIV / 2), &sensor->pll_pre, + &sensor->pll_mult); + pix_clk = div64_round(pix_clk, (DIV / 2)); + calc_pll(sensor, 2, pix_clk * (DIV / 2) * speed_mod, &sensor->pll_pre2, + &sensor->pll_mult2); + + sensor->total_width = total_width; + sensor->total_height = total_height; +} + +static int ar0521_write_mode(struct ar0521_dev *sensor) +{ + __be16 pll_regs[] = { + be(AR0521_REG_VT_PIX_CLK_DIV), + /* 0x300 */ be(4), /* vt_pix_clk_div = number of bits / 2 */ + /* 0x302 */ be(1), /* vt_sys_clk_div */ + /* 0x304 */ be((sensor->pll_pre2 << 8) | sensor->pll_pre), + /* 0x306 */ be((sensor->pll_mult2 << 8) | sensor->pll_mult), + /* 0x308 */ be(8), /* op_pix_clk_div = 2 * vt_pix_clk_div */ + /* 0x30A */ be(1) /* op_sys_clk_div */ + }; + int ret; + + /* Stop streaming for just a moment */ + ret = ar0521_write_reg(sensor, AR0521_REG_RESET, + AR0521_REG_RESET_DEFAULTS); + if (ret) + return ret; + + ret = ar0521_set_geometry(sensor); + if (ret) + return ret; + + ret = ar0521_write_regs(sensor, pll_regs, ARRAY_SIZE(pll_regs)); + if (ret) + return ret; + + ret = ar0521_write_reg(sensor, AR0521_REG_COARSE_INTEGRATION_TIME, + sensor->ctrls.exposure->val); + if (ret) + return ret; + + ret = ar0521_write_reg(sensor, AR0521_REG_RESET, + AR0521_REG_RESET_DEFAULTS | + AR0521_REG_RESET_STREAM); + if (ret) + return ret; + + ret = ar0521_write_reg(sensor, AR0521_REG_TEST_PATTERN_MODE, + sensor->ctrls.test_pattern->val); + return ret; +} + +static int ar0521_set_stream(struct ar0521_dev *sensor, bool on) +{ + int ret; + + if (on) { + ret = pm_runtime_resume_and_get(&sensor->i2c_client->dev); + if (ret < 0) + return ret; + + ar0521_calc_mode(sensor); + ret = ar0521_write_mode(sensor); + if (ret) + goto err; + + ret = ar0521_set_gains(sensor); + if (ret) + goto err; + + /* Exit LP-11 mode on clock and data lanes */ + ret = ar0521_write_reg(sensor, AR0521_REG_HISPI_CONTROL_STATUS, + 0); + if (ret) + goto err; + + /* Start streaming */ + ret = ar0521_write_reg(sensor, AR0521_REG_RESET, + AR0521_REG_RESET_DEFAULTS | + AR0521_REG_RESET_STREAM); + if (ret) + goto err; + + return 0; + +err: + pm_runtime_put(&sensor->i2c_client->dev); + return ret; + + } else { + /* + * Reset gain, the sensor may produce all white pixels without + * this + */ + ret = ar0521_write_reg(sensor, AR0521_REG_GLOBAL_GAIN, 0x2000); + if (ret) + return ret; + + /* Stop streaming */ + ret = ar0521_write_reg(sensor, AR0521_REG_RESET, + AR0521_REG_RESET_DEFAULTS); + if (ret) + return ret; + + pm_runtime_put(&sensor->i2c_client->dev); + return 0; + } +} + +static void ar0521_adj_fmt(struct v4l2_mbus_framefmt *fmt) +{ + fmt->width = clamp(ALIGN(fmt->width, 4), AR0521_WIDTH_MIN, + AR0521_WIDTH_MAX); + fmt->height = clamp(ALIGN(fmt->height, 4), AR0521_HEIGHT_MIN, + AR0521_HEIGHT_MAX); + fmt->code = MEDIA_BUS_FMT_SGRBG8_1X8; + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; +} + +static int ar0521_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *format) +{ + struct ar0521_dev *sensor = to_ar0521_dev(sd); + struct v4l2_mbus_framefmt *fmt; + + mutex_lock(&sensor->lock); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + fmt = v4l2_subdev_get_try_format(&sensor->sd, sd_state, 0 + /* pad */); + else + fmt = &sensor->fmt; + + format->format = *fmt; + + mutex_unlock(&sensor->lock); + return 0; +} + +static int ar0521_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *format) +{ + struct ar0521_dev *sensor = to_ar0521_dev(sd); + int ret = 0; + + ar0521_adj_fmt(&format->format); + + mutex_lock(&sensor->lock); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_get_try_format(sd, sd_state, 0 /* pad */); + *fmt = format->format; + } else { + sensor->fmt = format->format; + ar0521_calc_mode(sensor); + } + + mutex_unlock(&sensor->lock); + return ret; +} + +static int ar0521_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = ctrl_to_sd(ctrl); + struct ar0521_dev *sensor = to_ar0521_dev(sd); + int ret; + + /* v4l2_ctrl_lock() locks our own mutex */ + + switch (ctrl->id) { + case V4L2_CID_HBLANK: + case V4L2_CID_VBLANK: + sensor->total_width = sensor->fmt.width + + sensor->ctrls.hblank->val; + sensor->total_height = sensor->fmt.width + + sensor->ctrls.vblank->val; + break; + default: + ret = -EINVAL; + break; + } + + /* access the sensor only if it's powered up */ + if (!pm_runtime_get_if_in_use(&sensor->i2c_client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_HBLANK: + case V4L2_CID_VBLANK: + ret = ar0521_set_geometry(sensor); + break; + case V4L2_CID_GAIN: + case V4L2_CID_RED_BALANCE: + case V4L2_CID_BLUE_BALANCE: + ret = ar0521_set_gains(sensor); + break; + case V4L2_CID_EXPOSURE: + ret = ar0521_write_reg(sensor, + AR0521_REG_COARSE_INTEGRATION_TIME, + ctrl->val); + break; + case V4L2_CID_TEST_PATTERN: + ret = ar0521_write_reg(sensor, AR0521_REG_TEST_PATTERN_MODE, + ctrl->val); + break; + } + + pm_runtime_put(&sensor->i2c_client->dev); + return ret; +} + +static const struct v4l2_ctrl_ops ar0521_ctrl_ops = { + .s_ctrl = ar0521_s_ctrl, +}; + +static const char * const test_pattern_menu[] = { + "Disabled", + "Solid color", + "Color bars", + "Faded color bars" +}; + +static int ar0521_init_controls(struct ar0521_dev *sensor) +{ + const struct v4l2_ctrl_ops *ops = &ar0521_ctrl_ops; + struct ar0521_ctrls *ctrls = &sensor->ctrls; + struct v4l2_ctrl_handler *hdl = &ctrls->handler; + int ret; + + v4l2_ctrl_handler_init(hdl, 32); + + /* We can use our own mutex for the ctrl lock */ + hdl->lock = &sensor->lock; + + /* Manual gain */ + ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN, 0, 511, 1, 0); + ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE, + -512, 511, 1, 0); + ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BLUE_BALANCE, + -512, 511, 1, 0); + v4l2_ctrl_cluster(3, &ctrls->gain); + + ctrls->hblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK, + AR0521_WIDTH_BLANKING_MIN, 4094, 1, + AR0521_WIDTH_BLANKING_MIN); + ctrls->vblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK, + AR0521_HEIGHT_BLANKING_MIN, 4094, 2, + AR0521_HEIGHT_BLANKING_MIN); + v4l2_ctrl_cluster(2, &ctrls->hblank); + + /* Read-only */ + ctrls->pixrate = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_PIXEL_RATE, + AR0521_PIXEL_CLOCK_MIN, + AR0521_PIXEL_CLOCK_MAX, 1, + AR0521_PIXEL_CLOCK_RATE); + + /* Manual exposure time */ + ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, 0, + 65535, 1, 360); + + ctrls->test_pattern = v4l2_ctrl_new_std_menu_items(hdl, ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(test_pattern_menu) - 1, + 0, 0, test_pattern_menu); + + if (hdl->error) { + ret = hdl->error; + goto free_ctrls; + } + + sensor->sd.ctrl_handler = hdl; + return 0; + +free_ctrls: + v4l2_ctrl_handler_free(hdl); + return ret; +} + +#define REGS_ENTRY(a) {(a), ARRAY_SIZE(a)} +#define REGS(...) REGS_ENTRY(((const __be16[]){__VA_ARGS__})) + +static const struct initial_reg { + const __be16 *data; /* data[0] is register address */ + unsigned int count; +} initial_regs[] = { + REGS(be(0x0112), be(0x0808)), /* 8-bit/8-bit mode */ + + /* PEDESTAL+2 :+2 is a workaround for 10bit mode +0.5 rounding */ + REGS(be(0x301E), be(0x00AA)), + + /* corrections_recommended_bayer */ + REGS(be(0x3042), + be(0x0004), /* 3042: RNC: enable b/w rnc mode */ + be(0x4580)), /* 3044: RNC: enable row noise correction */ + + REGS(be(0x30D2), + be(0x0000), /* 30D2: CRM/CC: enable crm on Visible and CC rows */ + be(0x0000), /* 30D4: CC: CC enabled with 16 samples per column */ + /* 30D6: CC: bw mode enabled/12 bit data resolution/bw mode */ + be(0x2FFF)), + + REGS(be(0x30DA), + be(0x0FFF), /* 30DA: CC: column correction clip level 2 is 0 */ + be(0x0FFF), /* 30DC: CC: column correction clip level 3 is 0 */ + be(0x0000)), /* 30DE: CC: Group FPN correction */ + + /* RNC: rnc scaling factor = * 54 / 64 (32 / 38 * 64 = 53.9) */ + REGS(be(0x30EE), be(0x1136)), + REGS(be(0x30FA), be(0xFD00)), /* GPIO0 = flash, GPIO1 = shutter */ + REGS(be(0x3120), be(0x0005)), /* p1 dither enabled for 10bit mode */ + REGS(be(0x3172), be(0x0206)), /* txlo clk divider options */ + /* FDOC:fdoc settings with fdoc every frame turned of */ + REGS(be(0x3180), be(0x9434)), + + REGS(be(0x31B0), + be(0x008B), /* 31B0: frame_preamble - FIXME check WRT lanes# */ + be(0x0050)), /* 31B2: line_preamble - FIXME check WRT lanes# */ + + /* don't use continuous clock mode while shut down */ + REGS(be(0x31BC), be(0x068C)), + REGS(be(0x31E0), be(0x0781)), /* Fuse/2DDC: enable 2ddc */ + + /* analog_setup_recommended_10bit */ + REGS(be(0x341A), be(0x4735)), /* Samp&Hold pulse in ADC */ + REGS(be(0x3420), be(0x4735)), /* Samp&Hold pulse in ADC */ + REGS(be(0x3426), be(0x8A1A)), /* ADC offset distribution pulse */ + REGS(be(0x342A), be(0x0018)), /* pulse_config */ + + /* pixel_timing_recommended */ + REGS(be(0x3D00), + /* 3D00 */ be(0x043E), be(0x4760), be(0xFFFF), be(0xFFFF), + /* 3D08 */ be(0x8000), be(0x0510), be(0xAF08), be(0x0252), + /* 3D10 */ be(0x486F), be(0x5D5D), be(0x8056), be(0x8313), + /* 3D18 */ be(0x0087), be(0x6A48), be(0x6982), be(0x0280), + /* 3D20 */ be(0x8359), be(0x8D02), be(0x8020), be(0x4882), + /* 3D28 */ be(0x4269), be(0x6A95), be(0x5988), be(0x5A83), + /* 3D30 */ be(0x5885), be(0x6280), be(0x6289), be(0x6097), + /* 3D38 */ be(0x5782), be(0x605C), be(0xBF18), be(0x0961), + /* 3D40 */ be(0x5080), be(0x2090), be(0x4390), be(0x4382), + /* 3D48 */ be(0x5F8A), be(0x5D5D), be(0x9C63), be(0x8063), + /* 3D50 */ be(0xA960), be(0x9757), be(0x8260), be(0x5CFF), + /* 3D58 */ be(0xBF10), be(0x1681), be(0x0802), be(0x8000), + /* 3D60 */ be(0x141C), be(0x6000), be(0x6022), be(0x4D80), + /* 3D68 */ be(0x5C97), be(0x6A69), be(0xAC6F), be(0x4645), + /* 3D70 */ be(0x4400), be(0x0513), be(0x8069), be(0x6AC6), + /* 3D78 */ be(0x5F95), be(0x5F70), be(0x8040), be(0x4A81), + /* 3D80 */ be(0x0300), be(0xE703), be(0x0088), be(0x4A83), + /* 3D88 */ be(0x40FF), be(0xFFFF), be(0xFD70), be(0x8040), + /* 3D90 */ be(0x4A85), be(0x4FA8), be(0x4F8C), be(0x0070), + /* 3D98 */ be(0xBE47), be(0x8847), be(0xBC78), be(0x6B89), + /* 3DA0 */ be(0x6A80), be(0x6986), be(0x6B8E), be(0x6B80), + /* 3DA8 */ be(0x6980), be(0x6A88), be(0x7C9F), be(0x866B), + /* 3DB0 */ be(0x8765), be(0x46FF), be(0xE365), be(0xA679), + /* 3DB8 */ be(0x4A40), be(0x4580), be(0x44BC), be(0x7000), + /* 3DC0 */ be(0x8040), be(0x0802), be(0x10EF), be(0x0104), + /* 3DC8 */ be(0x3860), be(0x5D5D), be(0x5682), be(0x1300), + /* 3DD0 */ be(0x8648), be(0x8202), be(0x8082), be(0x598A), + /* 3DD8 */ be(0x0280), be(0x2048), be(0x3060), be(0x8042), + /* 3DE0 */ be(0x9259), be(0x865A), be(0x8258), be(0x8562), + /* 3DE8 */ be(0x8062), be(0x8560), be(0x9257), be(0x8221), + /* 3DF0 */ be(0x10FF), be(0xB757), be(0x9361), be(0x1019), + /* 3DF8 */ be(0x8020), be(0x9043), be(0x8E43), be(0x845F), + /* 3E00 */ be(0x835D), be(0x805D), be(0x8163), be(0x8063), + /* 3E08 */ be(0xA060), be(0x9157), be(0x8260), be(0x5CFF), + /* 3E10 */ be(0xFFFF), be(0xFFE5), be(0x1016), be(0x2048), + /* 3E18 */ be(0x0802), be(0x1C60), be(0x0014), be(0x0060), + /* 3E20 */ be(0x2205), be(0x8120), be(0x908F), be(0x6A80), + /* 3E28 */ be(0x6982), be(0x5F9F), be(0x6F46), be(0x4544), + /* 3E30 */ be(0x0005), be(0x8013), be(0x8069), be(0x6A80), + /* 3E38 */ be(0x7000), be(0x0000), be(0x0000), be(0x0000), + /* 3E40 */ be(0x0000), be(0x0000), be(0x0000), be(0x0000), + /* 3E48 */ be(0x0000), be(0x0000), be(0x0000), be(0x0000), + /* 3E50 */ be(0x0000), be(0x0000), be(0x0000), be(0x0000), + /* 3E58 */ be(0x0000), be(0x0000), be(0x0000), be(0x0000), + /* 3E60 */ be(0x0000), be(0x0000), be(0x0000), be(0x0000), + /* 3E68 */ be(0x0000), be(0x0000), be(0x0000), be(0x0000), + /* 3E70 */ be(0x0000), be(0x0000), be(0x0000), be(0x0000), + /* 3E78 */ be(0x0000), be(0x0000), be(0x0000), be(0x0000), + /* 3E80 */ be(0x0000), be(0x0000), be(0x0000), be(0x0000), + /* 3E88 */ be(0x0000), be(0x0000), be(0x0000), be(0x0000), + /* 3E90 */ be(0x0000), be(0x0000), be(0x0000), be(0x0000), + /* 3E98 */ be(0x0000), be(0x0000), be(0x0000), be(0x0000), + /* 3EA0 */ be(0x0000), be(0x0000), be(0x0000), be(0x0000), + /* 3EA8 */ be(0x0000), be(0x0000), be(0x0000), be(0x0000), + /* 3EB0 */ be(0x0000), be(0x0000), be(0x0000)), + + REGS(be(0x3EB6), be(0x004C)), /* ECL */ + + REGS(be(0x3EBA), + be(0xAAAD), /* 3EBA */ + be(0x0086)), /* 3EBC: Bias currents for FSC/ECL */ + + REGS(be(0x3EC0), + be(0x1E00), /* 3EC0: SFbin/SH mode settings */ + be(0x100A), /* 3EC2: CLK divider for ramp for 10 bit 400MH */ + /* 3EC4: FSC clamps for HDR mode and adc comp power down co */ + be(0x3300), + be(0xEA44), /* 3EC6: VLN and clk gating controls */ + be(0x6F6F), /* 3EC8: Txl0 and Txlo1 settings for normal mode */ + be(0x2F4A), /* 3ECA: CDAC/Txlo2/RSTGHI/RSTGLO settings */ + be(0x0506), /* 3ECC: RSTDHI/RSTDLO/CDAC/TXHI settings */ + /* 3ECE: Ramp buffer settings and Booster enable (bits 0-5) */ + be(0x203B), + be(0x13F0), /* 3ED0: TXLO from atest/sf bin settings */ + be(0xA53D), /* 3ED2: Ramp offset */ + be(0x862F), /* 3ED4: TXLO open loop/row driver settings */ + be(0x4081), /* 3ED6: Txlatch fr cfpn rows/vln bias */ + be(0x8003), /* 3ED8: Ramp step setting for 10 bit 400 Mhz */ + be(0xA580), /* 3EDA: Ramp Offset */ + be(0xC000), /* 3EDC: over range for rst and under range for sig */ + be(0xC103)), /* 3EDE: over range for sig and col dec clk settings */ + + /* corrections_recommended_bayer */ + REGS(be(0x3F00), + be(0x0017), /* 3F00: BM_T0 */ + be(0x02DD), /* 3F02: BM_T1 */ + /* 3F04: if Ana_gain less than 2, use noise_floor0, multipl */ + be(0x0020), + /* 3F06: if Ana_gain between 4 and 7, use noise_floor2 and */ + be(0x0040), + /* 3F08: if Ana_gain between 4 and 7, use noise_floor2 and */ + be(0x0070), + /* 3F0A: Define noise_floor0(low address) and noise_floor1 */ + be(0x0101), + be(0x0302)), /* 3F0C: Define noise_floor2 and noise_floor3 */ + + REGS(be(0x3F10), + be(0x0505), /* 3F10: single k factor 0 */ + be(0x0505), /* 3F12: single k factor 1 */ + be(0x0505), /* 3F14: single k factor 2 */ + be(0x01FF), /* 3F16: cross factor 0 */ + be(0x01FF), /* 3F18: cross factor 1 */ + be(0x01FF), /* 3F1A: cross factor 2 */ + be(0x0022)), /* 3F1E */ + + /* GTH_THRES_RTN: 4max,4min filtered out of every 46 samples and */ + REGS(be(0x3F2C), be(0x442E)), + + REGS(be(0x3F3E), + be(0x0000), /* 3F3E: Switch ADC from 12 bit to 10 bit mode */ + be(0x1511), /* 3F40: couple k factor 0 */ + be(0x1511), /* 3F42: couple k factor 1 */ + be(0x0707)), /* 3F44: couple k factor 2 */ +}; + +static int ar0521_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ar0521_dev *sensor = to_ar0521_dev(sd); + int i; + + clk_disable_unprepare(sensor->extclk); + + if (sensor->reset_gpio) + gpiod_set_value(sensor->reset_gpio, 1); /* assert RESET signal */ + + for (i = ARRAY_SIZE(ar0521_supply_names) - 1; i >= 0; i--) { + if (sensor->supplies[i]) + regulator_disable(sensor->supplies[i]); + } + return 0; +} + +static int ar0521_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ar0521_dev *sensor = to_ar0521_dev(sd); + unsigned int cnt; + int ret; + + for (cnt = 0; cnt < ARRAY_SIZE(ar0521_supply_names); cnt++) + if (sensor->supplies[cnt]) { + ret = regulator_enable(sensor->supplies[cnt]); + if (ret < 0) + goto off; + + usleep_range(1000, 1500); /* min 1 ms */ + } + + ret = clk_prepare_enable(sensor->extclk); + if (ret < 0) { + v4l2_err(&sensor->sd, "error enabling sensor clock\n"); + goto off; + } + usleep_range(1000, 1500); /* min 1 ms */ + + if (sensor->reset_gpio) + /* deassert RESET signal */ + gpiod_set_value(sensor->reset_gpio, 0); + usleep_range(4500, 5000); /* min 45000 clocks */ + + for (cnt = 0; cnt < ARRAY_SIZE(initial_regs); cnt++) + if (ar0521_write_regs(sensor, initial_regs[cnt].data, + initial_regs[cnt].count)) + goto off; + + ret = ar0521_write_reg(sensor, AR0521_REG_SERIAL_FORMAT, + AR0521_REG_SERIAL_FORMAT_MIPI | + sensor->lane_count); + if (ret) + goto off; + + /* set MIPI test mode - disabled for now */ + ret = ar0521_write_reg(sensor, AR0521_REG_HISPI_TEST_MODE, + ((0x40 << sensor->lane_count) - 0x40) | + AR0521_REG_HISPI_TEST_MODE_LP11); + if (ret) + goto off; + + ret = ar0521_write_reg(sensor, AR0521_REG_ROW_SPEED, 0x110 | + 4 / sensor->lane_count); + if (ret) + goto off; + + return 0; +off: + ar0521_power_off(dev); + return ret; +} + +static int ar0521_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct ar0521_dev *sensor = to_ar0521_dev(sd); + + if (code->index) + return -EINVAL; + + code->code = sensor->fmt.code; + return 0; +} + +static int ar0521_pre_streamon(struct v4l2_subdev *sd, u32 flags) +{ + struct ar0521_dev *sensor = to_ar0521_dev(sd); + int ret; + + if (!(flags & V4L2_SUBDEV_PRE_STREAMON_FL_MANUAL_LP)) + return -EACCES; + + ret = pm_runtime_resume_and_get(&sensor->i2c_client->dev); + if (ret < 0) + return ret; + + /* Set LP-11 on clock and data lanes */ + ret = ar0521_write_reg(sensor, AR0521_REG_HISPI_CONTROL_STATUS, + AR0521_REG_HISPI_CONTROL_STATUS_FRAMER_TEST_MODE_ENABLE); + if (ret) + goto err; + + /* Start streaming LP-11 */ + ret = ar0521_write_reg(sensor, AR0521_REG_RESET, + AR0521_REG_RESET_DEFAULTS | + AR0521_REG_RESET_STREAM); + if (ret) + goto err; + return 0; + +err: + pm_runtime_put(&sensor->i2c_client->dev); + return ret; +} + +static int ar0521_post_streamoff(struct v4l2_subdev *sd) +{ + struct ar0521_dev *sensor = to_ar0521_dev(sd); + + pm_runtime_put(&sensor->i2c_client->dev); + return 0; +} + +static int ar0521_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct ar0521_dev *sensor = to_ar0521_dev(sd); + int ret; + + mutex_lock(&sensor->lock); + + ret = ar0521_set_stream(sensor, enable); + if (!ret) + sensor->streaming = enable; + + mutex_unlock(&sensor->lock); + return ret; +} + +static const struct v4l2_subdev_core_ops ar0521_core_ops = { + .log_status = v4l2_ctrl_subdev_log_status, +}; + +static const struct v4l2_subdev_video_ops ar0521_video_ops = { + .s_stream = ar0521_s_stream, + .pre_streamon = ar0521_pre_streamon, + .post_streamoff = ar0521_post_streamoff, +}; + +static const struct v4l2_subdev_pad_ops ar0521_pad_ops = { + .enum_mbus_code = ar0521_enum_mbus_code, + .get_fmt = ar0521_get_fmt, + .set_fmt = ar0521_set_fmt, +}; + +static const struct v4l2_subdev_ops ar0521_subdev_ops = { + .core = &ar0521_core_ops, + .video = &ar0521_video_ops, + .pad = &ar0521_pad_ops, +}; + +static int __maybe_unused ar0521_suspend(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ar0521_dev *sensor = to_ar0521_dev(sd); + + if (sensor->streaming) + ar0521_set_stream(sensor, 0); + + return 0; +} + +static int __maybe_unused ar0521_resume(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ar0521_dev *sensor = to_ar0521_dev(sd); + + if (sensor->streaming) + return ar0521_set_stream(sensor, 1); + + return 0; +} + +static int ar0521_probe(struct i2c_client *client) +{ + struct v4l2_fwnode_endpoint ep = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + struct device *dev = &client->dev; + struct fwnode_handle *endpoint; + struct ar0521_dev *sensor; + unsigned int cnt; + int ret; + + sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return -ENOMEM; + + sensor->i2c_client = client; + sensor->fmt.width = AR0521_WIDTH_MAX; + sensor->fmt.height = AR0521_HEIGHT_MAX; + + endpoint = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!endpoint) { + dev_err(dev, "endpoint node not found\n"); + return -EINVAL; + } + + ret = v4l2_fwnode_endpoint_parse(endpoint, &ep); + fwnode_handle_put(endpoint); + if (ret) { + dev_err(dev, "could not parse endpoint\n"); + return ret; + } + + if (ep.bus_type != V4L2_MBUS_CSI2_DPHY) { + dev_err(dev, "invalid bus type, must be MIPI CSI2\n"); + return -EINVAL; + } + + sensor->lane_count = ep.bus.mipi_csi2.num_data_lanes; + switch (sensor->lane_count) { + case 1: + case 2: + case 4: + break; + default: + dev_err(dev, "invalid number of MIPI data lanes\n"); + return -EINVAL; + } + + /* Get master clock (extclk) */ + sensor->extclk = devm_clk_get(dev, "extclk"); + if (IS_ERR(sensor->extclk)) { + dev_err(dev, "failed to get extclk\n"); + return PTR_ERR(sensor->extclk); + } + + sensor->extclk_freq = clk_get_rate(sensor->extclk); + + if (sensor->extclk_freq < AR0521_EXTCLK_MIN || + sensor->extclk_freq > AR0521_EXTCLK_MAX) { + dev_err(dev, "extclk frequency out of range: %u Hz\n", + sensor->extclk_freq); + return -EINVAL; + } + + /* Request optional reset pin (usually active low) and assert it */ + sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + + v4l2_i2c_subdev_init(&sensor->sd, client, &ar0521_subdev_ops); + + sensor->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + sensor->pad.flags = MEDIA_PAD_FL_SOURCE; + sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad); + if (ret) + return ret; + + for (cnt = 0; cnt < ARRAY_SIZE(ar0521_supply_names); cnt++) { + struct regulator *supply = devm_regulator_get(dev, + ar0521_supply_names[cnt]); + + if (IS_ERR(supply)) { + dev_info(dev, "no %s regulator found: %li\n", + ar0521_supply_names[cnt], PTR_ERR(supply)); + return PTR_ERR(supply); + } + sensor->supplies[cnt] = supply; + } + + mutex_init(&sensor->lock); + + ret = ar0521_init_controls(sensor); + if (ret) + goto entity_cleanup; + + ar0521_adj_fmt(&sensor->fmt); + + ret = v4l2_async_register_subdev(&sensor->sd); + if (ret) + goto free_ctrls; + + /* Turn on the device and enable runtime PM */ + ret = ar0521_power_on(&client->dev); + if (ret) + goto disable; + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_idle(&client->dev); + return 0; + +disable: + v4l2_async_unregister_subdev(&sensor->sd); + media_entity_cleanup(&sensor->sd.entity); +free_ctrls: + v4l2_ctrl_handler_free(&sensor->ctrls.handler); +entity_cleanup: + media_entity_cleanup(&sensor->sd.entity); + mutex_destroy(&sensor->lock); + return ret; +} + +static int ar0521_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ar0521_dev *sensor = to_ar0521_dev(sd); + + v4l2_async_unregister_subdev(&sensor->sd); + media_entity_cleanup(&sensor->sd.entity); + v4l2_ctrl_handler_free(&sensor->ctrls.handler); + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + ar0521_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); + mutex_destroy(&sensor->lock); + return 0; +} + +static const struct dev_pm_ops ar0521_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ar0521_suspend, ar0521_resume) + SET_RUNTIME_PM_OPS(ar0521_power_off, ar0521_power_on, NULL) +}; +static const struct of_device_id ar0521_dt_ids[] = { + {.compatible = "onnn,ar0521"}, + {} +}; +MODULE_DEVICE_TABLE(of, ar0521_dt_ids); + +static struct i2c_driver ar0521_i2c_driver = { + .driver = { + .name = "ar0521", + .pm = &ar0521_pm_ops, + .of_match_table = ar0521_dt_ids, + }, + .probe_new = ar0521_probe, + .remove = ar0521_remove, +}; + +module_i2c_driver(ar0521_i2c_driver); + +MODULE_DESCRIPTION("AR0521 MIPI Camera subdev driver"); +MODULE_AUTHOR("Krzysztof Hałasa <khalasa@piap.pl>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c index cbce8b88dbcf..1fd4dc6e4726 100644 --- a/drivers/media/i2c/mt9p031.c +++ b/drivers/media/i2c/mt9p031.c @@ -623,12 +623,22 @@ static int mt9p031_get_selection(struct v4l2_subdev *subdev, { struct mt9p031 *mt9p031 = to_mt9p031(subdev); - if (sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.left = MT9P031_COLUMN_START_MIN; + sel->r.top = MT9P031_ROW_START_MIN; + sel->r.width = MT9P031_WINDOW_WIDTH_MAX; + sel->r.height = MT9P031_WINDOW_HEIGHT_MAX; + return 0; - sel->r = *__mt9p031_get_pad_crop(mt9p031, sd_state, sel->pad, - sel->which); - return 0; + case V4L2_SEL_TGT_CROP: + sel->r = *__mt9p031_get_pad_crop(mt9p031, sd_state, + sel->pad, sel->which); + return 0; + + default: + return -EINVAL; + } } static int mt9p031_set_selection(struct v4l2_subdev *subdev, @@ -682,6 +692,37 @@ static int mt9p031_set_selection(struct v4l2_subdev *subdev, return 0; } +static int mt9p031_init_cfg(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state) +{ + struct mt9p031 *mt9p031 = to_mt9p031(subdev); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + const int which = sd_state == NULL ? V4L2_SUBDEV_FORMAT_ACTIVE : + V4L2_SUBDEV_FORMAT_TRY; + + crop = __mt9p031_get_pad_crop(mt9p031, sd_state, 0, which); + v4l2_subdev_get_try_crop(subdev, sd_state, 0); + crop->left = MT9P031_COLUMN_START_DEF; + crop->top = MT9P031_ROW_START_DEF; + crop->width = MT9P031_WINDOW_WIDTH_DEF; + crop->height = MT9P031_WINDOW_HEIGHT_DEF; + + format = __mt9p031_get_pad_format(mt9p031, sd_state, 0, which); + + if (mt9p031->model == MT9P031_MODEL_MONOCHROME) + format->code = MEDIA_BUS_FMT_Y12_1X12; + else + format->code = MEDIA_BUS_FMT_SGRBG12_1X12; + + format->width = MT9P031_WINDOW_WIDTH_DEF; + format->height = MT9P031_WINDOW_HEIGHT_DEF; + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_SRGB; + + return 0; +} + /* ----------------------------------------------------------------------------- * V4L2 subdev control operations */ @@ -980,28 +1021,6 @@ static int mt9p031_registered(struct v4l2_subdev *subdev) static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) { - struct mt9p031 *mt9p031 = to_mt9p031(subdev); - struct v4l2_mbus_framefmt *format; - struct v4l2_rect *crop; - - crop = v4l2_subdev_get_try_crop(subdev, fh->state, 0); - crop->left = MT9P031_COLUMN_START_DEF; - crop->top = MT9P031_ROW_START_DEF; - crop->width = MT9P031_WINDOW_WIDTH_DEF; - crop->height = MT9P031_WINDOW_HEIGHT_DEF; - - format = v4l2_subdev_get_try_format(subdev, fh->state, 0); - - if (mt9p031->model == MT9P031_MODEL_MONOCHROME) - format->code = MEDIA_BUS_FMT_Y12_1X12; - else - format->code = MEDIA_BUS_FMT_SGRBG12_1X12; - - format->width = MT9P031_WINDOW_WIDTH_DEF; - format->height = MT9P031_WINDOW_HEIGHT_DEF; - format->field = V4L2_FIELD_NONE; - format->colorspace = V4L2_COLORSPACE_SRGB; - return mt9p031_set_power(subdev, 1); } @@ -1019,6 +1038,7 @@ static const struct v4l2_subdev_video_ops mt9p031_subdev_video_ops = { }; static const struct v4l2_subdev_pad_ops mt9p031_subdev_pad_ops = { + .init_cfg = mt9p031_init_cfg, .enum_mbus_code = mt9p031_enum_mbus_code, .enum_frame_size = mt9p031_enum_frame_size, .get_fmt = mt9p031_get_format, @@ -1166,20 +1186,9 @@ static int mt9p031_probe(struct i2c_client *client, mt9p031->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - mt9p031->crop.width = MT9P031_WINDOW_WIDTH_DEF; - mt9p031->crop.height = MT9P031_WINDOW_HEIGHT_DEF; - mt9p031->crop.left = MT9P031_COLUMN_START_DEF; - mt9p031->crop.top = MT9P031_ROW_START_DEF; - - if (mt9p031->model == MT9P031_MODEL_MONOCHROME) - mt9p031->format.code = MEDIA_BUS_FMT_Y12_1X12; - else - mt9p031->format.code = MEDIA_BUS_FMT_SGRBG12_1X12; - - mt9p031->format.width = MT9P031_WINDOW_WIDTH_DEF; - mt9p031->format.height = MT9P031_WINDOW_HEIGHT_DEF; - mt9p031->format.field = V4L2_FIELD_NONE; - mt9p031->format.colorspace = V4L2_COLORSPACE_SRGB; + ret = mt9p031_init_cfg(&mt9p031->subdev, NULL); + if (ret) + goto done; mt9p031->reset = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_HIGH); @@ -1214,6 +1223,7 @@ static int mt9p031_remove(struct i2c_client *client) } static const struct i2c_device_id mt9p031_id[] = { + { "mt9p006", MT9P031_MODEL_COLOR }, { "mt9p031", MT9P031_MODEL_COLOR }, { "mt9p031m", MT9P031_MODEL_MONOCHROME }, { } @@ -1222,6 +1232,7 @@ MODULE_DEVICE_TABLE(i2c, mt9p031_id); #if IS_ENABLED(CONFIG_OF) static const struct of_device_id mt9p031_of_match[] = { + { .compatible = "aptina,mt9p006", }, { .compatible = "aptina,mt9p031", }, { .compatible = "aptina,mt9p031m", }, { /* sentinel */ }, diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index db5a19babe67..502f0b62e950 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -29,8 +29,21 @@ #define OV5640_XCLK_MIN 6000000 #define OV5640_XCLK_MAX 54000000 +#define OV5640_NATIVE_WIDTH 2624 +#define OV5640_NATIVE_HEIGHT 1964 +#define OV5640_PIXEL_ARRAY_TOP 14 +#define OV5640_PIXEL_ARRAY_LEFT 16 +#define OV5640_PIXEL_ARRAY_WIDTH 2592 +#define OV5640_PIXEL_ARRAY_HEIGHT 1944 + +/* FIXME: not documented. */ +#define OV5640_MIN_VBLANK 24 +#define OV5640_MAX_VTS 3375 + #define OV5640_DEFAULT_SLAVE_ID 0x3c +#define OV5640_LINK_RATE_MAX 490000000U + #define OV5640_REG_SYS_RESET02 0x3002 #define OV5640_REG_SYS_CLOCK_ENABLE02 0x3006 #define OV5640_REG_SYS_CTRL0 0x3008 @@ -59,10 +72,16 @@ #define OV5640_REG_AEC_PK_MANUAL 0x3503 #define OV5640_REG_AEC_PK_REAL_GAIN 0x350a #define OV5640_REG_AEC_PK_VTS 0x350c +#define OV5640_REG_TIMING_HS 0x3800 +#define OV5640_REG_TIMING_VS 0x3802 +#define OV5640_REG_TIMING_HW 0x3804 +#define OV5640_REG_TIMING_VH 0x3806 #define OV5640_REG_TIMING_DVPHO 0x3808 #define OV5640_REG_TIMING_DVPVO 0x380a #define OV5640_REG_TIMING_HTS 0x380c #define OV5640_REG_TIMING_VTS 0x380e +#define OV5640_REG_TIMING_HOFFS 0x3810 +#define OV5640_REG_TIMING_VOFFS 0x3812 #define OV5640_REG_TIMING_TC_REG20 0x3820 #define OV5640_REG_TIMING_TC_REG21 0x3821 #define OV5640_REG_AEC_CTRL00 0x3a00 @@ -88,6 +107,7 @@ #define OV5640_REG_POLARITY_CTRL00 0x4740 #define OV5640_REG_MIPI_CTRL00 0x4800 #define OV5640_REG_DEBUG_MODE 0x4814 +#define OV5640_REG_PCLK_PERIOD 0x4837 #define OV5640_REG_ISP_FORMAT_MUX_CTRL 0x501f #define OV5640_REG_PRE_ISP_TEST_SET1 0x503d #define OV5640_REG_SDE_CTRL0 0x5580 @@ -118,6 +138,47 @@ enum ov5640_frame_rate { OV5640_NUM_FRAMERATES, }; +enum ov5640_pixel_rate_id { + OV5640_PIXEL_RATE_168M, + OV5640_PIXEL_RATE_148M, + OV5640_PIXEL_RATE_124M, + OV5640_PIXEL_RATE_96M, + OV5640_PIXEL_RATE_48M, + OV5640_NUM_PIXEL_RATES, +}; + +/* + * The chip manual suggests 24/48/96/192 MHz pixel clocks. + * + * 192MHz exceeds the sysclk limits; use 168MHz as maximum pixel rate for + * full resolution mode @15 FPS. + */ +static const u32 ov5640_pixel_rates[] = { + [OV5640_PIXEL_RATE_168M] = 168000000, + [OV5640_PIXEL_RATE_148M] = 148000000, + [OV5640_PIXEL_RATE_124M] = 124000000, + [OV5640_PIXEL_RATE_96M] = 96000000, + [OV5640_PIXEL_RATE_48M] = 48000000, +}; + +/* + * MIPI CSI-2 link frequencies. + * + * Derived from the above defined pixel rate for bpp = (8, 16, 24) and + * data_lanes = (1, 2) + * + * link_freq = (pixel_rate * bpp) / (2 * data_lanes) + */ +static const s64 ov5640_csi2_link_freqs[] = { + 992000000, 888000000, 768000000, 744000000, 672000000, 672000000, + 592000000, 592000000, 576000000, 576000000, 496000000, 496000000, + 384000000, 384000000, 384000000, 336000000, 296000000, 288000000, + 248000000, 192000000, 192000000, 192000000, 96000000, +}; + +/* Link freq for default mode: UYVY 16 bpp, 2 data lanes. */ +#define OV5640_DEFAULT_LINK_FREQ 13 + enum ov5640_format_mux { OV5640_FMT_MUX_YUV422 = 0, OV5640_FMT_MUX_RGB, @@ -130,20 +191,145 @@ enum ov5640_format_mux { struct ov5640_pixfmt { u32 code; u32 colorspace; + u8 bpp; + u8 ctrl00; + enum ov5640_format_mux mux; +}; + +static const struct ov5640_pixfmt ov5640_dvp_formats[] = { + { + /* YUV422, YUYV */ + .code = MEDIA_BUS_FMT_JPEG_1X8, + .colorspace = V4L2_COLORSPACE_JPEG, + .bpp = 16, + .ctrl00 = 0x30, + .mux = OV5640_FMT_MUX_YUV422, + }, { + /* YUV422, UYVY */ + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .bpp = 16, + .ctrl00 = 0x3f, + .mux = OV5640_FMT_MUX_YUV422, + }, { + /* YUV422, YUYV */ + .code = MEDIA_BUS_FMT_YUYV8_2X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .bpp = 16, + .ctrl00 = 0x30, + .mux = OV5640_FMT_MUX_YUV422, + }, { + /* RGB565 {g[2:0],b[4:0]},{r[4:0],g[5:3]} */ + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .colorspace = V4L2_COLORSPACE_SRGB, + .bpp = 16, + .ctrl00 = 0x6f, + .mux = OV5640_FMT_MUX_RGB, + }, { + /* RGB565 {r[4:0],g[5:3]},{g[2:0],b[4:0]} */ + .code = MEDIA_BUS_FMT_RGB565_2X8_BE, + .colorspace = V4L2_COLORSPACE_SRGB, + .bpp = 16, + .ctrl00 = 0x61, + .mux = OV5640_FMT_MUX_RGB, + }, { + /* Raw, BGBG... / GRGR... */ + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .bpp = 8, + .ctrl00 = 0x00, + .mux = OV5640_FMT_MUX_RAW_DPC, + }, { + /* Raw bayer, GBGB... / RGRG... */ + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .bpp = 8, + .ctrl00 = 0x01, + .mux = OV5640_FMT_MUX_RAW_DPC, + }, { + /* Raw bayer, GRGR... / BGBG... */ + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .bpp = 8, + .ctrl00 = 0x02, + .mux = OV5640_FMT_MUX_RAW_DPC, + }, { + /* Raw bayer, RGRG... / GBGB... */ + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .bpp = 8, + .ctrl00 = 0x03, + .mux = OV5640_FMT_MUX_RAW_DPC, + }, + { /* sentinel */ } }; -static const struct ov5640_pixfmt ov5640_formats[] = { - { MEDIA_BUS_FMT_JPEG_1X8, V4L2_COLORSPACE_JPEG, }, - { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_SRGB, }, - { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_COLORSPACE_SRGB, }, - { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_SRGB, }, - { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_COLORSPACE_SRGB, }, - { MEDIA_BUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB, }, - { MEDIA_BUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB, }, - { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB, }, - { MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_COLORSPACE_SRGB, }, - { MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_COLORSPACE_SRGB, }, - { MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_COLORSPACE_SRGB, }, +static const struct ov5640_pixfmt ov5640_csi2_formats[] = { + { + /* YUV422, YUYV */ + .code = MEDIA_BUS_FMT_JPEG_1X8, + .colorspace = V4L2_COLORSPACE_JPEG, + .bpp = 16, + .ctrl00 = 0x30, + .mux = OV5640_FMT_MUX_YUV422, + }, { + /* YUV422, UYVY */ + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .colorspace = V4L2_COLORSPACE_SRGB, + .bpp = 16, + .ctrl00 = 0x3f, + .mux = OV5640_FMT_MUX_YUV422, + }, { + /* YUV422, YUYV */ + .code = MEDIA_BUS_FMT_YUYV8_1X16, + .colorspace = V4L2_COLORSPACE_SRGB, + .bpp = 16, + .ctrl00 = 0x30, + .mux = OV5640_FMT_MUX_YUV422, + }, { + /* RGB565 {g[2:0],b[4:0]},{r[4:0],g[5:3]} */ + .code = MEDIA_BUS_FMT_RGB565_1X16, + .colorspace = V4L2_COLORSPACE_SRGB, + .bpp = 16, + .ctrl00 = 0x6f, + .mux = OV5640_FMT_MUX_RGB, + }, { + /* BGR888: RGB */ + .code = MEDIA_BUS_FMT_BGR888_1X24, + .colorspace = V4L2_COLORSPACE_SRGB, + .bpp = 24, + .ctrl00 = 0x23, + .mux = OV5640_FMT_MUX_RGB, + }, { + /* Raw, BGBG... / GRGR... */ + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .bpp = 8, + .ctrl00 = 0x00, + .mux = OV5640_FMT_MUX_RAW_DPC, + }, { + /* Raw bayer, GBGB... / RGRG... */ + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .bpp = 8, + .ctrl00 = 0x01, + .mux = OV5640_FMT_MUX_RAW_DPC, + }, { + /* Raw bayer, GRGR... / BGBG... */ + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .bpp = 8, + .ctrl00 = 0x02, + .mux = OV5640_FMT_MUX_RAW_DPC, + }, { + /* Raw bayer, RGRG... / GBGB... */ + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .bpp = 8, + .ctrl00 = 0x03, + .mux = OV5640_FMT_MUX_RAW_DPC, + }, + { /* sentinel */ } }; /* @@ -186,21 +372,42 @@ struct reg_value { u32 delay_ms; }; +struct ov5640_timings { + /* Analog crop rectangle. */ + struct v4l2_rect analog_crop; + /* Visibile crop: from analog crop top-left corner. */ + struct v4l2_rect crop; + /* Total pixels per line: width + fixed hblank. */ + u32 htot; + /* Default vertical blanking: frame height = height + vblank. */ + u32 vblank_def; +}; + struct ov5640_mode_info { enum ov5640_mode_id id; enum ov5640_downsize_mode dn_mode; - u32 hact; - u32 htot; - u32 vact; - u32 vtot; + enum ov5640_pixel_rate_id pixel_rate; + + unsigned int width; + unsigned int height; + + struct ov5640_timings dvp_timings; + struct ov5640_timings csi2_timings; + const struct reg_value *reg_data; u32 reg_data_size; + + /* Used by s_frame_interval only. */ u32 max_fps; + u32 def_fps; }; struct ov5640_ctrls { struct v4l2_ctrl_handler handler; struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; struct { struct v4l2_ctrl *auto_exp; struct v4l2_ctrl *exposure; @@ -249,6 +456,7 @@ struct ov5640_dev { const struct ov5640_mode_info *last_mode; enum ov5640_frame_rate current_fr; struct v4l2_fract frame_interval; + s64 current_link_freq; struct ov5640_ctrls ctrls; @@ -270,6 +478,40 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) ctrls.handler)->sd; } +static inline bool ov5640_is_csi2(const struct ov5640_dev *sensor) +{ + return sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY; +} + +static inline const struct ov5640_pixfmt * +ov5640_formats(struct ov5640_dev *sensor) +{ + return ov5640_is_csi2(sensor) ? ov5640_csi2_formats + : ov5640_dvp_formats; +} + +static const struct ov5640_pixfmt * +ov5640_code_to_pixfmt(struct ov5640_dev *sensor, u32 code) +{ + const struct ov5640_pixfmt *formats = ov5640_formats(sensor); + unsigned int i; + + for (i = 0; formats[i].code; ++i) { + if (formats[i].code == code) + return &formats[i]; + } + + return &formats[0]; +} + +static u32 ov5640_code_to_bpp(struct ov5640_dev *sensor, u32 code) +{ + const struct ov5640_pixfmt *format = ov5640_code_to_pixfmt(sensor, + code); + + return format->bpp; +} + /* * FIXME: all of these register tables are likely filled with * entries that set the register to their power-on default values, @@ -278,7 +520,19 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) * over i2c. */ /* YUV422 UYVY VGA@30fps */ -static const struct reg_value ov5640_init_setting_30fps_VGA[] = { + +static const struct v4l2_mbus_framefmt ov5640_default_fmt = { + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .width = 640, + .height = 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(V4L2_COLORSPACE_SRGB), + .quantization = V4L2_QUANTIZATION_FULL_RANGE, + .xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(V4L2_COLORSPACE_SRGB), + .field = V4L2_FIELD_NONE, +}; + +static const struct reg_value ov5640_init_setting[] = { {0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 5}, {0x3008, 0x42, 0, 0}, {0x3103, 0x03, 0, 0}, {0x3630, 0x36, 0, 0}, {0x3631, 0x0e, 0, 0}, {0x3632, 0xe2, 0, 0}, {0x3633, 0x12, 0, 0}, @@ -294,11 +548,7 @@ static const struct reg_value ov5640_init_setting_30fps_VGA[] = { {0x3c06, 0x00, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c08, 0x00, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, @@ -362,72 +612,11 @@ static const struct reg_value ov5640_init_setting_30fps_VGA[] = { {0x3a1f, 0x14, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3c00, 0x04, 0, 300}, }; -static const struct reg_value ov5640_setting_VGA_640_480[] = { - {0x3c07, 0x08, 0, 0}, - {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3814, 0x31, 0, 0}, - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, - {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, - {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, - {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, - {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, - {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, - {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, -}; - -static const struct reg_value ov5640_setting_XGA_1024_768[] = { - {0x3c07, 0x08, 0, 0}, - {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3814, 0x31, 0, 0}, - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, - {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, - {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, - {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, - {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, - {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, - {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, -}; - -static const struct reg_value ov5640_setting_QVGA_320_240[] = { - {0x3c07, 0x08, 0, 0}, - {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3814, 0x31, 0, 0}, - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, - {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, - {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, - {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, - {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, - {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, - {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, -}; - -static const struct reg_value ov5640_setting_QQVGA_160_120[] = { +static const struct reg_value ov5640_setting_low_res[] = { {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3814, 0x31, 0, 0}, - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, @@ -437,72 +626,11 @@ static const struct reg_value ov5640_setting_QQVGA_160_120[] = { {0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0}, }; -static const struct reg_value ov5640_setting_QCIF_176_144[] = { - {0x3c07, 0x08, 0, 0}, - {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3814, 0x31, 0, 0}, - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, - {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, - {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, - {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, - {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, - {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, - {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, -}; - -static const struct reg_value ov5640_setting_NTSC_720_480[] = { - {0x3c07, 0x08, 0, 0}, - {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3814, 0x31, 0, 0}, - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0}, - {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, - {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, - {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, - {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, - {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, - {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, -}; - -static const struct reg_value ov5640_setting_PAL_720_576[] = { - {0x3c07, 0x08, 0, 0}, - {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3814, 0x31, 0, 0}, - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, - {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, - {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, - {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, - {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, - {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, - {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, -}; - static const struct reg_value ov5640_setting_720P_1280_720[] = { {0x3c07, 0x07, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3814, 0x31, 0, 0}, - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0}, {0x3a03, 0xe4, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0xbc, 0, 0}, @@ -517,11 +645,7 @@ static const struct reg_value ov5640_setting_1080P_1920_1080[] = { {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3814, 0x11, 0, 0}, - {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, + {0x3815, 0x11, 0, 0}, {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0}, {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0}, {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, @@ -532,9 +656,6 @@ static const struct reg_value ov5640_setting_1080P_1920_1080[] = { {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0}, {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0}, - {0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0}, - {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0}, @@ -548,11 +669,7 @@ static const struct reg_value ov5640_setting_QSXGA_2592_1944[] = { {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3814, 0x11, 0, 0}, - {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, + {0x3815, 0x11, 0, 0}, {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0}, {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0}, {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, @@ -563,67 +680,462 @@ static const struct reg_value ov5640_setting_QSXGA_2592_1944[] = { {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 70}, }; -/* power-on sensor init reg table */ -static const struct ov5640_mode_info ov5640_mode_init_data = { - 0, SUBSAMPLING, 640, 1896, 480, 984, - ov5640_init_setting_30fps_VGA, - ARRAY_SIZE(ov5640_init_setting_30fps_VGA), - OV5640_30_FPS, +static const struct ov5640_mode_info ov5640_mode_data[OV5640_NUM_MODES] = { + { + /* 160x120 */ + .id = OV5640_MODE_QQVGA_160_120, + .dn_mode = SUBSAMPLING, + .pixel_rate = OV5640_PIXEL_RATE_48M, + .width = 160, + .height = 120, + .dvp_timings = { + .analog_crop = { + .left = 0, + .top = 4, + .width = 2624, + .height = 1944, + }, + .crop = { + .left = 16, + .top = 6, + .width = 160, + .height = 120, + }, + .htot = 1896, + .vblank_def = 864, + }, + .csi2_timings = { + /* Feed the full valid pixel array to the ISP. */ + .analog_crop = { + .left = OV5640_PIXEL_ARRAY_LEFT, + .top = OV5640_PIXEL_ARRAY_TOP, + .width = OV5640_PIXEL_ARRAY_WIDTH, + .height = OV5640_PIXEL_ARRAY_HEIGHT, + }, + /* Maintain a minimum processing margin. */ + .crop = { + .left = 2, + .top = 4, + .width = 160, + .height = 120, + }, + .htot = 1600, + .vblank_def = 878, + }, + .reg_data = ov5640_setting_low_res, + .reg_data_size = ARRAY_SIZE(ov5640_setting_low_res), + .max_fps = OV5640_30_FPS, + .def_fps = OV5640_30_FPS + }, { + /* 176x144 */ + .id = OV5640_MODE_QCIF_176_144, + .dn_mode = SUBSAMPLING, + .pixel_rate = OV5640_PIXEL_RATE_48M, + .width = 176, + .height = 144, + .dvp_timings = { + .analog_crop = { + .left = 0, + .top = 4, + .width = 2624, + .height = 1944, + }, + .crop = { + .left = 16, + .top = 6, + .width = 176, + .height = 144, + }, + .htot = 1896, + .vblank_def = 840, + }, + .csi2_timings = { + /* Feed the full valid pixel array to the ISP. */ + .analog_crop = { + .left = OV5640_PIXEL_ARRAY_LEFT, + .top = OV5640_PIXEL_ARRAY_TOP, + .width = OV5640_PIXEL_ARRAY_WIDTH, + .height = OV5640_PIXEL_ARRAY_HEIGHT, + }, + /* Maintain a minimum processing margin. */ + .crop = { + .left = 2, + .top = 4, + .width = 176, + .height = 144, + }, + .htot = 1600, + .vblank_def = 854, + }, + .reg_data = ov5640_setting_low_res, + .reg_data_size = ARRAY_SIZE(ov5640_setting_low_res), + .max_fps = OV5640_30_FPS, + .def_fps = OV5640_30_FPS + }, { + /* 320x240 */ + .id = OV5640_MODE_QVGA_320_240, + .dn_mode = SUBSAMPLING, + .width = 320, + .height = 240, + .pixel_rate = OV5640_PIXEL_RATE_48M, + .dvp_timings = { + .analog_crop = { + .left = 0, + .top = 4, + .width = 2624, + .height = 1944, + }, + .crop = { + .left = 16, + .top = 6, + .width = 320, + .height = 240, + }, + .htot = 1896, + .vblank_def = 744, + }, + .csi2_timings = { + /* Feed the full valid pixel array to the ISP. */ + .analog_crop = { + .left = OV5640_PIXEL_ARRAY_LEFT, + .top = OV5640_PIXEL_ARRAY_TOP, + .width = OV5640_PIXEL_ARRAY_WIDTH, + .height = OV5640_PIXEL_ARRAY_HEIGHT, + }, + /* Maintain a minimum processing margin. */ + .crop = { + .left = 2, + .top = 4, + .width = 320, + .height = 240, + }, + .htot = 1600, + .vblank_def = 760, + }, + .reg_data = ov5640_setting_low_res, + .reg_data_size = ARRAY_SIZE(ov5640_setting_low_res), + .max_fps = OV5640_30_FPS, + .def_fps = OV5640_30_FPS + }, { + /* 640x480 */ + .id = OV5640_MODE_VGA_640_480, + .dn_mode = SUBSAMPLING, + .pixel_rate = OV5640_PIXEL_RATE_48M, + .width = 640, + .height = 480, + .dvp_timings = { + .analog_crop = { + .left = 0, + .top = 4, + .width = 2624, + .height = 1944, + }, + .crop = { + .left = 16, + .top = 6, + .width = 640, + .height = 480, + }, + .htot = 1896, + .vblank_def = 600, + }, + .csi2_timings = { + /* Feed the full valid pixel array to the ISP. */ + .analog_crop = { + .left = OV5640_PIXEL_ARRAY_LEFT, + .top = OV5640_PIXEL_ARRAY_TOP, + .width = OV5640_PIXEL_ARRAY_WIDTH, + .height = OV5640_PIXEL_ARRAY_HEIGHT, + }, + /* Maintain a minimum processing margin. */ + .crop = { + .left = 2, + .top = 4, + .width = 640, + .height = 480, + }, + .htot = 1600, + .vblank_def = 520, + }, + .reg_data = ov5640_setting_low_res, + .reg_data_size = ARRAY_SIZE(ov5640_setting_low_res), + .max_fps = OV5640_60_FPS, + .def_fps = OV5640_30_FPS + }, { + /* 720x480 */ + .id = OV5640_MODE_NTSC_720_480, + .dn_mode = SUBSAMPLING, + .width = 720, + .height = 480, + .pixel_rate = OV5640_PIXEL_RATE_96M, + .dvp_timings = { + .analog_crop = { + .left = 0, + .top = 4, + .width = 2624, + .height = 1944, + }, + .crop = { + .left = 56, + .top = 60, + .width = 720, + .height = 480, + }, + .htot = 1896, + .vblank_def = 504, + }, + .csi2_timings = { + /* Feed the full valid pixel array to the ISP. */ + .analog_crop = { + .left = OV5640_PIXEL_ARRAY_LEFT, + .top = OV5640_PIXEL_ARRAY_TOP, + .width = OV5640_PIXEL_ARRAY_WIDTH, + .height = OV5640_PIXEL_ARRAY_HEIGHT, + }, + .crop = { + .left = 56, + .top = 60, + .width = 720, + .height = 480, + }, + .htot = 1896, + .vblank_def = 1206, + }, + .reg_data = ov5640_setting_low_res, + .reg_data_size = ARRAY_SIZE(ov5640_setting_low_res), + .max_fps = OV5640_30_FPS, + .def_fps = OV5640_30_FPS + }, { + /* 720x576 */ + .id = OV5640_MODE_PAL_720_576, + .dn_mode = SUBSAMPLING, + .width = 720, + .height = 576, + .pixel_rate = OV5640_PIXEL_RATE_96M, + .dvp_timings = { + .analog_crop = { + .left = 0, + .top = 4, + .width = 2624, + .height = 1944, + }, + .crop = { + .left = 56, + .top = 6, + .width = 720, + .height = 576, + }, + .htot = 1896, + .vblank_def = 408, + }, + .csi2_timings = { + /* Feed the full valid pixel array to the ISP. */ + .analog_crop = { + .left = OV5640_PIXEL_ARRAY_LEFT, + .top = OV5640_PIXEL_ARRAY_TOP, + .width = OV5640_PIXEL_ARRAY_WIDTH, + .height = OV5640_PIXEL_ARRAY_HEIGHT, + }, + .crop = { + .left = 56, + .top = 6, + .width = 720, + .height = 576, + }, + .htot = 1896, + .vblank_def = 1110, + }, + .reg_data = ov5640_setting_low_res, + .reg_data_size = ARRAY_SIZE(ov5640_setting_low_res), + .max_fps = OV5640_30_FPS, + .def_fps = OV5640_30_FPS + }, { + /* 1024x768 */ + .id = OV5640_MODE_XGA_1024_768, + .dn_mode = SUBSAMPLING, + .pixel_rate = OV5640_PIXEL_RATE_96M, + .width = 1024, + .height = 768, + .dvp_timings = { + .analog_crop = { + .left = 0, + .top = 4, + .width = 2624, + .height = 1944, + }, + .crop = { + .left = 16, + .top = 6, + .width = 1024, + .height = 768, + }, + .htot = 1896, + .vblank_def = 312, + }, + .csi2_timings = { + .analog_crop = { + .left = 0, + .top = 4, + .width = OV5640_NATIVE_WIDTH, + .height = OV5640_PIXEL_ARRAY_HEIGHT, + }, + .crop = { + .left = 16, + .top = 6, + .width = 1024, + .height = 768, + }, + .htot = 1896, + .vblank_def = 918, + }, + .reg_data = ov5640_setting_low_res, + .reg_data_size = ARRAY_SIZE(ov5640_setting_low_res), + .max_fps = OV5640_30_FPS, + .def_fps = OV5640_30_FPS + }, { + /* 1280x720 */ + .id = OV5640_MODE_720P_1280_720, + .dn_mode = SUBSAMPLING, + .pixel_rate = OV5640_PIXEL_RATE_124M, + .width = 1280, + .height = 720, + .dvp_timings = { + .analog_crop = { + .left = 0, + .top = 250, + .width = 2624, + .height = 1456, + }, + .crop = { + .left = 16, + .top = 4, + .width = 1280, + .height = 720, + }, + .htot = 1892, + .vblank_def = 20, + }, + .csi2_timings = { + .analog_crop = { + .left = 0, + .top = 250, + .width = 2624, + .height = 1456, + }, + .crop = { + .left = 16, + .top = 4, + .width = 1280, + .height = 720, + }, + .htot = 1600, + .vblank_def = 560, + }, + .reg_data = ov5640_setting_720P_1280_720, + .reg_data_size = ARRAY_SIZE(ov5640_setting_720P_1280_720), + .max_fps = OV5640_30_FPS, + .def_fps = OV5640_30_FPS + }, { + /* 1920x1080 */ + .id = OV5640_MODE_1080P_1920_1080, + .dn_mode = SCALING, + .pixel_rate = OV5640_PIXEL_RATE_148M, + .width = 1920, + .height = 1080, + .dvp_timings = { + .analog_crop = { + .left = 336, + .top = 434, + .width = 1952, + .height = 1088, + }, + .crop = { + .left = 16, + .top = 4, + .width = 1920, + .height = 1080, + }, + .htot = 2500, + .vblank_def = 40, + }, + .csi2_timings = { + /* Crop the full valid pixel array in the center. */ + .analog_crop = { + .left = 336, + .top = 434, + .width = 1952, + .height = 1088, + }, + /* Maintain a larger processing margins. */ + .crop = { + .left = 16, + .top = 4, + .width = 1920, + .height = 1080, + }, + .htot = 2234, + .vblank_def = 24, + }, + .reg_data = ov5640_setting_1080P_1920_1080, + .reg_data_size = ARRAY_SIZE(ov5640_setting_1080P_1920_1080), + .max_fps = OV5640_30_FPS, + .def_fps = OV5640_30_FPS + }, { + /* 2592x1944 */ + .id = OV5640_MODE_QSXGA_2592_1944, + .dn_mode = SCALING, + .pixel_rate = OV5640_PIXEL_RATE_168M, + .width = OV5640_PIXEL_ARRAY_WIDTH, + .height = OV5640_PIXEL_ARRAY_HEIGHT, + .dvp_timings = { + .analog_crop = { + .left = 0, + .top = 0, + .width = 2624, + .height = 1952, + }, + .crop = { + .left = 16, + .top = 4, + .width = 2592, + .height = 1944, + }, + .htot = 2844, + .vblank_def = 24, + }, + .csi2_timings = { + /* Give more processing margin to full resolution. */ + .analog_crop = { + .left = 0, + .top = 0, + .width = OV5640_NATIVE_WIDTH, + .height = 1952, + }, + .crop = { + .left = 16, + .top = 4, + .width = 2592, + .height = 1944, + }, + .htot = 2844, + .vblank_def = 24, + }, + .reg_data = ov5640_setting_QSXGA_2592_1944, + .reg_data_size = ARRAY_SIZE(ov5640_setting_QSXGA_2592_1944), + .max_fps = OV5640_15_FPS, + .def_fps = OV5640_15_FPS + }, }; -static const struct ov5640_mode_info -ov5640_mode_data[OV5640_NUM_MODES] = { - {OV5640_MODE_QQVGA_160_120, SUBSAMPLING, - 160, 1896, 120, 984, - ov5640_setting_QQVGA_160_120, - ARRAY_SIZE(ov5640_setting_QQVGA_160_120), - OV5640_30_FPS}, - {OV5640_MODE_QCIF_176_144, SUBSAMPLING, - 176, 1896, 144, 984, - ov5640_setting_QCIF_176_144, - ARRAY_SIZE(ov5640_setting_QCIF_176_144), - OV5640_30_FPS}, - {OV5640_MODE_QVGA_320_240, SUBSAMPLING, - 320, 1896, 240, 984, - ov5640_setting_QVGA_320_240, - ARRAY_SIZE(ov5640_setting_QVGA_320_240), - OV5640_30_FPS}, - {OV5640_MODE_VGA_640_480, SUBSAMPLING, - 640, 1896, 480, 1080, - ov5640_setting_VGA_640_480, - ARRAY_SIZE(ov5640_setting_VGA_640_480), - OV5640_60_FPS}, - {OV5640_MODE_NTSC_720_480, SUBSAMPLING, - 720, 1896, 480, 984, - ov5640_setting_NTSC_720_480, - ARRAY_SIZE(ov5640_setting_NTSC_720_480), - OV5640_30_FPS}, - {OV5640_MODE_PAL_720_576, SUBSAMPLING, - 720, 1896, 576, 984, - ov5640_setting_PAL_720_576, - ARRAY_SIZE(ov5640_setting_PAL_720_576), - OV5640_30_FPS}, - {OV5640_MODE_XGA_1024_768, SUBSAMPLING, - 1024, 1896, 768, 1080, - ov5640_setting_XGA_1024_768, - ARRAY_SIZE(ov5640_setting_XGA_1024_768), - OV5640_30_FPS}, - {OV5640_MODE_720P_1280_720, SUBSAMPLING, - 1280, 1892, 720, 740, - ov5640_setting_720P_1280_720, - ARRAY_SIZE(ov5640_setting_720P_1280_720), - OV5640_30_FPS}, - {OV5640_MODE_1080P_1920_1080, SCALING, - 1920, 2500, 1080, 1120, - ov5640_setting_1080P_1920_1080, - ARRAY_SIZE(ov5640_setting_1080P_1920_1080), - OV5640_30_FPS}, - {OV5640_MODE_QSXGA_2592_1944, SCALING, - 2592, 2844, 1944, 1968, - ov5640_setting_QSXGA_2592_1944, - ARRAY_SIZE(ov5640_setting_QSXGA_2592_1944), - OV5640_15_FPS}, -}; +static const struct ov5640_timings * +ov5640_timings(const struct ov5640_dev *sensor, + const struct ov5640_mode_info *mode) +{ + if (ov5640_is_csi2(sensor)) + return &mode->csi2_timings; + + return &mode->dvp_timings; +} static int ov5640_init_slave_id(struct ov5640_dev *sensor) { @@ -797,20 +1309,10 @@ static int ov5640_mod_reg(struct ov5640_dev *sensor, u16 reg, * +-----+-----+ * +------------> PCLK * - * This is deviating from the datasheet at least for the register - * 0x3108, since it's said here that the PCLK would be clocked from - * the PLL. - * - * There seems to be also (unverified) constraints: + * There seems to be also constraints: * - the PLL pre-divider output rate should be in the 4-27MHz range * - the PLL multiplier output rate should be in the 500-1000MHz range * - PCLK >= SCLK * 2 in YUV, >= SCLK in Raw or JPEG - * - * In the two latter cases, these constraints are met since our - * factors are hardcoded. If we were to change that, we would need to - * take this into account. The only varying parts are the PLL - * multiplier and the system clock divider, which are shared between - * all these clocks so won't cause any issue. */ /* @@ -830,13 +1332,6 @@ static int ov5640_mod_reg(struct ov5640_dev *sensor, u16 reg, #define OV5640_SYSDIV_MAX 16 /* - * Hardcode these values for scaler and non-scaler modes. - * FIXME: to be re-calcualted for 1 data lanes setups - */ -#define OV5640_MIPI_DIV_PCLK 2 -#define OV5640_MIPI_DIV_SCLK 1 - -/* * This is supposed to be ranging from 1 to 2, but the value is always * set to 2 in the vendor kernels. */ @@ -945,70 +1440,83 @@ out: /* * ov5640_set_mipi_pclk() - Calculate the clock tree configuration values * for the MIPI CSI-2 output. - * - * @rate: The requested bandwidth per lane in bytes per second. - * 'Bandwidth Per Lane' is calculated as: - * bpl = HTOT * VTOT * FPS * bpp / num_lanes; - * - * This function use the requested bandwidth to calculate: - * - sample_rate = bpl / (bpp / num_lanes); - * = bpl / (PLL_RDIV * BIT_DIV * PCLK_DIV * MIPI_DIV / num_lanes); - * - * - mipi_sclk = bpl / MIPI_DIV / 2; ( / 2 is for CSI-2 DDR) - * - * with these fixed parameters: - * PLL_RDIV = 2; - * BIT_DIVIDER = 2; (MIPI_BIT_MODE == 8 ? 2 : 2,5); - * PCLK_DIV = 1; - * - * The MIPI clock generation differs for modes that use the scaler and modes - * that do not. In case the scaler is in use, the MIPI_SCLK generates the MIPI - * BIT CLk, and thus: - * - * - mipi_sclk = bpl / MIPI_DIV / 2; - * MIPI_DIV = 1; - * - * For modes that do not go through the scaler, the MIPI BIT CLOCK is generated - * from the pixel clock, and thus: - * - * - sample_rate = bpl / (bpp / num_lanes); - * = bpl / (2 * 2 * 1 * MIPI_DIV / num_lanes); - * = bpl / (4 * MIPI_DIV / num_lanes); - * - MIPI_DIV = bpp / (4 * num_lanes); - * - * FIXME: this have been tested with 16bpp and 2 lanes setup only. - * MIPI_DIV is fixed to value 2, but it -might- be changed according to the - * above formula for setups with 1 lane or image formats with different bpp. - * - * FIXME: this deviates from the sensor manual documentation which is quite - * thin on the MIPI clock tree generation part. */ -static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor, - unsigned long rate) +static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor) { - const struct ov5640_mode_info *mode = sensor->current_mode; + u8 bit_div, mipi_div, pclk_div, sclk_div, sclk2x_div, root_div; u8 prediv, mult, sysdiv; - u8 mipi_div; + unsigned long link_freq; + unsigned long sysclk; + u8 pclk_period; + u32 sample_rate; + u32 num_lanes; int ret; + /* Use the link freq computed at ov5640_update_pixel_rate() time. */ + link_freq = sensor->current_link_freq; + /* - * 1280x720 is reported to use 'SUBSAMPLING' only, - * but according to the sensor manual it goes through the - * scaler before subsampling. + * - mipi_div - Additional divider for the MIPI lane clock. + * + * Higher link frequencies would make sysclk > 1GHz. + * Keep the sysclk low and do not divide in the MIPI domain. */ - if (mode->dn_mode == SCALING || - (mode->id == OV5640_MODE_720P_1280_720)) - mipi_div = OV5640_MIPI_DIV_SCLK; + if (link_freq > OV5640_LINK_RATE_MAX) + mipi_div = 1; else - mipi_div = OV5640_MIPI_DIV_PCLK; + mipi_div = 2; - ov5640_calc_sys_clk(sensor, rate, &prediv, &mult, &sysdiv); + sysclk = link_freq * mipi_div; + ov5640_calc_sys_clk(sensor, sysclk, &prediv, &mult, &sysdiv); - ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL0, - 0x0f, OV5640_PLL_CTRL0_MIPI_MODE_8BIT); + /* + * Adjust PLL parameters to maintain the MIPI_SCLK-to-PCLK ratio. + * + * - root_div = 2 (fixed) + * - bit_div : MIPI 8-bit = 2; MIPI 10-bit = 2.5 + * - pclk_div = 1 (fixed) + * - p_div = (2 lanes ? mipi_div : 2 * mipi_div) + * + * This results in the following MIPI_SCLK depending on the number + * of lanes: + * + * - 2 lanes: MIPI_SCLK = (4 or 5) * PCLK + * - 1 lanes: MIPI_SCLK = (8 or 10) * PCLK + */ + root_div = OV5640_PLL_CTRL3_PLL_ROOT_DIV_2; + bit_div = OV5640_PLL_CTRL0_MIPI_MODE_8BIT; + pclk_div = ilog2(OV5640_PCLK_ROOT_DIV); - ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1, - 0xff, sysdiv << 4 | mipi_div); + /* + * Scaler clock: + * - YUV: PCLK >= 2 * SCLK + * - RAW or JPEG: PCLK >= SCLK + * - sclk2x_div = sclk_div / 2 + */ + sclk_div = ilog2(OV5640_SCLK_ROOT_DIV); + sclk2x_div = ilog2(OV5640_SCLK2X_ROOT_DIV); + + /* + * Set the pixel clock period expressed in ns with 1-bit decimal + * (0x01=0.5ns). + * + * The register is very briefly documented. In the OV5645 datasheet it + * is described as (2 * pclk period), and from testing it seems the + * actual definition is 2 * 8-bit sample period. + * + * 2 * sample_period = (mipi_clk * 2 * num_lanes / bpp) * (bpp / 8) / 2 + */ + num_lanes = sensor->ep.bus.mipi_csi2.num_data_lanes; + sample_rate = (link_freq * mipi_div * num_lanes * 2) / 16; + pclk_period = 2000000000UL / sample_rate; + + /* Program the clock tree registers. */ + ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL0, 0x0f, bit_div); + if (ret) + return ret; + + ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1, 0xff, + (sysdiv << 4) | mipi_div); if (ret) return ret; @@ -1016,13 +1524,29 @@ static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor, if (ret) return ret; - ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3, - 0x1f, OV5640_PLL_CTRL3_PLL_ROOT_DIV_2 | prediv); + ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3, 0x1f, + root_div | prediv); if (ret) return ret; - return ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, - 0x30, OV5640_PLL_SYS_ROOT_DIVIDER_BYPASS); + ret = ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x3f, + (pclk_div << 4) | (sclk2x_div << 2) | sclk_div); + if (ret) + return ret; + + return ov5640_write_reg(sensor, OV5640_REG_PCLK_PERIOD, pclk_period); +} + +static u32 ov5640_calc_pixel_rate(struct ov5640_dev *sensor) +{ + const struct ov5640_mode_info *mode = sensor->current_mode; + const struct ov5640_timings *timings = &mode->dvp_timings; + u32 rate; + + rate = timings->htot * (timings->crop.height + timings->vblank_def); + rate *= ov5640_framerates[sensor->current_fr]; + + return rate; } static unsigned long ov5640_calc_pclk(struct ov5640_dev *sensor, @@ -1042,11 +1566,16 @@ static unsigned long ov5640_calc_pclk(struct ov5640_dev *sensor, return _rate / *pll_rdiv / *bit_div / *pclk_div; } -static int ov5640_set_dvp_pclk(struct ov5640_dev *sensor, unsigned long rate) +static int ov5640_set_dvp_pclk(struct ov5640_dev *sensor) { u8 prediv, mult, sysdiv, pll_rdiv, bit_div, pclk_div; + u32 rate; int ret; + rate = ov5640_calc_pixel_rate(sensor); + rate *= ov5640_code_to_bpp(sensor, sensor->fmt.code); + rate /= sensor->ep.bus.parallel.bus_width; + ov5640_calc_pclk(sensor, rate, &prediv, &mult, &sysdiv, &pll_rdiv, &bit_div, &pclk_div); @@ -1098,17 +1627,20 @@ static int ov5640_set_jpeg_timings(struct ov5640_dev *sensor, if (ret < 0) return ret; - ret = ov5640_write_reg16(sensor, OV5640_REG_VFIFO_HSIZE, mode->hact); + ret = ov5640_write_reg16(sensor, OV5640_REG_VFIFO_HSIZE, mode->width); if (ret < 0) return ret; - return ov5640_write_reg16(sensor, OV5640_REG_VFIFO_VSIZE, mode->vact); + return ov5640_write_reg16(sensor, OV5640_REG_VFIFO_VSIZE, mode->height); } /* download ov5640 settings to sensor through i2c */ static int ov5640_set_timings(struct ov5640_dev *sensor, const struct ov5640_mode_info *mode) { + const struct ov5640_timings *timings; + const struct v4l2_rect *analog_crop; + const struct v4l2_rect *crop; int ret; if (sensor->fmt.code == MEDIA_BUS_FMT_JPEG_1X8) { @@ -1117,32 +1649,68 @@ static int ov5640_set_timings(struct ov5640_dev *sensor, return ret; } - ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPHO, mode->hact); + timings = ov5640_timings(sensor, mode); + analog_crop = &timings->analog_crop; + crop = &timings->crop; + + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HS, + analog_crop->left); + if (ret < 0) + return ret; + + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_VS, + analog_crop->top); + if (ret < 0) + return ret; + + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HW, + analog_crop->left + analog_crop->width - 1); + if (ret < 0) + return ret; + + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_VH, + analog_crop->top + analog_crop->height - 1); + if (ret < 0) + return ret; + + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HOFFS, crop->left); + if (ret < 0) + return ret; + + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_VOFFS, crop->top); + if (ret < 0) + return ret; + + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPHO, mode->width); + if (ret < 0) + return ret; + + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPVO, mode->height); if (ret < 0) return ret; - ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPVO, mode->vact); + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HTS, timings->htot); if (ret < 0) return ret; - ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HTS, mode->htot); + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS, + mode->height + timings->vblank_def); if (ret < 0) return ret; - return ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS, mode->vtot); + return 0; } -static int ov5640_load_regs(struct ov5640_dev *sensor, - const struct ov5640_mode_info *mode) +static void ov5640_load_regs(struct ov5640_dev *sensor, + const struct reg_value *regs, unsigned int regnum) { - const struct reg_value *regs = mode->reg_data; unsigned int i; u32 delay_ms; u16 reg_addr; u8 mask, val; int ret = 0; - for (i = 0; i < mode->reg_data_size; ++i, ++regs) { + for (i = 0; i < regnum; ++i, ++regs) { delay_ms = regs->delay_ms; reg_addr = regs->reg_addr; val = regs->val; @@ -1151,7 +1719,7 @@ static int ov5640_load_regs(struct ov5640_dev *sensor, /* remain in power down mode for DVP */ if (regs->reg_addr == OV5640_REG_SYS_CTRL0 && val == OV5640_REG_SYS_CTRL0_SW_PWUP && - sensor->ep.bus_type != V4L2_MBUS_CSI2_DPHY) + !ov5640_is_csi2(sensor)) continue; if (mask) @@ -1164,8 +1732,6 @@ static int ov5640_load_regs(struct ov5640_dev *sensor, if (delay_ms) usleep_range(1000 * delay_ms, 1000 * delay_ms + 100); } - - return ov5640_set_timings(sensor, mode); } static int ov5640_set_autoexposure(struct ov5640_dev *sensor, bool on) @@ -1550,37 +2116,22 @@ static int ov5640_set_virtual_channel(struct ov5640_dev *sensor) } static const struct ov5640_mode_info * -ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr, - int width, int height, bool nearest) +ov5640_find_mode(struct ov5640_dev *sensor, int width, int height, bool nearest) { const struct ov5640_mode_info *mode; mode = v4l2_find_nearest_size(ov5640_mode_data, ARRAY_SIZE(ov5640_mode_data), - hact, vact, - width, height); + width, height, width, height); if (!mode || - (!nearest && (mode->hact != width || mode->vact != height))) - return NULL; - - /* Check to see if the current mode exceeds the max frame rate */ - if (ov5640_framerates[fr] > ov5640_framerates[mode->max_fps]) + (!nearest && + (mode->width != width || mode->height != height))) return NULL; return mode; } -static u64 ov5640_calc_pixel_rate(struct ov5640_dev *sensor) -{ - u64 rate; - - rate = sensor->current_mode->vtot * sensor->current_mode->htot; - rate *= ov5640_framerates[sensor->current_fr]; - - return rate; -} - /* * sensor changes between scaling and subsampling, go through * exposure calculation @@ -1628,7 +2179,8 @@ static int ov5640_set_mode_exposure_calc(struct ov5640_dev *sensor, return ret; /* Write capture setting */ - ret = ov5640_load_regs(sensor, mode); + ov5640_load_regs(sensor, mode->reg_data, mode->reg_data_size); + ret = ov5640_set_timings(sensor, mode); if (ret < 0) return ret; @@ -1752,7 +2304,8 @@ static int ov5640_set_mode_direct(struct ov5640_dev *sensor, return -EINVAL; /* Write capture setting */ - return ov5640_load_regs(sensor, mode); + ov5640_load_regs(sensor, mode->reg_data, mode->reg_data_size); + return ov5640_set_timings(sensor, mode); } static int ov5640_set_mode(struct ov5640_dev *sensor) @@ -1762,7 +2315,6 @@ static int ov5640_set_mode(struct ov5640_dev *sensor) enum ov5640_downsize_mode dn_mode, orig_dn_mode; bool auto_gain = sensor->ctrls.auto_gain->val == 1; bool auto_exp = sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO; - unsigned long rate; int ret; dn_mode = mode->dn_mode; @@ -1781,19 +2333,10 @@ static int ov5640_set_mode(struct ov5640_dev *sensor) goto restore_auto_gain; } - /* - * All the formats we support have 16 bits per pixel, seems to require - * the same rate than YUV, so we can just use 16 bpp all the time. - */ - rate = ov5640_calc_pixel_rate(sensor) * 16; - if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY) { - rate = rate / sensor->ep.bus.mipi_csi2.num_data_lanes; - ret = ov5640_set_mipi_pclk(sensor, rate); - } else { - rate = rate / sensor->ep.bus.parallel.bus_width; - ret = ov5640_set_dvp_pclk(sensor, rate); - } - + if (ov5640_is_csi2(sensor)) + ret = ov5640_set_mipi_pclk(sensor); + else + ret = ov5640_set_dvp_pclk(sensor); if (ret < 0) return 0; @@ -1860,10 +2403,8 @@ static int ov5640_restore_mode(struct ov5640_dev *sensor) int ret; /* first load the initial register values */ - ret = ov5640_load_regs(sensor, &ov5640_mode_init_data); - if (ret < 0) - return ret; - sensor->last_mode = &ov5640_mode_init_data; + ov5640_load_regs(sensor, ov5640_init_setting, + ARRAY_SIZE(ov5640_init_setting)); ret = ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x3f, (ilog2(OV5640_SCLK2X_ROOT_DIV) << 2) | @@ -2224,7 +2765,7 @@ static int ov5640_try_frame_interval(struct ov5640_dev *sensor, fi->denominator = best_fps; find_mode: - mode = ov5640_find_mode(sensor, rate, width, height, false); + mode = ov5640_find_mode(sensor, width, height, false); return mode ? rate : -EINVAL; } @@ -2260,25 +2801,34 @@ static int ov5640_try_fmt_internal(struct v4l2_subdev *sd, { struct ov5640_dev *sensor = to_ov5640_dev(sd); const struct ov5640_mode_info *mode; - int i; + const struct ov5640_pixfmt *pixfmt; + unsigned int bpp; - mode = ov5640_find_mode(sensor, fr, fmt->width, fmt->height, true); + mode = ov5640_find_mode(sensor, fmt->width, fmt->height, true); if (!mode) return -EINVAL; - fmt->width = mode->hact; - fmt->height = mode->vact; + + pixfmt = ov5640_code_to_pixfmt(sensor, fmt->code); + bpp = pixfmt->bpp; + + /* + * Adjust mode according to bpp: + * - 8bpp modes work for resolution >= 1280x720 + * - 24bpp modes work resolution < 1280x720 + */ + if (bpp == 8 && mode->width < 1280) + mode = &ov5640_mode_data[OV5640_MODE_720P_1280_720]; + else if (bpp == 24 && mode->width > 1024) + mode = &ov5640_mode_data[OV5640_MODE_XGA_1024_768]; + + fmt->width = mode->width; + fmt->height = mode->height; if (new_mode) *new_mode = mode; - for (i = 0; i < ARRAY_SIZE(ov5640_formats); i++) - if (ov5640_formats[i].code == fmt->code) - break; - if (i >= ARRAY_SIZE(ov5640_formats)) - i = 0; - - fmt->code = ov5640_formats[i].code; - fmt->colorspace = ov5640_formats[i].colorspace; + fmt->code = pixfmt->code; + fmt->colorspace = pixfmt->colorspace; fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); @@ -2286,6 +2836,107 @@ static int ov5640_try_fmt_internal(struct v4l2_subdev *sd, return 0; } +static int ov5640_update_pixel_rate(struct ov5640_dev *sensor) +{ + const struct ov5640_mode_info *mode = sensor->current_mode; + enum ov5640_pixel_rate_id pixel_rate_id = mode->pixel_rate; + struct v4l2_mbus_framefmt *fmt = &sensor->fmt; + const struct ov5640_timings *timings; + s32 exposure_val, exposure_max; + unsigned int hblank; + unsigned int i = 0; + u32 pixel_rate; + s64 link_freq; + u32 num_lanes; + u32 vblank; + u32 bpp; + + /* + * Update the pixel rate control value. + * + * For DVP mode, maintain the pixel rate calculation using fixed FPS. + */ + if (!ov5640_is_csi2(sensor)) { + __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate, + ov5640_calc_pixel_rate(sensor)); + + return 0; + } + + /* + * The MIPI CSI-2 link frequency should comply with the CSI-2 + * specification and be lower than 1GHz. + * + * Start from the suggested pixel_rate for the current mode and + * progressively slow it down if it exceeds 1GHz. + */ + num_lanes = sensor->ep.bus.mipi_csi2.num_data_lanes; + bpp = ov5640_code_to_bpp(sensor, fmt->code); + do { + pixel_rate = ov5640_pixel_rates[pixel_rate_id]; + link_freq = pixel_rate * bpp / (2 * num_lanes); + } while (link_freq >= 1000000000U && + ++pixel_rate_id < OV5640_NUM_PIXEL_RATES); + + sensor->current_link_freq = link_freq; + + /* + * Higher link rates require the clock tree to be programmed with + * 'mipi_div' = 1; this has the effect of halving the actual output + * pixel rate in the MIPI domain. + * + * Adjust the pixel rate and link frequency control value to report it + * correctly to userspace. + */ + if (link_freq > OV5640_LINK_RATE_MAX) { + pixel_rate /= 2; + link_freq /= 2; + } + + for (i = 0; i < ARRAY_SIZE(ov5640_csi2_link_freqs); ++i) { + if (ov5640_csi2_link_freqs[i] == link_freq) + break; + } + WARN_ON(i == ARRAY_SIZE(ov5640_csi2_link_freqs)); + + __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate, pixel_rate); + __v4l2_ctrl_s_ctrl(sensor->ctrls.link_freq, i); + + timings = ov5640_timings(sensor, mode); + hblank = timings->htot - mode->width; + __v4l2_ctrl_modify_range(sensor->ctrls.hblank, + hblank, hblank, 1, hblank); + + vblank = timings->vblank_def; + + if (sensor->current_fr != mode->def_fps) { + /* + * Compute the vertical blanking according to the framerate + * configured with s_frame_interval. + */ + int fie_num = sensor->frame_interval.numerator; + int fie_denom = sensor->frame_interval.denominator; + + vblank = ((fie_num * pixel_rate / fie_denom) / timings->htot) - + mode->height; + } + + __v4l2_ctrl_modify_range(sensor->ctrls.vblank, OV5640_MIN_VBLANK, + OV5640_MAX_VTS - mode->height, 1, vblank); + __v4l2_ctrl_s_ctrl(sensor->ctrls.vblank, vblank); + + exposure_max = timings->crop.height + vblank - 4; + exposure_val = clamp_t(s32, sensor->ctrls.exposure->val, + sensor->ctrls.exposure->minimum, + exposure_max); + + __v4l2_ctrl_modify_range(sensor->ctrls.exposure, + sensor->ctrls.exposure->minimum, + exposure_max, 1, exposure_val); + + return 0; +} + static int ov5640_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) @@ -2316,6 +2967,7 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd, } if (new_mode != sensor->current_mode) { + sensor->current_fr = new_mode->def_fps; sensor->current_mode = new_mode; sensor->pending_mode_change = true; } @@ -2325,80 +2977,70 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd, /* update format even if code is unchanged, resolution might change */ sensor->fmt = *mbus_fmt; - __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate, - ov5640_calc_pixel_rate(sensor)); + ov5640_update_pixel_rate(sensor); + out: mutex_unlock(&sensor->lock); return ret; } +static int ov5640_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + struct ov5640_dev *sensor = to_ov5640_dev(sd); + const struct ov5640_mode_info *mode = sensor->current_mode; + const struct ov5640_timings *timings; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: { + mutex_lock(&sensor->lock); + timings = ov5640_timings(sensor, mode); + sel->r = timings->analog_crop; + mutex_unlock(&sensor->lock); + + return 0; + } + + case V4L2_SEL_TGT_NATIVE_SIZE: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = OV5640_NATIVE_WIDTH; + sel->r.height = OV5640_NATIVE_HEIGHT; + + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + sel->r.top = OV5640_PIXEL_ARRAY_TOP; + sel->r.left = OV5640_PIXEL_ARRAY_LEFT; + sel->r.width = OV5640_PIXEL_ARRAY_WIDTH; + sel->r.height = OV5640_PIXEL_ARRAY_HEIGHT; + + return 0; + } + + return -EINVAL; +} + static int ov5640_set_framefmt(struct ov5640_dev *sensor, struct v4l2_mbus_framefmt *format) { + bool is_jpeg = format->code == MEDIA_BUS_FMT_JPEG_1X8; + const struct ov5640_pixfmt *pixfmt; int ret = 0; - bool is_jpeg = false; - u8 fmt, mux; - switch (format->code) { - case MEDIA_BUS_FMT_UYVY8_1X16: - case MEDIA_BUS_FMT_UYVY8_2X8: - /* YUV422, UYVY */ - fmt = 0x3f; - mux = OV5640_FMT_MUX_YUV422; - break; - case MEDIA_BUS_FMT_YUYV8_1X16: - case MEDIA_BUS_FMT_YUYV8_2X8: - /* YUV422, YUYV */ - fmt = 0x30; - mux = OV5640_FMT_MUX_YUV422; - break; - case MEDIA_BUS_FMT_RGB565_2X8_LE: - /* RGB565 {g[2:0],b[4:0]},{r[4:0],g[5:3]} */ - fmt = 0x6F; - mux = OV5640_FMT_MUX_RGB; - break; - case MEDIA_BUS_FMT_RGB565_2X8_BE: - /* RGB565 {r[4:0],g[5:3]},{g[2:0],b[4:0]} */ - fmt = 0x61; - mux = OV5640_FMT_MUX_RGB; - break; - case MEDIA_BUS_FMT_JPEG_1X8: - /* YUV422, YUYV */ - fmt = 0x30; - mux = OV5640_FMT_MUX_YUV422; - is_jpeg = true; - break; - case MEDIA_BUS_FMT_SBGGR8_1X8: - /* Raw, BGBG... / GRGR... */ - fmt = 0x00; - mux = OV5640_FMT_MUX_RAW_DPC; - break; - case MEDIA_BUS_FMT_SGBRG8_1X8: - /* Raw bayer, GBGB... / RGRG... */ - fmt = 0x01; - mux = OV5640_FMT_MUX_RAW_DPC; - break; - case MEDIA_BUS_FMT_SGRBG8_1X8: - /* Raw bayer, GRGR... / BGBG... */ - fmt = 0x02; - mux = OV5640_FMT_MUX_RAW_DPC; - break; - case MEDIA_BUS_FMT_SRGGB8_1X8: - /* Raw bayer, RGRG... / GBGB... */ - fmt = 0x03; - mux = OV5640_FMT_MUX_RAW_DPC; - break; - default: - return -EINVAL; - } + pixfmt = ov5640_code_to_pixfmt(sensor, format->code); /* FORMAT CONTROL00: YUV and RGB formatting */ - ret = ov5640_write_reg(sensor, OV5640_REG_FORMAT_CONTROL00, fmt); + ret = ov5640_write_reg(sensor, OV5640_REG_FORMAT_CONTROL00, + pixfmt->ctrl00); if (ret) return ret; /* FORMAT MUX CONTROL: ISP YUV or RGB */ - ret = ov5640_write_reg(sensor, OV5640_REG_ISP_FORMAT_MUX_CTRL, mux); + ret = ov5640_write_reg(sensor, OV5640_REG_ISP_FORMAT_MUX_CTRL, + pixfmt->mux); if (ret) return ret; @@ -2655,6 +3297,15 @@ static int ov5640_set_ctrl_vflip(struct ov5640_dev *sensor, int value) (BIT(2) | BIT(1)) : 0); } +static int ov5640_set_ctrl_vblank(struct ov5640_dev *sensor, int value) +{ + const struct ov5640_mode_info *mode = sensor->current_mode; + + /* Update the VTOT timing register value. */ + return ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS, + mode->height + value); +} + static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = ctrl_to_sd(ctrl); @@ -2685,10 +3336,25 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = ctrl_to_sd(ctrl); struct ov5640_dev *sensor = to_ov5640_dev(sd); + const struct ov5640_mode_info *mode = sensor->current_mode; + const struct ov5640_timings *timings; + unsigned int exp_max; int ret; /* v4l2_ctrl_lock() locks our own mutex */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update the exposure range to the newly programmed vblank. */ + timings = ov5640_timings(sensor, mode); + exp_max = mode->height + ctrl->val - 4; + __v4l2_ctrl_modify_range(sensor->ctrls.exposure, + sensor->ctrls.exposure->minimum, + exp_max, sensor->ctrls.exposure->step, + timings->vblank_def); + break; + } + /* * If the device is not powered up by the host driver do * not apply any controls to H/W at this time. Instead @@ -2728,6 +3394,9 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_VFLIP: ret = ov5640_set_ctrl_vflip(sensor, ctrl->val); break; + case V4L2_CID_VBLANK: + ret = ov5640_set_ctrl_vblank(sensor, ctrl->val); + break; default: ret = -EINVAL; break; @@ -2743,9 +3412,14 @@ static const struct v4l2_ctrl_ops ov5640_ctrl_ops = { static int ov5640_init_controls(struct ov5640_dev *sensor) { + const struct ov5640_mode_info *mode = sensor->current_mode; const struct v4l2_ctrl_ops *ops = &ov5640_ctrl_ops; struct ov5640_ctrls *ctrls = &sensor->ctrls; struct v4l2_ctrl_handler *hdl = &ctrls->handler; + struct v4l2_fwnode_device_properties props; + const struct ov5640_timings *timings; + unsigned int max_vblank; + unsigned int hblank; int ret; v4l2_ctrl_handler_init(hdl, 32); @@ -2755,8 +3429,25 @@ static int ov5640_init_controls(struct ov5640_dev *sensor) /* Clock related controls */ ctrls->pixel_rate = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_PIXEL_RATE, - 0, INT_MAX, 1, - ov5640_calc_pixel_rate(sensor)); + ov5640_pixel_rates[OV5640_NUM_PIXEL_RATES - 1], + ov5640_pixel_rates[0], 1, + ov5640_pixel_rates[mode->pixel_rate]); + + ctrls->link_freq = v4l2_ctrl_new_int_menu(hdl, ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(ov5640_csi2_link_freqs) - 1, + OV5640_DEFAULT_LINK_FREQ, + ov5640_csi2_link_freqs); + + timings = ov5640_timings(sensor, mode); + hblank = timings->htot - mode->width; + ctrls->hblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK, hblank, + hblank, 1, hblank); + + max_vblank = OV5640_MAX_VTS - mode->height; + ctrls->vblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK, + OV5640_MIN_VBLANK, max_vblank, + 1, timings->vblank_def); /* Auto/manual white balance */ ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops, @@ -2805,7 +3496,20 @@ static int ov5640_init_controls(struct ov5640_dev *sensor) goto free_ctrls; } + ret = v4l2_fwnode_device_parse(&sensor->i2c_client->dev, &props); + if (ret) + goto free_ctrls; + + if (props.rotation == 180) + sensor->upside_down = true; + + ret = v4l2_ctrl_new_fwnode_properties(hdl, ops, &props); + if (ret) + goto free_ctrls; + ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; + ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + ctrls->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE; ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; @@ -2825,16 +3529,29 @@ static int ov5640_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { + struct ov5640_dev *sensor = to_ov5640_dev(sd); + u32 bpp = ov5640_code_to_bpp(sensor, fse->code); + unsigned int index = fse->index; + if (fse->pad != 0) return -EINVAL; - if (fse->index >= OV5640_NUM_MODES) + if (!bpp) + return -EINVAL; + + /* Only low-resolution modes are supported for 24bpp formats. */ + if (bpp == 24 && index >= OV5640_MODE_720P_1280_720) + return -EINVAL; + + /* FIXME: Low resolution modes don't work in 8bpp formats. */ + if (bpp == 8) + index += OV5640_MODE_720P_1280_720; + + if (index >= OV5640_NUM_MODES) return -EINVAL; - fse->min_width = - ov5640_mode_data[fse->index].hact; + fse->min_width = ov5640_mode_data[index].width; fse->max_width = fse->min_width; - fse->min_height = - ov5640_mode_data[fse->index].vact; + fse->min_height = ov5640_mode_data[index].height; fse->max_height = fse->min_height; return 0; @@ -2898,20 +3615,25 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd, mode = sensor->current_mode; frame_rate = ov5640_try_frame_interval(sensor, &fi->interval, - mode->hact, mode->vact); + mode->width, + mode->height); if (frame_rate < 0) { /* Always return a valid frame interval value */ fi->interval = sensor->frame_interval; goto out; } - mode = ov5640_find_mode(sensor, frame_rate, mode->hact, - mode->vact, true); + mode = ov5640_find_mode(sensor, mode->width, mode->height, true); if (!mode) { ret = -EINVAL; goto out; } + if (ov5640_framerates[frame_rate] > ov5640_framerates[mode->max_fps]) { + ret = -EINVAL; + goto out; + } + if (mode != sensor->current_mode || frame_rate != sensor->current_fr) { sensor->current_fr = frame_rate; @@ -2919,8 +3641,7 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd, sensor->current_mode = mode; sensor->pending_mode_change = true; - __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate, - ov5640_calc_pixel_rate(sensor)); + ov5640_update_pixel_rate(sensor); } out: mutex_unlock(&sensor->lock); @@ -2931,12 +3652,23 @@ static int ov5640_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { - if (code->pad != 0) - return -EINVAL; - if (code->index >= ARRAY_SIZE(ov5640_formats)) + struct ov5640_dev *sensor = to_ov5640_dev(sd); + const struct ov5640_pixfmt *formats; + unsigned int num_formats; + + if (ov5640_is_csi2(sensor)) { + formats = ov5640_csi2_formats; + num_formats = ARRAY_SIZE(ov5640_csi2_formats) - 1; + } else { + formats = ov5640_dvp_formats; + num_formats = ARRAY_SIZE(ov5640_dvp_formats) - 1; + } + + if (code->index >= num_formats) return -EINVAL; - code->code = ov5640_formats[code->index].code; + code->code = formats[code->index].code; + return 0; } @@ -2961,7 +3693,7 @@ static int ov5640_s_stream(struct v4l2_subdev *sd, int enable) sensor->pending_fmt_change = false; } - if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY) + if (ov5640_is_csi2(sensor)) ret = ov5640_set_stream_mipi(sensor, enable); else ret = ov5640_set_stream_dvp(sensor, enable); @@ -2974,6 +3706,23 @@ out: return ret; } +static int ov5640_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_mbus_framefmt *fmt = + v4l2_subdev_get_try_format(sd, state, 0); + struct v4l2_rect *crop = v4l2_subdev_get_try_crop(sd, state, 0); + + *fmt = ov5640_default_fmt; + + crop->left = OV5640_PIXEL_ARRAY_LEFT; + crop->top = OV5640_PIXEL_ARRAY_TOP; + crop->width = OV5640_PIXEL_ARRAY_WIDTH; + crop->height = OV5640_PIXEL_ARRAY_HEIGHT; + + return 0; +} + static const struct v4l2_subdev_core_ops ov5640_core_ops = { .s_power = ov5640_s_power, .log_status = v4l2_ctrl_subdev_log_status, @@ -2988,9 +3737,11 @@ static const struct v4l2_subdev_video_ops ov5640_video_ops = { }; static const struct v4l2_subdev_pad_ops ov5640_pad_ops = { + .init_cfg = ov5640_init_cfg, .enum_mbus_code = ov5640_enum_mbus_code, .get_fmt = ov5640_get_fmt, .set_fmt = ov5640_set_fmt, + .get_selection = ov5640_get_selection, .enum_frame_size = ov5640_enum_frame_size, .enum_frame_interval = ov5640_enum_frame_interval, }; @@ -3046,8 +3797,6 @@ static int ov5640_probe(struct i2c_client *client) struct device *dev = &client->dev; struct fwnode_handle *endpoint; struct ov5640_dev *sensor; - struct v4l2_mbus_framefmt *fmt; - u32 rotation; int ret; sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); @@ -3060,40 +3809,17 @@ static int ov5640_probe(struct i2c_client *client) * default init sequence initialize sensor to * YUV422 UYVY VGA@30fps */ - fmt = &sensor->fmt; - fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; - fmt->colorspace = V4L2_COLORSPACE_SRGB; - fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); - fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; - fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); - fmt->width = 640; - fmt->height = 480; - fmt->field = V4L2_FIELD_NONE; + sensor->fmt = ov5640_default_fmt; sensor->frame_interval.numerator = 1; sensor->frame_interval.denominator = ov5640_framerates[OV5640_30_FPS]; sensor->current_fr = OV5640_30_FPS; sensor->current_mode = &ov5640_mode_data[OV5640_MODE_VGA_640_480]; sensor->last_mode = sensor->current_mode; + sensor->current_link_freq = OV5640_DEFAULT_LINK_FREQ; sensor->ae_target = 52; - /* optional indication of physical rotation of sensor */ - ret = fwnode_property_read_u32(dev_fwnode(&client->dev), "rotation", - &rotation); - if (!ret) { - switch (rotation) { - case 180: - sensor->upside_down = true; - fallthrough; - case 0: - break; - default: - dev_warn(dev, "%u degrees rotation is not supported, ignoring...\n", - rotation); - } - } - endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL); if (!endpoint) { diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c index 117ff5403312..82a9b2de7735 100644 --- a/drivers/media/i2c/ov5693.c +++ b/drivers/media/i2c/ov5693.c @@ -127,11 +127,16 @@ #define OV5693_LINK_FREQ_419_2MHZ 419200000 #define OV5693_PIXEL_RATE 167680000 -/* Miscellaneous */ -#define OV5693_NUM_SUPPLIES 2 - #define to_ov5693_sensor(x) container_of(x, struct ov5693_device, sd) +static const char * const ov5693_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital circuit power */ +}; + +#define OV5693_NUM_SUPPLIES ARRAY_SIZE(ov5693_supply_names) + struct ov5693_reg { u32 reg; u8 val; @@ -152,7 +157,7 @@ struct ov5693_device { struct gpio_desc *reset; struct gpio_desc *powerdown; struct regulator_bulk_data supplies[OV5693_NUM_SUPPLIES]; - struct clk *clk; + struct clk *xvclk; struct ov5693_mode { struct v4l2_rect crop; @@ -352,11 +357,6 @@ static const s64 link_freq_menu_items[] = { OV5693_LINK_FREQ_419_2MHZ }; -static const char * const ov5693_supply_names[] = { - "avdd", - "dovdd", -}; - static const char * const ov5693_test_pattern_menu[] = { "Disabled", "Random Data", @@ -794,7 +794,7 @@ static void ov5693_sensor_powerdown(struct ov5693_device *ov5693) regulator_bulk_disable(OV5693_NUM_SUPPLIES, ov5693->supplies); - clk_disable_unprepare(ov5693->clk); + clk_disable_unprepare(ov5693->xvclk); } static int ov5693_sensor_powerup(struct ov5693_device *ov5693) @@ -804,7 +804,7 @@ static int ov5693_sensor_powerup(struct ov5693_device *ov5693) gpiod_set_value_cansleep(ov5693->reset, 1); gpiod_set_value_cansleep(ov5693->powerdown, 1); - ret = clk_prepare_enable(ov5693->clk); + ret = clk_prepare_enable(ov5693->xvclk); if (ret) { dev_err(ov5693->dev, "Failed to enable clk\n"); goto fail_power; @@ -1390,7 +1390,7 @@ out_free_bus_cfg: static int ov5693_probe(struct i2c_client *client) { struct ov5693_device *ov5693; - u32 clk_rate; + u32 xvclk_rate; int ret = 0; ov5693 = devm_kzalloc(&client->dev, sizeof(*ov5693), GFP_KERNEL); @@ -1408,16 +1408,28 @@ static int ov5693_probe(struct i2c_client *client) v4l2_i2c_subdev_init(&ov5693->sd, client, &ov5693_ops); - ov5693->clk = devm_clk_get(&client->dev, "xvclk"); - if (IS_ERR(ov5693->clk)) { - dev_err(&client->dev, "Error getting clock\n"); - return PTR_ERR(ov5693->clk); + ov5693->xvclk = devm_clk_get_optional(&client->dev, "xvclk"); + if (IS_ERR(ov5693->xvclk)) + return dev_err_probe(&client->dev, PTR_ERR(ov5693->xvclk), + "failed to get xvclk: %ld\n", + PTR_ERR(ov5693->xvclk)); + + if (ov5693->xvclk) { + xvclk_rate = clk_get_rate(ov5693->xvclk); + } else { + ret = fwnode_property_read_u32(dev_fwnode(&client->dev), + "clock-frequency", + &xvclk_rate); + + if (ret) { + dev_err(&client->dev, "can't get clock frequency"); + return ret; + } } - clk_rate = clk_get_rate(ov5693->clk); - if (clk_rate != OV5693_XVCLK_FREQ) + if (xvclk_rate != OV5693_XVCLK_FREQ) dev_warn(&client->dev, "Found clk freq %u, expected %u\n", - clk_rate, OV5693_XVCLK_FREQ); + xvclk_rate, OV5693_XVCLK_FREQ); ret = ov5693_configure_gpios(ov5693); if (ret) @@ -1521,10 +1533,17 @@ static const struct acpi_device_id ov5693_acpi_match[] = { }; MODULE_DEVICE_TABLE(acpi, ov5693_acpi_match); +static const struct of_device_id ov5693_of_match[] = { + { .compatible = "ovti,ov5693", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, ov5693_of_match); + static struct i2c_driver ov5693_driver = { .driver = { .name = "ov5693", .acpi_match_table = ov5693_acpi_match, + .of_match_table = ov5693_of_match, .pm = &ov5693_pm_ops, }, .probe_new = ov5693_probe, diff --git a/drivers/media/i2c/ov7251.c b/drivers/media/i2c/ov7251.c index 0e7be15bc20a..1bd797c7926b 100644 --- a/drivers/media/i2c/ov7251.c +++ b/drivers/media/i2c/ov7251.c @@ -934,6 +934,8 @@ static int ov7251_set_power_on(struct device *dev) ARRAY_SIZE(ov7251_global_init_setting)); if (ret < 0) { dev_err(ov7251->dev, "error during global init\n"); + gpiod_set_value_cansleep(ov7251->enable_gpio, 0); + clk_disable_unprepare(ov7251->xclk); ov7251_regulators_disable(ov7251); return ret; } @@ -1340,7 +1342,7 @@ static int ov7251_s_stream(struct v4l2_subdev *subdev, int enable) if (enable) { ret = pm_runtime_get_sync(ov7251->dev); if (ret < 0) - goto unlock_out; + goto err_power_down; ret = ov7251_pll_configure(ov7251); if (ret) { @@ -1372,12 +1374,11 @@ static int ov7251_s_stream(struct v4l2_subdev *subdev, int enable) pm_runtime_put(ov7251->dev); } -unlock_out: mutex_unlock(&ov7251->lock); return ret; err_power_down: - pm_runtime_put_noidle(ov7251->dev); + pm_runtime_put(ov7251->dev); mutex_unlock(&ov7251->lock); return ret; } diff --git a/drivers/media/i2c/st-mipid02.c b/drivers/media/i2c/st-mipid02.c index ef976d085d72..16cc547976dd 100644 --- a/drivers/media/i2c/st-mipid02.c +++ b/drivers/media/i2c/st-mipid02.c @@ -50,6 +50,7 @@ /* Bits definition for MIPID02_MODE_REG2 */ #define MODE_HSYNC_ACTIVE_HIGH BIT(1) #define MODE_VSYNC_ACTIVE_HIGH BIT(2) +#define MODE_PCLK_SAMPLE_RISING BIT(3) /* Bits definition for MIPID02_DATA_SELECTION_CTRL */ #define SELECTION_MANUAL_DATA BIT(2) #define SELECTION_MANUAL_WIDTH BIT(3) @@ -61,9 +62,12 @@ static const u32 mipid02_supported_fmt_codes[] = { MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SRGGB10_1X10, MEDIA_BUS_FMT_SBGGR12_1X12, MEDIA_BUS_FMT_SGBRG12_1X12, MEDIA_BUS_FMT_SGRBG12_1X12, MEDIA_BUS_FMT_SRGGB12_1X12, - MEDIA_BUS_FMT_UYVY8_1X16, MEDIA_BUS_FMT_BGR888_1X24, + MEDIA_BUS_FMT_YUYV8_1X16, MEDIA_BUS_FMT_YVYU8_1X16, + MEDIA_BUS_FMT_UYVY8_1X16, MEDIA_BUS_FMT_VYUY8_1X16, + MEDIA_BUS_FMT_RGB565_1X16, MEDIA_BUS_FMT_BGR888_1X24, MEDIA_BUS_FMT_RGB565_2X8_LE, MEDIA_BUS_FMT_RGB565_2X8_BE, - MEDIA_BUS_FMT_YUYV8_2X8, MEDIA_BUS_FMT_UYVY8_2X8, + MEDIA_BUS_FMT_YUYV8_2X8, MEDIA_BUS_FMT_YVYU8_2X8, + MEDIA_BUS_FMT_UYVY8_2X8, MEDIA_BUS_FMT_VYUY8_2X8, MEDIA_BUS_FMT_JPEG_1X8 }; @@ -130,9 +134,15 @@ static int bpp_from_code(__u32 code) case MEDIA_BUS_FMT_SGRBG12_1X12: case MEDIA_BUS_FMT_SRGGB12_1X12: return 12; + case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_YVYU8_1X16: case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_VYUY8_1X16: + case MEDIA_BUS_FMT_RGB565_1X16: case MEDIA_BUS_FMT_YUYV8_2X8: + case MEDIA_BUS_FMT_YVYU8_2X8: case MEDIA_BUS_FMT_UYVY8_2X8: + case MEDIA_BUS_FMT_VYUY8_2X8: case MEDIA_BUS_FMT_RGB565_2X8_LE: case MEDIA_BUS_FMT_RGB565_2X8_BE: return 16; @@ -161,12 +171,18 @@ static u8 data_type_from_code(__u32 code) case MEDIA_BUS_FMT_SGRBG12_1X12: case MEDIA_BUS_FMT_SRGGB12_1X12: return 0x2c; + case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_YVYU8_1X16: case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_VYUY8_1X16: case MEDIA_BUS_FMT_YUYV8_2X8: + case MEDIA_BUS_FMT_YVYU8_2X8: case MEDIA_BUS_FMT_UYVY8_2X8: + case MEDIA_BUS_FMT_VYUY8_2X8: return 0x1e; case MEDIA_BUS_FMT_BGR888_1X24: return 0x24; + case MEDIA_BUS_FMT_RGB565_1X16: case MEDIA_BUS_FMT_RGB565_2X8_LE: case MEDIA_BUS_FMT_RGB565_2X8_BE: return 0x22; @@ -201,8 +217,16 @@ static __u32 get_fmt_code(__u32 code) static __u32 serial_to_parallel_code(__u32 serial) { + if (serial == MEDIA_BUS_FMT_RGB565_1X16) + return MEDIA_BUS_FMT_RGB565_2X8_LE; + if (serial == MEDIA_BUS_FMT_YUYV8_1X16) + return MEDIA_BUS_FMT_YUYV8_2X8; + if (serial == MEDIA_BUS_FMT_YVYU8_1X16) + return MEDIA_BUS_FMT_YVYU8_2X8; if (serial == MEDIA_BUS_FMT_UYVY8_1X16) return MEDIA_BUS_FMT_UYVY8_2X8; + if (serial == MEDIA_BUS_FMT_VYUY8_1X16) + return MEDIA_BUS_FMT_VYUY8_2X8; if (serial == MEDIA_BUS_FMT_BGR888_1X24) return MEDIA_BUS_FMT_BGR888_3X8; @@ -494,6 +518,8 @@ static int mipid02_configure_from_tx(struct mipid02_dev *bridge) bridge->r.mode_reg2 |= MODE_HSYNC_ACTIVE_HIGH; if (ep->bus.parallel.flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) bridge->r.mode_reg2 |= MODE_VSYNC_ACTIVE_HIGH; + if (ep->bus.parallel.flags & V4L2_MBUS_PCLK_SAMPLE_RISING) + bridge->r.mode_reg2 |= MODE_PCLK_SAMPLE_RISING; return 0; } diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c index 965c7afec57d..f66ac14cffad 100644 --- a/drivers/media/i2c/tda1997x.c +++ b/drivers/media/i2c/tda1997x.c @@ -2797,6 +2797,7 @@ err_free_mutex: cancel_delayed_work(&state->delayed_work_enable_hpd); mutex_destroy(&state->page_lock); mutex_destroy(&state->lock); + tda1997x_set_power(state, 0); err_free_state: kfree(state); dev_err(&client->dev, "%s failed: %d\n", __func__, ret); diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 65472438444b..93a980c4e899 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -1285,7 +1285,7 @@ static int tvp5150_disable_all_input_links(struct tvp5150 *decoder) int err; for (i = 0; i < TVP5150_NUM_PADS - 1; i++) { - connector_pad = media_entity_remote_pad(&decoder->pads[i]); + connector_pad = media_pad_remote_pad_first(&decoder->pads[i]); if (!connector_pad) continue; diff --git a/drivers/media/mc/mc-entity.c b/drivers/media/mc/mc-entity.c index 11f5207f73aa..afd1bd7ff7b6 100644 --- a/drivers/media/mc/mc-entity.c +++ b/drivers/media/mc/mc-entity.c @@ -9,6 +9,7 @@ */ #include <linux/bitmap.h> +#include <linux/list.h> #include <linux/property.h> #include <linux/slab.h> #include <media/media-entity.h> @@ -449,7 +450,7 @@ __must_check int __media_pipeline_start(struct media_entity *entity, bitmap_zero(active, entity->num_pads); bitmap_fill(has_no_links, entity->num_pads); - list_for_each_entry(link, &entity->links, list) { + for_each_media_entity_data_link(entity, link) { struct media_pad *pad = link->sink->entity == entity ? link->sink : link->source; @@ -888,7 +889,7 @@ media_entity_find_link(struct media_pad *source, struct media_pad *sink) { struct media_link *link; - list_for_each_entry(link, &source->entity->links, list) { + for_each_media_entity_data_link(source->entity, link) { if (link->source->entity == source->entity && link->source->index == source->index && link->sink->entity == sink->entity && @@ -900,11 +901,11 @@ media_entity_find_link(struct media_pad *source, struct media_pad *sink) } EXPORT_SYMBOL_GPL(media_entity_find_link); -struct media_pad *media_entity_remote_pad(const struct media_pad *pad) +struct media_pad *media_pad_remote_pad_first(const struct media_pad *pad) { struct media_link *link; - list_for_each_entry(link, &pad->entity->links, list) { + for_each_media_entity_data_link(pad->entity, link) { if (!(link->flags & MEDIA_LNK_FL_ENABLED)) continue; @@ -918,7 +919,77 @@ struct media_pad *media_entity_remote_pad(const struct media_pad *pad) return NULL; } -EXPORT_SYMBOL_GPL(media_entity_remote_pad); +EXPORT_SYMBOL_GPL(media_pad_remote_pad_first); + +struct media_pad * +media_entity_remote_pad_unique(const struct media_entity *entity, + unsigned int type) +{ + struct media_pad *pad = NULL; + struct media_link *link; + + list_for_each_entry(link, &entity->links, list) { + struct media_pad *local_pad; + struct media_pad *remote_pad; + + if (((link->flags & MEDIA_LNK_FL_LINK_TYPE) != + MEDIA_LNK_FL_DATA_LINK) || + !(link->flags & MEDIA_LNK_FL_ENABLED)) + continue; + + if (type == MEDIA_PAD_FL_SOURCE) { + local_pad = link->sink; + remote_pad = link->source; + } else { + local_pad = link->source; + remote_pad = link->sink; + } + + if (local_pad->entity == entity) { + if (pad) + return ERR_PTR(-ENOTUNIQ); + + pad = remote_pad; + } + } + + if (!pad) + return ERR_PTR(-ENOLINK); + + return pad; +} +EXPORT_SYMBOL_GPL(media_entity_remote_pad_unique); + +struct media_pad *media_pad_remote_pad_unique(const struct media_pad *pad) +{ + struct media_pad *found_pad = NULL; + struct media_link *link; + + list_for_each_entry(link, &pad->entity->links, list) { + struct media_pad *remote_pad; + + if (!(link->flags & MEDIA_LNK_FL_ENABLED)) + continue; + + if (link->sink == pad) + remote_pad = link->source; + else if (link->source == pad) + remote_pad = link->sink; + else + continue; + + if (found_pad) + return ERR_PTR(-ENOTUNIQ); + + found_pad = remote_pad; + } + + if (!found_pad) + return ERR_PTR(-ENOLINK); + + return found_pad; +} +EXPORT_SYMBOL_GPL(media_pad_remote_pad_unique); static void media_interface_init(struct media_device *mdev, struct media_interface *intf, @@ -1051,3 +1122,18 @@ struct media_link *media_create_ancillary_link(struct media_entity *primary, return link; } EXPORT_SYMBOL_GPL(media_create_ancillary_link); + +struct media_link *__media_entity_next_link(struct media_entity *entity, + struct media_link *link, + unsigned long link_type) +{ + link = link ? list_next_entry(link, list) + : list_first_entry(&entity->links, typeof(*link), list); + + list_for_each_entry_from(link, &entity->links, list) + if ((link->flags & MEDIA_LNK_FL_LINK_TYPE) == link_type) + return link; + + return NULL; +} +EXPORT_SYMBOL_GPL(__media_entity_next_link); diff --git a/drivers/media/pci/cx18/cx18-av-core.c b/drivers/media/pci/cx18/cx18-av-core.c index 76e5a504df8c..d3358643fb7d 100644 --- a/drivers/media/pci/cx18/cx18-av-core.c +++ b/drivers/media/pci/cx18/cx18-av-core.c @@ -180,7 +180,7 @@ static void cx18_av_initialize(struct v4l2_subdev *sd) */ cx18_av_and_or4(cx, CXADEC_CHIP_CTRL, 0xFFFBFFFF, 0x00120000); - /* Setup the Video and and Aux/Audio PLLs */ + /* Setup the Video and Aux/Audio PLLs */ cx18_av_init(cx); /* set video to auto-detect */ diff --git a/drivers/media/pci/cx88/cx88-core.c b/drivers/media/pci/cx88/cx88-core.c index 89d4d5a3ba34..52be42f9a7fa 100644 --- a/drivers/media/pci/cx88/cx88-core.c +++ b/drivers/media/pci/cx88/cx88-core.c @@ -618,12 +618,24 @@ EXPORT_SYMBOL(cx88_reset); static inline unsigned int norm_swidth(v4l2_std_id norm) { - return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 754 : 922; + if (norm & (V4L2_STD_NTSC | V4L2_STD_PAL_M)) + return 754; + + if (norm & V4L2_STD_PAL_Nc) + return 745; + + return 922; } static inline unsigned int norm_hdelay(v4l2_std_id norm) { - return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 135 : 186; + if (norm & (V4L2_STD_NTSC | V4L2_STD_PAL_M)) + return 135; + + if (norm & V4L2_STD_PAL_Nc) + return 149; + + return 186; } static inline unsigned int norm_vdelay(v4l2_std_id norm) @@ -636,7 +648,7 @@ static inline unsigned int norm_fsc8(v4l2_std_id norm) if (norm & V4L2_STD_PAL_M) return 28604892; // 3.575611 MHz - if (norm & (V4L2_STD_PAL_Nc)) + if (norm & V4L2_STD_PAL_Nc) return 28656448; // 3.582056 MHz if (norm & V4L2_STD_NTSC) // All NTSC/M and variants @@ -841,8 +853,8 @@ static int set_tvaudio(struct cx88_core *core) } else if (V4L2_STD_SECAM_DK & norm) { core->tvaudio = WW_DK; - } else if ((V4L2_STD_NTSC_M & norm) || - (V4L2_STD_PAL_M & norm)) { + } else if ((V4L2_STD_NTSC_M | V4L2_STD_PAL_M | V4L2_STD_PAL_Nc) & + norm) { core->tvaudio = WW_BTSC; } else if (V4L2_STD_NTSC_M_JP & norm) { diff --git a/drivers/media/pci/ddbridge/ddbridge-ci.c b/drivers/media/pci/ddbridge/ddbridge-ci.c index 377991095aba..ee20813c33ff 100644 --- a/drivers/media/pci/ddbridge/ddbridge-ci.c +++ b/drivers/media/pci/ddbridge/ddbridge-ci.c @@ -5,15 +5,6 @@ * Copyright (C) 2010-2017 Digital Devices GmbH * Marcus Metzler <mocm@metzlerbros.de> * Ralph Metzler <rjkm@metzlerbros.de> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 only, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include "ddbridge.h" diff --git a/drivers/media/pci/ddbridge/ddbridge-ci.h b/drivers/media/pci/ddbridge/ddbridge-ci.h index cc98656af349..41cd97e52aa1 100644 --- a/drivers/media/pci/ddbridge/ddbridge-ci.h +++ b/drivers/media/pci/ddbridge/ddbridge-ci.h @@ -5,15 +5,6 @@ * Copyright (C) 2010-2017 Digital Devices GmbH * Marcus Metzler <mocm@metzlerbros.de> * Ralph Metzler <rjkm@metzlerbros.de> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 only, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __DDBRIDGE_CI_H__ diff --git a/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c index 92fe051c672f..fe833f39698a 100644 --- a/drivers/media/pci/ddbridge/ddbridge-core.c +++ b/drivers/media/pci/ddbridge/ddbridge-core.c @@ -5,15 +5,6 @@ * Copyright (C) 2010-2017 Digital Devices GmbH * Marcus Metzler <mocm@metzlerbros.de> * Ralph Metzler <rjkm@metzlerbros.de> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 only, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/module.h> diff --git a/drivers/media/pci/ddbridge/ddbridge-hw.c b/drivers/media/pci/ddbridge/ddbridge-hw.c index f9c91bdbd041..d7d9cd0dad23 100644 --- a/drivers/media/pci/ddbridge/ddbridge-hw.c +++ b/drivers/media/pci/ddbridge/ddbridge-hw.c @@ -5,15 +5,6 @@ * Copyright (C) 2010-2017 Digital Devices GmbH * Ralph Metzler <rjkm@metzlerbros.de> * Marcus Metzler <mocm@metzlerbros.de> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 only, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include "ddbridge.h" diff --git a/drivers/media/pci/ddbridge/ddbridge-hw.h b/drivers/media/pci/ddbridge/ddbridge-hw.h index e34bd94c266b..934f296f48c0 100644 --- a/drivers/media/pci/ddbridge/ddbridge-hw.h +++ b/drivers/media/pci/ddbridge/ddbridge-hw.h @@ -5,15 +5,6 @@ * Copyright (C) 2010-2017 Digital Devices GmbH * Ralph Metzler <rjkm@metzlerbros.de> * Marcus Metzler <mocm@metzlerbros.de> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 only, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef _DDBRIDGE_HW_H_ @@ -40,4 +31,4 @@ struct ddb_device_id { const struct ddb_info *get_ddb_info(u16 vendor, u16 device, u16 subvendor, u16 subdevice); -#endif /* _DDBRIDGE_HW_H */ +#endif /* _DDBRIDGE_HW_H_ */ diff --git a/drivers/media/pci/ddbridge/ddbridge-i2c.c b/drivers/media/pci/ddbridge/ddbridge-i2c.c index aafa6030c8cc..c894be180446 100644 --- a/drivers/media/pci/ddbridge/ddbridge-i2c.c +++ b/drivers/media/pci/ddbridge/ddbridge-i2c.c @@ -5,15 +5,6 @@ * Copyright (C) 2010-2017 Digital Devices GmbH * Ralph Metzler <rjkm@metzlerbros.de> * Marcus Metzler <mocm@metzlerbros.de> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 only, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/module.h> diff --git a/drivers/media/pci/ddbridge/ddbridge-i2c.h b/drivers/media/pci/ddbridge/ddbridge-i2c.h index 90830f7b1638..48555d41aa48 100644 --- a/drivers/media/pci/ddbridge/ddbridge-i2c.h +++ b/drivers/media/pci/ddbridge/ddbridge-i2c.h @@ -5,15 +5,6 @@ * Copyright (C) 2010-2017 Digital Devices GmbH * Ralph Metzler <rjkm@metzlerbros.de> * Marcus Metzler <mocm@metzlerbros.de> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 only, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __DDBRIDGE_I2C_H__ diff --git a/drivers/media/pci/ddbridge/ddbridge-io.h b/drivers/media/pci/ddbridge/ddbridge-io.h index 1a5b31b52494..991246cecee2 100644 --- a/drivers/media/pci/ddbridge/ddbridge-io.h +++ b/drivers/media/pci/ddbridge/ddbridge-io.h @@ -5,15 +5,6 @@ * Copyright (C) 2010-2017 Digital Devices GmbH * Ralph Metzler <rjkm@metzlerbros.de> * Marcus Metzler <mocm@metzlerbros.de> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 only, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __DDBRIDGE_IO_H__ diff --git a/drivers/media/pci/ddbridge/ddbridge-main.c b/drivers/media/pci/ddbridge/ddbridge-main.c index 25d0d6745b52..91733ab9f58c 100644 --- a/drivers/media/pci/ddbridge/ddbridge-main.c +++ b/drivers/media/pci/ddbridge/ddbridge-main.c @@ -5,15 +5,6 @@ * Copyright (C) 2010-2017 Digital Devices GmbH * Ralph Metzler <rjkm@metzlerbros.de> * Marcus Metzler <mocm@metzlerbros.de> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 only, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/drivers/media/pci/ddbridge/ddbridge-max.c b/drivers/media/pci/ddbridge/ddbridge-max.c index 576dd2318e4d..0582b86bb869 100644 --- a/drivers/media/pci/ddbridge/ddbridge-max.c +++ b/drivers/media/pci/ddbridge/ddbridge-max.c @@ -5,15 +5,6 @@ * Copyright (C) 2010-2017 Digital Devices GmbH * Ralph Metzler <rjkm@metzlerbros.de> * Marcus Metzler <mocm@metzlerbros.de> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 only, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/module.h> diff --git a/drivers/media/pci/ddbridge/ddbridge-max.h b/drivers/media/pci/ddbridge/ddbridge-max.h index 6543dfc77138..da1553fe8695 100644 --- a/drivers/media/pci/ddbridge/ddbridge-max.h +++ b/drivers/media/pci/ddbridge/ddbridge-max.h @@ -5,15 +5,6 @@ * Copyright (C) 2010-2017 Digital Devices GmbH * Ralph Metzler <rjkm@metzlerbros.de> * Marcus Metzler <mocm@metzlerbros.de> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 only, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef _DDBRIDGE_MAX_H_ @@ -27,4 +18,4 @@ int ddb_lnb_init_fmode(struct ddb *dev, struct ddb_link *link, u32 fm); int ddb_fe_attach_mxl5xx(struct ddb_input *input); int ddb_fe_attach_mci(struct ddb_input *input, u32 type); -#endif /* _DDBRIDGE_MAX_H */ +#endif /* _DDBRIDGE_MAX_H_ */ diff --git a/drivers/media/pci/ddbridge/ddbridge-mci.c b/drivers/media/pci/ddbridge/ddbridge-mci.c index 97384ae9ad27..a006cb0fa199 100644 --- a/drivers/media/pci/ddbridge/ddbridge-mci.c +++ b/drivers/media/pci/ddbridge/ddbridge-mci.c @@ -5,15 +5,6 @@ * Copyright (C) 2017-2018 Digital Devices GmbH * Ralph Metzler <rjkm@metzlerbros.de> * Marcus Metzler <mocm@metzlerbros.de> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 only, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include "ddbridge.h" diff --git a/drivers/media/pci/ddbridge/ddbridge-mci.h b/drivers/media/pci/ddbridge/ddbridge-mci.h index 24241111c634..d9799fbf59d4 100644 --- a/drivers/media/pci/ddbridge/ddbridge-mci.h +++ b/drivers/media/pci/ddbridge/ddbridge-mci.h @@ -5,15 +5,6 @@ * Copyright (C) 2017-2018 Digital Devices GmbH * Marcus Metzler <mocm@metzlerbros.de> * Ralph Metzler <rjkm@metzlerbros.de> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 only, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef _DDBRIDGE_MCI_H_ diff --git a/drivers/media/pci/ddbridge/ddbridge-regs.h b/drivers/media/pci/ddbridge/ddbridge-regs.h index 2942a7f35099..42256fc9695d 100644 --- a/drivers/media/pci/ddbridge/ddbridge-regs.h +++ b/drivers/media/pci/ddbridge/ddbridge-regs.h @@ -3,15 +3,6 @@ * ddbridge-regs.h: Digital Devices PCIe bridge driver * * Copyright (C) 2010-2017 Digital Devices GmbH - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 only, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __DDBRIDGE_REGS_H__ diff --git a/drivers/media/pci/ddbridge/ddbridge-sx8.c b/drivers/media/pci/ddbridge/ddbridge-sx8.c index 374fcee94960..c8de8d283f85 100644 --- a/drivers/media/pci/ddbridge/ddbridge-sx8.c +++ b/drivers/media/pci/ddbridge/ddbridge-sx8.c @@ -5,15 +5,6 @@ * Copyright (C) 2018 Digital Devices GmbH * Marcus Metzler <mocm@metzlerbros.de> * Ralph Metzler <rjkm@metzlerbros.de> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 only, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include "ddbridge.h" diff --git a/drivers/media/pci/ddbridge/ddbridge.h b/drivers/media/pci/ddbridge/ddbridge.h index b834449e78f8..f3699dbd193f 100644 --- a/drivers/media/pci/ddbridge/ddbridge.h +++ b/drivers/media/pci/ddbridge/ddbridge.h @@ -4,15 +4,6 @@ * * Copyright (C) 2010-2017 Digital Devices GmbH * Ralph Metzler <rmetzler@digitaldevices.de> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 only, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef _DDBRIDGE_H_ @@ -379,4 +370,4 @@ void ddb_unmap(struct ddb *dev); int ddb_exit_ddbridge(int stage, int error); int ddb_init_ddbridge(void); -#endif /* DDBRIDGE_H */ +#endif /* _DDBRIDGE_H_ */ diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c index dbdbdb648a0d..a3fe547b7fce 100644 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2-main.c @@ -1323,7 +1323,7 @@ static int cio2_video_link_validate(struct media_link *link) struct v4l2_subdev_format source_fmt; int ret; - if (!media_entity_remote_pad(entity->pads)) { + if (!media_pad_remote_pad_first(entity->pads)) { dev_info(dev, "video node %s pad not connected\n", vd->name); return -ENOTCONN; } diff --git a/drivers/media/pci/saa7164/saa7164-api.c b/drivers/media/pci/saa7164/saa7164-api.c index 5526bcc7a9bd..965d285a9240 100644 --- a/drivers/media/pci/saa7164/saa7164-api.c +++ b/drivers/media/pci/saa7164/saa7164-api.c @@ -801,7 +801,7 @@ int saa7164_api_read_eeprom(struct saa7164_dev *dev, u8 *buf, int buflen) if (buflen < 128) return -ENOMEM; - /* Assumption: Hauppauge eeprom is at 0xa0 on on bus 0 */ + /* Assumption: Hauppauge eeprom is at 0xa0 on bus 0 */ /* TODO: Pull the details from the boards struct */ return saa7164_api_i2c_read(&dev->i2c_bus[0], 0xa0 >> 1, sizeof(reg), ®[0], 128, buf); diff --git a/drivers/media/pci/sta2x11/Kconfig b/drivers/media/pci/sta2x11/Kconfig index a96e170ab04e..118b922c08c3 100644 --- a/drivers/media/pci/sta2x11/Kconfig +++ b/drivers/media/pci/sta2x11/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config STA2X11_VIP tristate "STA2X11 VIP Video For Linux" - depends on PCI && VIDEO_DEV && VIRT_TO_BUS && I2C + depends on PCI && VIDEO_DEV && I2C depends on STA2X11 || COMPILE_TEST select GPIOLIB if MEDIA_SUBDRV_AUTOSELECT select VIDEO_ADV7180 if MEDIA_SUBDRV_AUTOSELECT diff --git a/drivers/media/pci/tw5864/tw5864-core.c b/drivers/media/pci/tw5864/tw5864-core.c index 5cae73e6fb9c..560ff1ddcc83 100644 --- a/drivers/media/pci/tw5864/tw5864-core.c +++ b/drivers/media/pci/tw5864/tw5864-core.c @@ -254,9 +254,9 @@ static int tw5864_initdev(struct pci_dev *pci_dev, /* pci init */ dev->pci = pci_dev; - err = pci_enable_device(pci_dev); + err = pcim_enable_device(pci_dev); if (err) { - dev_err(&dev->pci->dev, "pci_enable_device() failed\n"); + dev_err(&dev->pci->dev, "pcim_enable_device() failed\n"); goto unreg_v4l2; } @@ -265,21 +265,16 @@ static int tw5864_initdev(struct pci_dev *pci_dev, err = dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(32)); if (err) { dev_err(&dev->pci->dev, "32 bit PCI DMA is not supported\n"); - goto disable_pci; + goto unreg_v4l2; } /* get mmio */ - err = pci_request_regions(pci_dev, dev->name); + err = pcim_iomap_regions(pci_dev, BIT(0), dev->name); if (err) { dev_err(&dev->pci->dev, "Cannot request regions for MMIO\n"); - goto disable_pci; - } - dev->mmio = pci_ioremap_bar(pci_dev, 0); - if (!dev->mmio) { - err = -EIO; - dev_err(&dev->pci->dev, "can't ioremap() MMIO memory\n"); - goto release_mmio; + goto unreg_v4l2; } + dev->mmio = pcim_iomap_table(pci_dev)[0]; spin_lock_init(&dev->slock); @@ -291,7 +286,7 @@ static int tw5864_initdev(struct pci_dev *pci_dev, err = tw5864_video_init(dev, video_nr); if (err) - goto unmap_mmio; + goto unreg_v4l2; /* get irq */ err = devm_request_irq(&pci_dev->dev, pci_dev->irq, tw5864_isr, @@ -308,12 +303,6 @@ static int tw5864_initdev(struct pci_dev *pci_dev, fini_video: tw5864_video_fini(dev); -unmap_mmio: - iounmap(dev->mmio); -release_mmio: - pci_release_regions(pci_dev); -disable_pci: - pci_disable_device(pci_dev); unreg_v4l2: v4l2_device_unregister(&dev->v4l2_dev); return err; @@ -331,11 +320,6 @@ static void tw5864_finidev(struct pci_dev *pci_dev) /* unregister */ tw5864_video_fini(dev); - /* release resources */ - iounmap(dev->mmio); - pci_release_regions(pci_dev); - pci_disable_device(pci_dev); - v4l2_device_unregister(&dev->v4l2_dev); } diff --git a/drivers/media/pci/tw686x/tw686x-core.c b/drivers/media/pci/tw686x/tw686x-core.c index 6676e069b515..c53099c958ca 100644 --- a/drivers/media/pci/tw686x/tw686x-core.c +++ b/drivers/media/pci/tw686x/tw686x-core.c @@ -315,13 +315,6 @@ static int tw686x_probe(struct pci_dev *pci_dev, spin_lock_init(&dev->lock); - err = request_irq(pci_dev->irq, tw686x_irq, IRQF_SHARED, - dev->name, dev); - if (err < 0) { - dev_err(&pci_dev->dev, "unable to request interrupt\n"); - goto iounmap; - } - timer_setup(&dev->dma_delay_timer, tw686x_dma_delay, 0); /* @@ -333,18 +326,26 @@ static int tw686x_probe(struct pci_dev *pci_dev, err = tw686x_video_init(dev); if (err) { dev_err(&pci_dev->dev, "can't register video\n"); - goto free_irq; + goto iounmap; } err = tw686x_audio_init(dev); if (err) dev_warn(&pci_dev->dev, "can't register audio\n"); + err = request_irq(pci_dev->irq, tw686x_irq, IRQF_SHARED, + dev->name, dev); + if (err < 0) { + dev_err(&pci_dev->dev, "unable to request interrupt\n"); + goto tw686x_free; + } + pci_set_drvdata(pci_dev, dev); return 0; -free_irq: - free_irq(pci_dev->irq, dev); +tw686x_free: + tw686x_video_free(dev); + tw686x_audio_free(dev); iounmap: pci_iounmap(pci_dev, dev->mmio); free_region: diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c index 6344a479119f..3ebf7a2c95f0 100644 --- a/drivers/media/pci/tw686x/tw686x-video.c +++ b/drivers/media/pci/tw686x/tw686x-video.c @@ -1280,8 +1280,10 @@ int tw686x_video_init(struct tw686x_dev *dev) video_set_drvdata(vdev, vc); err = video_register_device(vdev, VFL_TYPE_VIDEO, -1); - if (err < 0) + if (err < 0) { + video_device_release(vdev); goto error; + } vc->num = vdev->num; } diff --git a/drivers/media/platform/amphion/vdec.c b/drivers/media/platform/amphion/vdec.c index 3c02aa2a54aa..9e64041cc1c1 100644 --- a/drivers/media/platform/amphion/vdec.c +++ b/drivers/media/platform/amphion/vdec.c @@ -63,6 +63,7 @@ struct vdec_t { bool is_source_changed; u32 source_change; u32 drain; + bool aborting; }; static const struct vpu_format vdec_formats[] = { @@ -104,7 +105,6 @@ static const struct vpu_format vdec_formats[] = { .pixfmt = V4L2_PIX_FMT_VC1_ANNEX_L, .num_planes = 1, .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - .flags = V4L2_FMT_FLAG_DYN_RESOLUTION }, { .pixfmt = V4L2_PIX_FMT_MPEG2, @@ -178,16 +178,6 @@ static int vdec_ctrl_init(struct vpu_inst *inst) return 0; } -static void vdec_set_last_buffer_dequeued(struct vpu_inst *inst) -{ - struct vdec_t *vdec = inst->priv; - - if (vdec->eos_received) { - if (!vpu_set_last_buffer_dequeued(inst)) - vdec->eos_received--; - } -} - static void vdec_handle_resolution_change(struct vpu_inst *inst) { struct vdec_t *vdec = inst->priv; @@ -234,6 +224,21 @@ static int vdec_update_state(struct vpu_inst *inst, enum vpu_codec_state state, return 0; } +static void vdec_set_last_buffer_dequeued(struct vpu_inst *inst) +{ + struct vdec_t *vdec = inst->priv; + + if (inst->state == VPU_CODEC_STATE_DYAMIC_RESOLUTION_CHANGE) + return; + + if (vdec->eos_received) { + if (!vpu_set_last_buffer_dequeued(inst)) { + vdec->eos_received--; + vdec_update_state(inst, VPU_CODEC_STATE_DRAIN, 0); + } + } +} + static int vdec_querycap(struct file *file, void *fh, struct v4l2_capability *cap) { strscpy(cap->driver, "amphion-vpu", sizeof(cap->driver)); @@ -493,6 +498,8 @@ static int vdec_drain(struct vpu_inst *inst) static int vdec_cmd_start(struct vpu_inst *inst) { + struct vdec_t *vdec = inst->priv; + switch (inst->state) { case VPU_CODEC_STATE_STARTED: case VPU_CODEC_STATE_DRAIN: @@ -503,6 +510,8 @@ static int vdec_cmd_start(struct vpu_inst *inst) break; } vpu_process_capture_buffer(inst); + if (vdec->eos_received) + vdec_set_last_buffer_dequeued(inst); return 0; } @@ -731,6 +740,7 @@ static void vdec_stop_done(struct vpu_inst *inst) vdec->eos_received = 0; vdec->is_source_changed = false; vdec->source_change = 0; + inst->total_input_count = 0; vpu_inst_unlock(inst); } @@ -939,6 +949,9 @@ static int vdec_response_frame(struct vpu_inst *inst, struct vb2_v4l2_buffer *vb if (inst->state != VPU_CODEC_STATE_ACTIVE) return -EINVAL; + if (vdec->aborting) + return -EINVAL; + if (!vdec->req_frame_count) return -EINVAL; @@ -1048,6 +1061,8 @@ static void vdec_clear_slots(struct vpu_inst *inst) vpu_buf = vdec->slots[i]; vbuf = &vpu_buf->m2m_buf.vb; + vpu_trace(inst->dev, "clear slot %d\n", i); + vdec_response_fs_release(inst, i, vpu_buf->tag); vdec_recycle_buffer(inst, vbuf); vdec->slots[i]->state = VPU_BUF_STATE_IDLE; vdec->slots[i] = NULL; @@ -1203,7 +1218,6 @@ static void vdec_event_eos(struct vpu_inst *inst) vdec->eos_received++; vdec->fixed_fmt = false; inst->min_buffer_cap = VDEC_MIN_BUFFER_CAP; - vdec_update_state(inst, VPU_CODEC_STATE_DRAIN, 0); vdec_set_last_buffer_dequeued(inst); vpu_inst_unlock(inst); } @@ -1310,6 +1324,8 @@ static void vdec_abort(struct vpu_inst *inst) int ret; vpu_trace(inst->dev, "[%d] state = %d\n", inst->id, inst->state); + + vdec->aborting = true; vpu_iface_add_scode(inst, SCODE_PADDING_ABORT); vdec->params.end_flag = 1; vpu_iface_set_decode_params(inst, &vdec->params, 1); @@ -1333,6 +1349,7 @@ static void vdec_abort(struct vpu_inst *inst) vdec->decoded_frame_count = 0; vdec->display_frame_count = 0; vdec->sequence = 0; + vdec->aborting = false; } static void vdec_stop(struct vpu_inst *inst, bool free) @@ -1369,8 +1386,7 @@ static void vdec_cleanup(struct vpu_inst *inst) return; vdec = inst->priv; - if (vdec) - vfree(vdec); + vfree(vdec); inst->priv = NULL; vfree(inst); } @@ -1480,10 +1496,10 @@ static int vdec_stop_session(struct vpu_inst *inst, u32 type) vdec_update_state(inst, VPU_CODEC_STATE_SEEK, 0); vdec->drain = 0; } else { - if (inst->state != VPU_CODEC_STATE_DYAMIC_RESOLUTION_CHANGE) + if (inst->state != VPU_CODEC_STATE_DYAMIC_RESOLUTION_CHANGE) { vdec_abort(inst); - - vdec->eos_received = 0; + vdec->eos_received = 0; + } vdec_clear_slots(inst); } diff --git a/drivers/media/platform/amphion/venc.c b/drivers/media/platform/amphion/venc.c index 43d61d82f58c..461524dd1e44 100644 --- a/drivers/media/platform/amphion/venc.c +++ b/drivers/media/platform/amphion/venc.c @@ -919,8 +919,7 @@ static void venc_cleanup(struct vpu_inst *inst) return; venc = inst->priv; - if (venc) - vfree(venc); + vfree(venc); inst->priv = NULL; vfree(inst); } diff --git a/drivers/media/platform/amphion/vpu.h b/drivers/media/platform/amphion/vpu.h index e56b96a7e5d3..f914de6ed81e 100644 --- a/drivers/media/platform/amphion/vpu.h +++ b/drivers/media/platform/amphion/vpu.h @@ -258,6 +258,7 @@ struct vpu_inst { struct vpu_format cap_format; u32 min_buffer_cap; u32 min_buffer_out; + u32 total_input_count; struct v4l2_rect crop; u32 colorspace; diff --git a/drivers/media/platform/amphion/vpu_cmds.c b/drivers/media/platform/amphion/vpu_cmds.c index 9b39d77a178d..f4d7ca78a621 100644 --- a/drivers/media/platform/amphion/vpu_cmds.c +++ b/drivers/media/platform/amphion/vpu_cmds.c @@ -117,8 +117,7 @@ static void vpu_free_cmd(struct vpu_cmd_t *cmd) { if (!cmd) return; - if (cmd->pkt) - vfree(cmd->pkt); + vfree(cmd->pkt); vfree(cmd); } diff --git a/drivers/media/platform/amphion/vpu_core.c b/drivers/media/platform/amphion/vpu_core.c index 68ad183925fd..73faa50d2865 100644 --- a/drivers/media/platform/amphion/vpu_core.c +++ b/drivers/media/platform/amphion/vpu_core.c @@ -257,14 +257,8 @@ static int vpu_core_register(struct device *dev, struct vpu_core *core) } list_add_tail(&core->list, &vpu->cores); - vpu_core_get_vpu(core); - if (vpu_iface_get_power_state(core)) - ret = vpu_core_restore(core); - if (ret) - goto error; - return 0; error: if (core->msg_buffer) { @@ -362,7 +356,10 @@ struct vpu_core *vpu_request_core(struct vpu_dev *vpu, enum vpu_core_type type) pm_runtime_resume_and_get(core->dev); if (core->state == VPU_CORE_DEINIT) { - ret = vpu_core_boot(core, true); + if (vpu_iface_get_power_state(core)) + ret = vpu_core_restore(core); + else + ret = vpu_core_boot(core, true); if (ret) { pm_runtime_put_sync(core->dev); mutex_unlock(&core->lock); @@ -455,8 +452,13 @@ int vpu_inst_unregister(struct vpu_inst *inst) } vpu_core_check_hang(core); if (core->state == VPU_CORE_HANG && !core->instance_mask) { + int err; + dev_info(core->dev, "reset hang core\n"); - if (!vpu_core_sw_reset(core)) { + mutex_unlock(&core->lock); + err = vpu_core_sw_reset(core); + mutex_lock(&core->lock); + if (!err) { core->state = VPU_CORE_ACTIVE; core->hang_mask = 0; } diff --git a/drivers/media/platform/amphion/vpu_dbg.c b/drivers/media/platform/amphion/vpu_dbg.c index da62bd718fb8..f72c8a506b22 100644 --- a/drivers/media/platform/amphion/vpu_dbg.c +++ b/drivers/media/platform/amphion/vpu_dbg.c @@ -27,7 +27,7 @@ struct print_buf_desc { u32 bytes; u32 read; u32 write; - char buffer[0]; + char buffer[]; }; static char *vb2_stat_name[] = { diff --git a/drivers/media/platform/amphion/vpu_malone.c b/drivers/media/platform/amphion/vpu_malone.c index f29c223eefce..f4a488bf9880 100644 --- a/drivers/media/platform/amphion/vpu_malone.c +++ b/drivers/media/platform/amphion/vpu_malone.c @@ -309,6 +309,7 @@ struct malone_padding_scode { struct malone_fmt_mapping { u32 pixelformat; enum vpu_malone_format malone_format; + u32 is_disabled; }; struct malone_scode_t { @@ -568,6 +569,8 @@ static enum vpu_malone_format vpu_malone_format_remap(u32 pixelformat) u32 i; for (i = 0; i < ARRAY_SIZE(fmt_mappings); i++) { + if (fmt_mappings[i].is_disabled) + continue; if (pixelformat == fmt_mappings[i].pixelformat) return fmt_mappings[i].malone_format; } @@ -575,6 +578,19 @@ static enum vpu_malone_format vpu_malone_format_remap(u32 pixelformat) return MALONE_FMT_NULL; } +bool vpu_malone_check_fmt(enum vpu_core_type type, u32 pixelfmt) +{ + if (!vpu_imx8q_check_fmt(type, pixelfmt)) + return false; + + if (pixelfmt == V4L2_PIX_FMT_NV12M_8L128 || pixelfmt == V4L2_PIX_FMT_NV12M_10BE_8L128) + return true; + if (vpu_malone_format_remap(pixelfmt) == MALONE_FMT_NULL) + return false; + + return true; +} + static void vpu_malone_set_stream_cfg(struct vpu_shared_addr *shared, u32 instance, enum vpu_malone_format malone_format) @@ -610,6 +626,8 @@ static int vpu_malone_set_params(struct vpu_shared_addr *shared, enum vpu_malone_format malone_format; malone_format = vpu_malone_format_remap(params->codec_format); + if (WARN_ON(malone_format == MALONE_FMT_NULL)) + return -EINVAL; iface->udata_buffer[instance].base = params->udata.base; iface->udata_buffer[instance].slot_size = params->udata.size; @@ -1296,6 +1314,8 @@ static int vpu_malone_insert_scode_vc1_l_seq(struct malone_scode_t *scode) int size = 0; u8 rcv_seqhdr[MALONE_VC1_RCV_SEQ_HEADER_LEN]; + if (scode->inst->total_input_count) + return 0; scode->need_data = 0; ret = vpu_malone_insert_scode_seq(scode, MALONE_CODEC_ID_VC1_SIMPLE, sizeof(rcv_seqhdr)); diff --git a/drivers/media/platform/amphion/vpu_malone.h b/drivers/media/platform/amphion/vpu_malone.h index e5a5cbe9843e..02a9d9530970 100644 --- a/drivers/media/platform/amphion/vpu_malone.h +++ b/drivers/media/platform/amphion/vpu_malone.h @@ -40,5 +40,6 @@ int vpu_malone_pre_cmd(struct vpu_shared_addr *shared, u32 instance); int vpu_malone_post_cmd(struct vpu_shared_addr *shared, u32 instance); int vpu_malone_init_instance(struct vpu_shared_addr *shared, u32 instance); u32 vpu_malone_get_max_instance_count(struct vpu_shared_addr *shared); +bool vpu_malone_check_fmt(enum vpu_core_type type, u32 pixelfmt); #endif diff --git a/drivers/media/platform/amphion/vpu_msgs.c b/drivers/media/platform/amphion/vpu_msgs.c index d5850df8f1d5..d8247f36d84b 100644 --- a/drivers/media/platform/amphion/vpu_msgs.c +++ b/drivers/media/platform/amphion/vpu_msgs.c @@ -150,7 +150,12 @@ static void vpu_session_handle_eos(struct vpu_inst *inst, struct vpu_rpc_event * static void vpu_session_handle_error(struct vpu_inst *inst, struct vpu_rpc_event *pkt) { - dev_err(inst->dev, "unsupported stream\n"); + char *str = (char *)pkt->data; + + if (strlen(str)) + dev_err(inst->dev, "instance %d firmware error : %s\n", inst->id, str); + else + dev_err(inst->dev, "instance %d is unsupported stream\n", inst->id); call_void_vop(inst, event_notify, VPU_MSG_ID_UNSUPPORTED, NULL); vpu_v4l2_set_error(inst); } diff --git a/drivers/media/platform/amphion/vpu_rpc.c b/drivers/media/platform/amphion/vpu_rpc.c index 18a164766409..676f7da041bd 100644 --- a/drivers/media/platform/amphion/vpu_rpc.c +++ b/drivers/media/platform/amphion/vpu_rpc.c @@ -195,7 +195,7 @@ static struct vpu_iface_ops imx8q_rpc_ops[] = { }, [VPU_CORE_TYPE_DEC] = { .check_codec = vpu_imx8q_check_codec, - .check_fmt = vpu_imx8q_check_fmt, + .check_fmt = vpu_malone_check_fmt, .boot_core = vpu_imx8q_boot_core, .get_power_state = vpu_imx8q_get_power_state, .on_firmware_loaded = vpu_imx8q_on_firmware_loaded, diff --git a/drivers/media/platform/amphion/vpu_rpc.h b/drivers/media/platform/amphion/vpu_rpc.h index 25119e5e807e..7eb6f01e6ab5 100644 --- a/drivers/media/platform/amphion/vpu_rpc.h +++ b/drivers/media/platform/amphion/vpu_rpc.h @@ -312,11 +312,16 @@ static inline int vpu_iface_input_frame(struct vpu_inst *inst, struct vb2_buffer *vb) { struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core); + int ret; if (!ops || !ops->input_frame) return -EINVAL; - return ops->input_frame(inst->core->iface, inst, vb); + ret = ops->input_frame(inst->core->iface, inst, vb); + if (ret < 0) + return ret; + inst->total_input_count++; + return ret; } static inline int vpu_iface_config_memory_resource(struct vpu_inst *inst, diff --git a/drivers/media/platform/amphion/vpu_v4l2.c b/drivers/media/platform/amphion/vpu_v4l2.c index 446f07d09d0b..8a3eed957ae6 100644 --- a/drivers/media/platform/amphion/vpu_v4l2.c +++ b/drivers/media/platform/amphion/vpu_v4l2.c @@ -500,10 +500,12 @@ static int vpu_vb2_start_streaming(struct vb2_queue *q, unsigned int count) fmt->sizeimage[1], fmt->bytesperline[1], fmt->sizeimage[2], fmt->bytesperline[2], q->num_buffers); - call_void_vop(inst, start, q->type); vb2_clear_last_buffer_dequeued(q); + ret = call_vop(inst, start, q->type); + if (ret) + vpu_vb2_buffers_return(inst, q->type, VB2_BUF_STATE_QUEUED); - return 0; + return ret; } static void vpu_vb2_stop_streaming(struct vb2_queue *q) diff --git a/drivers/media/platform/atmel/Kconfig b/drivers/media/platform/atmel/Kconfig index 83aebee0c8eb..f399dba62e17 100644 --- a/drivers/media/platform/atmel/Kconfig +++ b/drivers/media/platform/atmel/Kconfig @@ -20,12 +20,14 @@ config VIDEO_ATMEL_ISC config VIDEO_ATMEL_XISC tristate "ATMEL eXtended Image Sensor Controller (XISC) support" depends on V4L_PLATFORM_DRIVERS - depends on VIDEO_DEV && COMMON_CLK && VIDEO_V4L2_SUBDEV_API + depends on VIDEO_DEV && COMMON_CLK depends on ARCH_AT91 || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG select REGMAP_MMIO select V4L2_FWNODE select VIDEO_ATMEL_ISC_BASE + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API help This module makes the ATMEL eXtended Image Sensor Controller available as a v4l2 device. diff --git a/drivers/media/platform/atmel/atmel-isc-base.c b/drivers/media/platform/atmel/atmel-isc-base.c index 2f07a50035c8..9e5317a7d516 100644 --- a/drivers/media/platform/atmel/atmel-isc-base.c +++ b/drivers/media/platform/atmel/atmel-isc-base.c @@ -132,12 +132,9 @@ static int isc_buffer_prepare(struct vb2_buffer *vb) return 0; } -static void isc_start_dma(struct isc_device *isc) +static void isc_crop_pfe(struct isc_device *isc) { struct regmap *regmap = isc->regmap; - u32 sizeimage = isc->fmt.fmt.pix.sizeimage; - u32 dctrl_dview; - dma_addr_t addr0; u32 h, w; h = isc->fmt.fmt.pix.height; @@ -172,6 +169,14 @@ static void isc_start_dma(struct isc_device *isc) regmap_update_bits(regmap, ISC_PFE_CFG0, ISC_PFE_CFG0_COLEN | ISC_PFE_CFG0_ROWEN, ISC_PFE_CFG0_COLEN | ISC_PFE_CFG0_ROWEN); +} + +static void isc_start_dma(struct isc_device *isc) +{ + struct regmap *regmap = isc->regmap; + u32 sizeimage = isc->fmt.fmt.pix.sizeimage; + u32 dctrl_dview; + dma_addr_t addr0; addr0 = vb2_dma_contig_plane_dma_addr(&isc->cur_frm->vb.vb2_buf, 0); regmap_write(regmap, ISC_DAD0 + isc->offsets.dma, addr0); @@ -369,6 +374,7 @@ static int isc_start_streaming(struct vb2_queue *vq, unsigned int count) struct isc_buffer, list); list_del(&isc->cur_frm->list); + isc_crop_pfe(isc); isc_start_dma(isc); spin_unlock_irqrestore(&isc->dma_queue_lock, flags); @@ -1466,7 +1472,7 @@ static void isc_awb_work(struct work_struct *w) if (isc->stop) { mutex_unlock(&isc->awb_mutex); return; - }; + } isc_update_profile(isc); @@ -1525,10 +1531,6 @@ static int isc_s_awb_ctrl(struct v4l2_ctrl *ctrl) else ctrls->awb = ISC_WB_NONE; - /* we did not configure ISC yet */ - if (!isc->config.sd_format) - break; - /* configure the controls with new values from v4l2 */ if (ctrl->cluster[ISC_CTRL_R_GAIN]->is_new) ctrls->gain[ISC_HIS_CFG_MODE_R] = isc->r_gain_ctrl->val; diff --git a/drivers/media/platform/atmel/atmel-sama7g5-isc.c b/drivers/media/platform/atmel/atmel-sama7g5-isc.c index 83b175070c06..8b11aa8340d7 100644 --- a/drivers/media/platform/atmel/atmel-sama7g5-isc.c +++ b/drivers/media/platform/atmel/atmel-sama7g5-isc.c @@ -591,11 +591,13 @@ static const struct dev_pm_ops microchip_xisc_dev_pm_ops = { SET_RUNTIME_PM_OPS(xisc_runtime_suspend, xisc_runtime_resume, NULL) }; +#if IS_ENABLED(CONFIG_OF) static const struct of_device_id microchip_xisc_of_match[] = { { .compatible = "microchip,sama7g5-isc" }, { } }; MODULE_DEVICE_TABLE(of, microchip_xisc_of_match); +#endif static struct platform_driver microchip_xisc_driver = { .probe = microchip_xisc_probe, diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c index bc5b0a0168ec..87685a62a5c2 100644 --- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c @@ -1369,6 +1369,9 @@ static int mtk_jpeg_probe(struct platform_device *pdev) jpeg->vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; + if (of_get_property(pdev->dev.of_node, "dma-ranges", NULL)) + dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(34)); + ret = video_register_device(jpeg->vdev, VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n"); diff --git a/drivers/media/platform/mediatek/mdp/mtk_mdp_ipi.h b/drivers/media/platform/mediatek/mdp/mtk_mdp_ipi.h index 2cb8cecb3077..b810c96695c8 100644 --- a/drivers/media/platform/mediatek/mdp/mtk_mdp_ipi.h +++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_ipi.h @@ -40,12 +40,14 @@ struct mdp_ipi_init { * @ipi_id : IPI_MDP * @ap_inst : AP mtk_mdp_vpu address * @vpu_inst_addr : VPU MDP instance address + * @padding : Alignment padding */ struct mdp_ipi_comm { uint32_t msg_id; uint32_t ipi_id; uint64_t ap_inst; uint32_t vpu_inst_addr; + uint32_t padding; }; /** diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.c index 52e5d36aa912..7d194a476713 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.c @@ -35,6 +35,44 @@ mtk_vdec_find_format(struct v4l2_format *f, return NULL; } +static bool mtk_vdec_get_cap_fmt(struct mtk_vcodec_ctx *ctx, int format_index) +{ + const struct mtk_vcodec_dec_pdata *dec_pdata = ctx->dev->vdec_pdata; + const struct mtk_video_fmt *fmt; + struct mtk_q_data *q_data; + int num_frame_count = 0, i; + bool ret = true; + + for (i = 0; i < *dec_pdata->num_formats; i++) { + if (dec_pdata->vdec_formats[i].type != MTK_FMT_FRAME) + continue; + + num_frame_count++; + } + + if (num_frame_count == 1) + return true; + + fmt = &dec_pdata->vdec_formats[format_index]; + q_data = &ctx->q_data[MTK_Q_DATA_SRC]; + switch (q_data->fmt->fourcc) { + case V4L2_PIX_FMT_VP8_FRAME: + if (fmt->fourcc == V4L2_PIX_FMT_MM21) + ret = true; + break; + case V4L2_PIX_FMT_H264_SLICE: + case V4L2_PIX_FMT_VP9_FRAME: + if (fmt->fourcc == V4L2_PIX_FMT_MM21) + ret = false; + break; + default: + ret = true; + break; + } + + return ret; +} + static struct mtk_q_data *mtk_vdec_get_q_data(struct mtk_vcodec_ctx *ctx, enum v4l2_buf_type type) { @@ -112,8 +150,6 @@ void mtk_vcodec_dec_set_default_params(struct mtk_vcodec_ctx *ctx) { struct mtk_q_data *q_data; - ctx->dev->vdec_pdata->init_vdec_params(ctx); - ctx->m2m_ctx->q_lock = &ctx->dev->dev_mutex; ctx->fh.m2m_ctx = ctx->m2m_ctx; ctx->fh.ctrl_handler = &ctx->ctrl_hdl; @@ -141,15 +177,6 @@ void mtk_vcodec_dec_set_default_params(struct mtk_vcodec_ctx *ctx) q_data->coded_height = DFT_CFG_HEIGHT; q_data->fmt = ctx->dev->vdec_pdata->default_cap_fmt; q_data->field = V4L2_FIELD_NONE; - ctx->max_width = MTK_VDEC_MAX_W; - ctx->max_height = MTK_VDEC_MAX_H; - - v4l_bound_align_image(&q_data->coded_width, - MTK_VDEC_MIN_W, - ctx->max_width, 4, - &q_data->coded_height, - MTK_VDEC_MIN_H, - ctx->max_height, 5, 6); q_data->sizeimage[0] = q_data->coded_width * q_data->coded_height; q_data->bytesperline[0] = q_data->coded_width; @@ -185,12 +212,34 @@ static int vidioc_vdec_dqbuf(struct file *file, void *priv, return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); } +static int mtk_vcodec_dec_get_chip_name(void *priv) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct device *dev = &ctx->dev->plat_dev->dev; + + if (of_device_is_compatible(dev->of_node, "mediatek,mt8173-vcodec-dec")) + return 8173; + else if (of_device_is_compatible(dev->of_node, "mediatek,mt8183-vcodec-dec")) + return 8183; + else if (of_device_is_compatible(dev->of_node, "mediatek,mt8192-vcodec-dec")) + return 8192; + else if (of_device_is_compatible(dev->of_node, "mediatek,mt8195-vcodec-dec")) + return 8195; + else if (of_device_is_compatible(dev->of_node, "mediatek,mt8186-vcodec-dec")) + return 8186; + else + return 8173; +} + static int vidioc_vdec_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - strscpy(cap->driver, MTK_VCODEC_DEC_NAME, sizeof(cap->driver)); - strscpy(cap->bus_info, MTK_PLATFORM_STR, sizeof(cap->bus_info)); - strscpy(cap->card, MTK_PLATFORM_STR, sizeof(cap->card)); + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct device *dev = &ctx->dev->plat_dev->dev; + int platform_name = mtk_vcodec_dec_get_chip_name(priv); + + strscpy(cap->driver, dev->driver->name, sizeof(cap->driver)); + snprintf(cap->card, sizeof(cap->card), "MT%d video decoder", platform_name); return 0; } @@ -198,6 +247,11 @@ static int vidioc_vdec_querycap(struct file *file, void *priv, static int vidioc_vdec_subscribe_evt(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub) { + struct mtk_vcodec_ctx *ctx = fh_to_ctx(fh); + + if (ctx->dev->vdec_pdata->uses_stateless_api) + return v4l2_ctrl_subscribe_event(fh, sub); + switch (sub->type) { case V4L2_EVENT_EOS: return v4l2_event_subscribe(fh, sub, 2, NULL); @@ -212,13 +266,18 @@ static int vidioc_try_fmt(struct mtk_vcodec_ctx *ctx, struct v4l2_format *f, const struct mtk_video_fmt *fmt) { struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; + const struct v4l2_frmsize_stepwise *frmsize; pix_fmt_mp->field = V4L2_FIELD_NONE; - pix_fmt_mp->width = - clamp(pix_fmt_mp->width, MTK_VDEC_MIN_W, ctx->max_width); - pix_fmt_mp->height = - clamp(pix_fmt_mp->height, MTK_VDEC_MIN_H, ctx->max_height); + /* Always apply frame size constraints from the coded side */ + if (V4L2_TYPE_IS_OUTPUT(f->type)) + frmsize = &fmt->frmsize; + else + frmsize = &ctx->q_data[MTK_Q_DATA_SRC].fmt->frmsize; + + pix_fmt_mp->width = clamp(pix_fmt_mp->width, MTK_VDEC_MIN_W, frmsize->max_width); + pix_fmt_mp->height = clamp(pix_fmt_mp->height, MTK_VDEC_MIN_H, frmsize->max_height); if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { pix_fmt_mp->num_planes = 1; @@ -234,18 +293,15 @@ static int vidioc_try_fmt(struct mtk_vcodec_ctx *ctx, struct v4l2_format *f, */ tmp_w = pix_fmt_mp->width; tmp_h = pix_fmt_mp->height; - v4l_bound_align_image(&pix_fmt_mp->width, - MTK_VDEC_MIN_W, - ctx->max_width, 6, - &pix_fmt_mp->height, - MTK_VDEC_MIN_H, - ctx->max_height, 6, 9); + v4l_bound_align_image(&pix_fmt_mp->width, MTK_VDEC_MIN_W, frmsize->max_width, 6, + &pix_fmt_mp->height, MTK_VDEC_MIN_H, frmsize->max_height, 6, + 9); if (pix_fmt_mp->width < tmp_w && - (pix_fmt_mp->width + 64) <= ctx->max_width) + (pix_fmt_mp->width + 64) <= frmsize->max_width) pix_fmt_mp->width += 64; if (pix_fmt_mp->height < tmp_h && - (pix_fmt_mp->height + 64) <= ctx->max_height) + (pix_fmt_mp->height + 64) <= frmsize->max_height) pix_fmt_mp->height += 64; mtk_v4l2_debug(0, @@ -435,13 +491,6 @@ static int vidioc_vdec_s_fmt(struct file *file, void *priv, if (fmt == NULL) return -EINVAL; - if (!(ctx->dev->dec_capability & VCODEC_CAPABILITY_4K_DISABLED) && - fmt->fourcc != V4L2_PIX_FMT_VP8_FRAME) { - mtk_v4l2_debug(3, "4K is enabled"); - ctx->max_width = VCODEC_DEC_4K_CODED_WIDTH; - ctx->max_height = VCODEC_DEC_4K_CODED_HEIGHT; - } - q_data->fmt = fmt; vidioc_try_fmt(ctx, f, q_data->fmt); if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { @@ -526,15 +575,17 @@ static int vidioc_enum_framesizes(struct file *file, void *priv, if (fsize->index != 0) return -EINVAL; - for (i = 0; i < *dec_pdata->num_framesizes; ++i) { - if (fsize->pixel_format != dec_pdata->vdec_framesizes[i].fourcc) + for (i = 0; i < *dec_pdata->num_formats; i++) { + if (fsize->pixel_format != dec_pdata->vdec_formats[i].fourcc) continue; + /* Only coded formats have frame sizes set */ + if (!dec_pdata->vdec_formats[i].frmsize.max_width) + return -ENOTTY; + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; - fsize->stepwise = dec_pdata->vdec_framesizes[i].stepwise; + fsize->stepwise = dec_pdata->vdec_formats[i].frmsize; - fsize->stepwise.max_width = ctx->max_width; - fsize->stepwise.max_height = ctx->max_height; mtk_v4l2_debug(1, "%x, %d %d %d %d %d %d", ctx->dev->dec_capability, fsize->stepwise.min_width, @@ -566,6 +617,9 @@ static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, void *priv, dec_pdata->vdec_formats[i].type != MTK_FMT_FRAME) continue; + if (!output_queue && !mtk_vdec_get_cap_fmt(ctx, i)) + continue; + if (j == f->index) break; ++j; @@ -735,6 +789,7 @@ int vb2ops_vdec_buf_prepare(struct vb2_buffer *vb) mtk_v4l2_err("data will not fit into plane %d (%lu < %d)", i, vb2_plane_size(vb, i), q_data->sizeimage[i]); + return -EINVAL; } if (!V4L2_TYPE_IS_OUTPUT(vb->type)) vb2_set_plane_payload(vb, i, q_data->sizeimage[i]); @@ -938,6 +993,7 @@ int mtk_vcodec_dec_queue_init(void *priv, struct vb2_queue *src_vq, src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; src_vq->lock = &ctx->dev->dev_mutex; src_vq->dev = &ctx->dev->plat_dev->dev; + src_vq->allow_cache_hints = 1; ret = vb2_queue_init(src_vq); if (ret) { @@ -953,6 +1009,7 @@ int mtk_vcodec_dec_queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; dst_vq->lock = &ctx->dev->dev_mutex; dst_vq->dev = &ctx->dev->plat_dev->dev; + dst_vq->allow_cache_hints = 1; ret = vb2_queue_init(dst_vq); if (ret) diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_drv.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_drv.c index 995e6e2fb1ab..e0b6ae9d6caa 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_drv.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_drv.c @@ -208,9 +208,12 @@ static int fops_vcodec_open(struct file *file) dev->dec_capability = mtk_vcodec_fw_get_vdec_capa(dev->fw_handler); + mtk_v4l2_debug(0, "decoder capability %x", dev->dec_capability); } + ctx->dev->vdec_pdata->init_vdec_params(ctx); + list_add(&ctx->list, &dev->ctx_list); mutex_unlock(&dev->dev_mutex); @@ -386,8 +389,14 @@ static int mtk_vcodec_probe(struct platform_device *pdev) mtk_v4l2_err("Main device of_platform_populate failed."); goto err_reg_cont; } + } else { + set_bit(MTK_VDEC_CORE, dev->subdev_bitmap); } + atomic_set(&dev->dec_active_cnt, 0); + memset(dev->vdec_racing_info, 0, sizeof(dev->vdec_racing_info)); + mutex_init(&dev->dec_racing_info_mutex); + ret = video_register_device(vfd_dec, VFL_TYPE_VIDEO, -1); if (ret) { mtk_v4l2_err("Failed to register video device"); @@ -465,6 +474,10 @@ static const struct of_device_id mtk_vcodec_match[] = { .compatible = "mediatek,mt8186-vcodec-dec", .data = &mtk_vdec_single_core_pdata, }, + { + .compatible = "mediatek,mt8195-vcodec-dec", + .data = &mtk_lat_sig_core_pdata, + }, {}, }; diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.c index 14bed2bd4283..376db0e433d7 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.c @@ -28,6 +28,10 @@ static const struct of_device_id mtk_vdec_hw_match[] = { .compatible = "mediatek,mtk-vcodec-core", .data = (void *)MTK_VDEC_CORE, }, + { + .compatible = "mediatek,mtk-vcodec-lat-soc", + .data = (void *)MTK_VDEC_LAT_SOC, + }, {}, }; MODULE_DEVICE_TABLE(of, mtk_vdec_hw_match); @@ -166,9 +170,11 @@ static int mtk_vdec_hw_probe(struct platform_device *pdev) subdev_dev->reg_base[VDEC_HW_SYS] = main_dev->reg_base[VDEC_HW_SYS]; set_bit(subdev_dev->hw_idx, main_dev->subdev_bitmap); - ret = mtk_vdec_hw_init_irq(subdev_dev); - if (ret) - goto err; + if (IS_SUPPORT_VDEC_HW_IRQ(hw_idx)) { + ret = mtk_vdec_hw_init_irq(subdev_dev); + if (ret) + goto err; + } subdev_dev->reg_base[VDEC_HW_MISC] = devm_platform_ioremap_resource(pdev, 0); diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.h b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.h index a63e4b1b81c3..36faa8d9d681 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.h +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.h @@ -17,6 +17,8 @@ #define VDEC_IRQ_CLR 0x10 #define VDEC_IRQ_CFG_REG 0xa4 +#define IS_SUPPORT_VDEC_HW_IRQ(hw_idx) ((hw_idx) != MTK_VDEC_LAT_SOC) + /** * enum mtk_vdec_hw_reg_idx - subdev hardware register base index * @VDEC_HW_SYS : vdec soc register index diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_pm.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_pm.c index 0fb7e5ba635b..4305e4eb9900 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_pm.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_pm.c @@ -144,6 +144,34 @@ static void mtk_vcodec_dec_disable_irq(struct mtk_vcodec_dev *vdec_dev, int hw_i } } +static void mtk_vcodec_load_racing_info(struct mtk_vcodec_ctx *ctx) +{ + void __iomem *vdec_racing_addr; + int j; + + mutex_lock(&ctx->dev->dec_racing_info_mutex); + if (atomic_inc_return(&ctx->dev->dec_active_cnt) == 1) { + vdec_racing_addr = ctx->dev->reg_base[VDEC_MISC] + 0x100; + for (j = 0; j < 132; j++) + writel(ctx->dev->vdec_racing_info[j], vdec_racing_addr + j * 4); + } + mutex_unlock(&ctx->dev->dec_racing_info_mutex); +} + +static void mtk_vcodec_record_racing_info(struct mtk_vcodec_ctx *ctx) +{ + void __iomem *vdec_racing_addr; + int j; + + mutex_lock(&ctx->dev->dec_racing_info_mutex); + if (atomic_dec_and_test(&ctx->dev->dec_active_cnt)) { + vdec_racing_addr = ctx->dev->reg_base[VDEC_MISC] + 0x100; + for (j = 0; j < 132; j++) + ctx->dev->vdec_racing_info[j] = readl(vdec_racing_addr + j * 4); + } + mutex_unlock(&ctx->dev->dec_racing_info_mutex); +} + static struct mtk_vcodec_pm *mtk_vcodec_dec_get_pm(struct mtk_vcodec_dev *vdec_dev, int hw_idx) { @@ -174,6 +202,14 @@ static void mtk_vcodec_dec_child_dev_on(struct mtk_vcodec_dev *vdec_dev, mtk_vcodec_dec_pw_on(pm); mtk_vcodec_dec_clock_on(pm); } + + if (hw_idx == MTK_VDEC_LAT0) { + pm = mtk_vcodec_dec_get_pm(vdec_dev, MTK_VDEC_LAT_SOC); + if (pm) { + mtk_vcodec_dec_pw_on(pm); + mtk_vcodec_dec_clock_on(pm); + } + } } static void mtk_vcodec_dec_child_dev_off(struct mtk_vcodec_dev *vdec_dev, @@ -186,6 +222,14 @@ static void mtk_vcodec_dec_child_dev_off(struct mtk_vcodec_dev *vdec_dev, mtk_vcodec_dec_clock_off(pm); mtk_vcodec_dec_pw_off(pm); } + + if (hw_idx == MTK_VDEC_LAT0) { + pm = mtk_vcodec_dec_get_pm(vdec_dev, MTK_VDEC_LAT_SOC); + if (pm) { + mtk_vcodec_dec_clock_off(pm); + mtk_vcodec_dec_pw_off(pm); + } + } } void mtk_vcodec_dec_enable_hardware(struct mtk_vcodec_ctx *ctx, int hw_idx) @@ -198,11 +242,17 @@ void mtk_vcodec_dec_enable_hardware(struct mtk_vcodec_ctx *ctx, int hw_idx) mtk_vcodec_dec_child_dev_on(ctx->dev, hw_idx); mtk_vcodec_dec_enable_irq(ctx->dev, hw_idx); + + if (IS_VDEC_INNER_RACING(ctx->dev->dec_capability)) + mtk_vcodec_load_racing_info(ctx); } EXPORT_SYMBOL_GPL(mtk_vcodec_dec_enable_hardware); void mtk_vcodec_dec_disable_hardware(struct mtk_vcodec_ctx *ctx, int hw_idx) { + if (IS_VDEC_INNER_RACING(ctx->dev->dec_capability)) + mtk_vcodec_record_racing_info(ctx); + mtk_vcodec_dec_disable_irq(ctx->dev, hw_idx); mtk_vcodec_dec_child_dev_off(ctx->dev, hw_idx); diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateful.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateful.c index 9c7e6145cebb..035c86e7809f 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateful.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateful.c @@ -17,18 +17,24 @@ static const struct mtk_video_fmt mtk_video_formats[] = { .type = MTK_FMT_DEC, .num_planes = 1, .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, + .frmsize = { MTK_VDEC_MIN_W, MTK_VDEC_MAX_W, 16, + MTK_VDEC_MIN_H, MTK_VDEC_MAX_H, 16 }, }, { .fourcc = V4L2_PIX_FMT_VP8, .type = MTK_FMT_DEC, .num_planes = 1, .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, + .frmsize = { MTK_VDEC_MIN_W, MTK_VDEC_MAX_W, 16, + MTK_VDEC_MIN_H, MTK_VDEC_MAX_H, 16 }, }, { .fourcc = V4L2_PIX_FMT_VP9, .type = MTK_FMT_DEC, .num_planes = 1, .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, + .frmsize = { MTK_VDEC_MIN_W, MTK_VDEC_MAX_W, 16, + MTK_VDEC_MIN_H, MTK_VDEC_MAX_H, 16 }, }, { .fourcc = V4L2_PIX_FMT_MT21C, @@ -43,27 +49,6 @@ static const unsigned int num_supported_formats = #define DEFAULT_OUT_FMT_IDX 0 #define DEFAULT_CAP_FMT_IDX 3 -static const struct mtk_codec_framesizes mtk_vdec_framesizes[] = { - { - .fourcc = V4L2_PIX_FMT_H264, - .stepwise = { MTK_VDEC_MIN_W, MTK_VDEC_MAX_W, 16, - MTK_VDEC_MIN_H, MTK_VDEC_MAX_H, 16 }, - }, - { - .fourcc = V4L2_PIX_FMT_VP8, - .stepwise = { MTK_VDEC_MIN_W, MTK_VDEC_MAX_W, 16, - MTK_VDEC_MIN_H, MTK_VDEC_MAX_H, 16 }, - }, - { - .fourcc = V4L2_PIX_FMT_VP9, - .stepwise = { MTK_VDEC_MIN_W, MTK_VDEC_MAX_W, 16, - MTK_VDEC_MIN_H, MTK_VDEC_MAX_H, 16 }, - }, -}; - -static const unsigned int num_supported_framesize = - ARRAY_SIZE(mtk_vdec_framesizes); - /* * This function tries to clean all display buffers, the buffers will return * in display order. @@ -618,8 +603,6 @@ const struct mtk_vcodec_dec_pdata mtk_vdec_8173_pdata = { .num_formats = &num_supported_formats, .default_out_fmt = &mtk_video_formats[DEFAULT_OUT_FMT_IDX], .default_cap_fmt = &mtk_video_formats[DEFAULT_CAP_FMT_IDX], - .vdec_framesizes = mtk_vdec_framesizes, - .num_framesizes = &num_supported_framesize, .worker = mtk_vdec_worker, .flush_decoder = mtk_vdec_flush_decoder, .is_subdev_supported = false, diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateless.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateless.c index 16d55785d84b..c45bd2599bb2 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateless.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateless.c @@ -112,14 +112,12 @@ static const struct mtk_stateless_control mtk_stateless_controls[] = { #define NUM_CTRLS ARRAY_SIZE(mtk_stateless_controls) static struct mtk_video_fmt mtk_video_formats[5]; -static struct mtk_codec_framesizes mtk_vdec_framesizes[3]; static struct mtk_video_fmt default_out_format; static struct mtk_video_fmt default_cap_format; static unsigned int num_formats; -static unsigned int num_framesizes; -static struct v4l2_frmsize_stepwise stepwise_fhd = { +static const struct v4l2_frmsize_stepwise stepwise_fhd = { .min_width = MTK_VDEC_MIN_W, .max_width = MTK_VDEC_MAX_W, .step_width = 16, @@ -348,7 +346,6 @@ static void mtk_vcodec_add_formats(unsigned int fourcc, struct mtk_vcodec_dev *dev = ctx->dev; const struct mtk_vcodec_dec_pdata *pdata = dev->vdec_pdata; int count_formats = *pdata->num_formats; - int count_framesizes = *pdata->num_framesizes; switch (fourcc) { case V4L2_PIX_FMT_H264_SLICE: @@ -357,10 +354,15 @@ static void mtk_vcodec_add_formats(unsigned int fourcc, mtk_video_formats[count_formats].fourcc = fourcc; mtk_video_formats[count_formats].type = MTK_FMT_DEC; mtk_video_formats[count_formats].num_planes = 1; - - mtk_vdec_framesizes[count_framesizes].fourcc = fourcc; - mtk_vdec_framesizes[count_framesizes].stepwise = stepwise_fhd; - num_framesizes++; + mtk_video_formats[count_formats].frmsize = stepwise_fhd; + + if (!(ctx->dev->dec_capability & VCODEC_CAPABILITY_4K_DISABLED) && + fourcc != V4L2_PIX_FMT_VP8_FRAME) { + mtk_video_formats[count_formats].frmsize.max_width = + VCODEC_DEC_4K_CODED_WIDTH; + mtk_video_formats[count_formats].frmsize.max_height = + VCODEC_DEC_4K_CODED_HEIGHT; + } break; case V4L2_PIX_FMT_MM21: case V4L2_PIX_FMT_MT21C: @@ -374,15 +376,15 @@ static void mtk_vcodec_add_formats(unsigned int fourcc, } num_formats++; - mtk_v4l2_debug(3, "num_formats: %d num_frames:%d dec_capability: 0x%x", - count_formats, count_framesizes, ctx->dev->dec_capability); + mtk_v4l2_debug(3, "num_formats: %d dec_capability: 0x%x", + count_formats, ctx->dev->dec_capability); } static void mtk_vcodec_get_supported_formats(struct mtk_vcodec_ctx *ctx) { int cap_format_count = 0, out_format_count = 0; - if (num_formats && num_framesizes) + if (num_formats) return; if (ctx->dev->dec_capability & MTK_VDEC_FORMAT_MM21) { @@ -461,8 +463,6 @@ const struct mtk_vcodec_dec_pdata mtk_vdec_8183_pdata = { .num_formats = &num_formats, .default_out_fmt = &default_out_format, .default_cap_fmt = &default_cap_format, - .vdec_framesizes = mtk_vdec_framesizes, - .num_framesizes = &num_framesizes, .uses_stateless_api = true, .worker = mtk_vdec_worker, .flush_decoder = mtk_vdec_flush_decoder, @@ -481,8 +481,6 @@ const struct mtk_vcodec_dec_pdata mtk_lat_sig_core_pdata = { .num_formats = &num_formats, .default_out_fmt = &default_out_format, .default_cap_fmt = &default_cap_format, - .vdec_framesizes = mtk_vdec_framesizes, - .num_framesizes = &num_framesizes, .uses_stateless_api = true, .worker = mtk_vdec_worker, .flush_decoder = mtk_vdec_flush_decoder, @@ -500,8 +498,6 @@ const struct mtk_vcodec_dec_pdata mtk_vdec_single_core_pdata = { .num_formats = &num_formats, .default_out_fmt = &default_out_format, .default_cap_fmt = &default_cap_format, - .vdec_framesizes = mtk_vdec_framesizes, - .num_framesizes = &num_framesizes, .uses_stateless_api = true, .worker = mtk_vdec_worker, .flush_decoder = mtk_vdec_flush_decoder, diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_drv.h b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_drv.h index a29041a0b7e0..ef4584a46417 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_drv.h +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_drv.h @@ -19,15 +19,14 @@ #include "mtk_vcodec_util.h" #include "vdec_msg_queue.h" -#define MTK_VCODEC_DRV_NAME "mtk_vcodec_drv" #define MTK_VCODEC_DEC_NAME "mtk-vcodec-dec" #define MTK_VCODEC_ENC_NAME "mtk-vcodec-enc" -#define MTK_PLATFORM_STR "platform:mt8173" #define MTK_VCODEC_MAX_PLANES 3 #define MTK_V4L2_BENCHMARK 0 #define WAIT_INTR_TIMEOUT_MS 1000 #define IS_VDEC_LAT_ARCH(hw_arch) ((hw_arch) >= MTK_VDEC_LAT_SINGLE_CORE) +#define IS_VDEC_INNER_RACING(capability) ((capability) & MTK_VCODEC_INNER_RACING) /* * enum mtk_hw_reg_idx - MTK hw register base index @@ -104,6 +103,7 @@ enum mtk_vdec_hw_id { MTK_VDEC_CORE, MTK_VDEC_LAT0, MTK_VDEC_LAT1, + MTK_VDEC_LAT_SOC, MTK_VDEC_HW_MAX, }; @@ -125,15 +125,7 @@ struct mtk_video_fmt { enum mtk_fmt_type type; u32 num_planes; u32 flags; -}; - -/* - * struct mtk_codec_framesizes - Structure used to store information about - * framesizes - */ -struct mtk_codec_framesizes { - u32 fourcc; - struct v4l2_frmsize_stepwise stepwise; + struct v4l2_frmsize_stepwise frmsize; }; /* @@ -255,7 +247,7 @@ struct vdec_pic_info { * @param_change: indicate encode parameter type * @enc_params: encoding parameters * @dec_if: hooked decoder driver interface - * @enc_if: hoooked encoder driver interface + * @enc_if: hooked encoder driver interface * @drv_handle: driver handle for specific decode/encode instance * * @picinfo: store picture info after header parsing @@ -285,8 +277,6 @@ struct vdec_pic_info { * mtk_video_dec_buf. * @hw_id: hardware index used to identify different hardware. * - * @max_width: hardware supported max width - * @max_height: hardware supported max height * @msg_queue: msg queue used to store lat buffer information. */ struct mtk_vcodec_ctx { @@ -333,8 +323,6 @@ struct mtk_vcodec_ctx { struct mutex lock; int hw_id; - unsigned int max_width; - unsigned int max_height; struct vdec_msg_queue msg_queue; }; @@ -356,6 +344,7 @@ enum mtk_vdec_format_types { MTK_VDEC_FORMAT_H264_SLICE = 0x100, MTK_VDEC_FORMAT_VP8_FRAME = 0x200, MTK_VDEC_FORMAT_VP9_FRAME = 0x400, + MTK_VCODEC_INNER_RACING = 0x20000, }; /** @@ -373,9 +362,6 @@ enum mtk_vdec_format_types { * @default_out_fmt: default output buffer format * @default_cap_fmt: default capture buffer format * - * @vdec_framesizes: supported video decoder frame sizes - * @num_framesizes: count of video decoder frame sizes - * * @hw_arch: hardware arch is used to separate pure_sin_core and lat_sin_core * * @is_subdev_supported: whether support parent-node architecture(subdev) @@ -398,9 +384,6 @@ struct mtk_vcodec_dec_pdata { const struct mtk_video_fmt *default_out_fmt; const struct mtk_video_fmt *default_cap_fmt; - const struct mtk_codec_framesizes *vdec_framesizes; - const int *num_framesizes; - enum mtk_vdec_hw_arch hw_arch; bool is_subdev_supported; @@ -477,6 +460,10 @@ struct mtk_vcodec_enc_pdata { * @subdev_dev: subdev hardware device * @subdev_prob_done: check whether all used hw device is prob done * @subdev_bitmap: used to record hardware is ready or not + * + * @dec_active_cnt: used to mark whether need to record register value + * @vdec_racing_info: record register value + * @dec_racing_info_mutex: mutex lock used for inner racing mode */ struct mtk_vcodec_dev { struct v4l2_device v4l2_dev; @@ -522,6 +509,11 @@ struct mtk_vcodec_dev { void *subdev_dev[MTK_VDEC_HW_MAX]; int (*subdev_prob_done)(struct mtk_vcodec_dev *vdec_dev); DECLARE_BITMAP(subdev_bitmap, MTK_VDEC_HW_MAX); + + atomic_t dec_active_cnt; + u32 vdec_racing_info[132]; + /* Protects access to vdec_racing_info data */ + struct mutex dec_racing_info_mutex; }; static inline struct mtk_vcodec_ctx *fh_to_ctx(struct v4l2_fh *fh) diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c index c21367038c34..25e816863597 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c @@ -50,6 +50,14 @@ static int vidioc_venc_s_ctrl(struct v4l2_ctrl *ctrl) int ret = 0; switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_BITRATE_MODE val= %d", + ctrl->val); + if (ctrl->val != V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) { + mtk_v4l2_err("Unsupported bitrate mode =%d", ctrl->val); + ret = -EINVAL; + } + break; case V4L2_CID_MPEG_VIDEO_BITRATE: mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_BITRATE val = %d", ctrl->val); @@ -204,12 +212,32 @@ static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, pdata->num_output_formats); } +static int mtk_vcodec_enc_get_chip_name(void *priv) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct device *dev = &ctx->dev->plat_dev->dev; + + if (of_device_is_compatible(dev->of_node, "mediatek,mt8173-vcodec-enc")) + return 8173; + else if (of_device_is_compatible(dev->of_node, "mediatek,mt8183-vcodec-enc")) + return 8183; + else if (of_device_is_compatible(dev->of_node, "mediatek,mt8192-vcodec-enc")) + return 8192; + else if (of_device_is_compatible(dev->of_node, "mediatek,mt8195-vcodec-enc")) + return 8195; + else + return 8173; +} + static int vidioc_venc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - strscpy(cap->driver, MTK_VCODEC_ENC_NAME, sizeof(cap->driver)); - strscpy(cap->bus_info, MTK_PLATFORM_STR, sizeof(cap->bus_info)); - strscpy(cap->card, MTK_PLATFORM_STR, sizeof(cap->card)); + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct device *dev = &ctx->dev->plat_dev->dev; + int platform_name = mtk_vcodec_enc_get_chip_name(priv); + + strscpy(cap->driver, dev->driver->name, sizeof(cap->driver)); + snprintf(cap->card, sizeof(cap->card), "MT%d video encoder", platform_name); return 0; } @@ -1373,6 +1401,9 @@ int mtk_vcodec_enc_ctrls_setup(struct mtk_vcodec_ctx *ctx) 0, V4L2_MPEG_VIDEO_H264_LEVEL_4_0); v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_MPEG_VIDEO_VP8_PROFILE, V4L2_MPEG_VIDEO_VP8_PROFILE_0, 0, V4L2_MPEG_VIDEO_VP8_PROFILE_0); + v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_MPEG_VIDEO_BITRATE_MODE, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, + 0, V4L2_MPEG_VIDEO_BITRATE_MODE_CBR); if (handler->error) { diff --git a/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_common.c b/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_common.c index ca628321d272..580ce979e2a3 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_common.c +++ b/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_common.c @@ -51,7 +51,7 @@ void mtk_vdec_h264_fill_dpb_info(struct mtk_vcodec_ctx *ctx, struct vb2_queue *vq; struct vb2_buffer *vb; struct vb2_v4l2_buffer *vb2_v4l2; - int index, vb2_index; + int index; vq = v4l2_m2m_get_vq(ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); @@ -62,8 +62,8 @@ void mtk_vdec_h264_fill_dpb_info(struct mtk_vcodec_ctx *ctx, continue; } - vb2_index = vb2_find_timestamp(vq, dpb->reference_ts, 0); - if (vb2_index < 0) { + vb = vb2_find_buffer(vq, dpb->reference_ts); + if (!vb) { dev_err(&ctx->dev->plat_dev->dev, "Reference invalid: dpb_index(%d) reference_ts(%lld)", index, dpb->reference_ts); @@ -76,7 +76,6 @@ void mtk_vdec_h264_fill_dpb_info(struct mtk_vcodec_ctx *ctx, else h264_dpb_info[index].reference_flag = 2; - vb = vq->bufs[vb2_index]; vb2_v4l2 = container_of(vb, struct vb2_v4l2_buffer, vb2_buf); h264_dpb_info[index].field = vb2_v4l2->field; diff --git a/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_multi_if.c b/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_multi_if.c index 784d01f8bd50..4cc92700692b 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_multi_if.c +++ b/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_multi_if.c @@ -633,6 +633,17 @@ static int vdec_h264_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, goto err_scp_decode; } + share_info->trans_end = inst->ctx->msg_queue.wdma_addr.dma_addr + + inst->vsi->wdma_end_addr_offset; + share_info->trans_start = inst->ctx->msg_queue.wdma_wptr_addr; + share_info->nal_info = inst->vsi->dec.nal_info; + + if (IS_VDEC_INNER_RACING(inst->ctx->dev->dec_capability)) { + memcpy(&share_info->h264_slice_params, &inst->vsi->h264_slice_params, + sizeof(share_info->h264_slice_params)); + vdec_msg_queue_qbuf(&inst->ctx->dev->msg_queue_core_ctx, lat_buf); + } + /* wait decoder done interrupt */ timeout = mtk_vcodec_wait_for_done_ctx(inst->ctx, MTK_INST_IRQ_RECEIVED, WAIT_INTR_TIMEOUT_MS, MTK_VDEC_LAT0); @@ -646,18 +657,22 @@ static int vdec_h264_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, share_info->trans_end = inst->ctx->msg_queue.wdma_addr.dma_addr + inst->vsi->wdma_end_addr_offset; - share_info->trans_start = inst->ctx->msg_queue.wdma_wptr_addr; - share_info->nal_info = inst->vsi->dec.nal_info; vdec_msg_queue_update_ube_wptr(&lat_buf->ctx->msg_queue, share_info->trans_end); - memcpy(&share_info->h264_slice_params, &inst->vsi->h264_slice_params, - sizeof(share_info->h264_slice_params)); - vdec_msg_queue_qbuf(&inst->ctx->dev->msg_queue_core_ctx, lat_buf); + if (!IS_VDEC_INNER_RACING(inst->ctx->dev->dec_capability)) { + memcpy(&share_info->h264_slice_params, &inst->vsi->h264_slice_params, + sizeof(share_info->h264_slice_params)); + vdec_msg_queue_qbuf(&inst->ctx->dev->msg_queue_core_ctx, lat_buf); + } + mtk_vcodec_debug(inst, "dec num: %d lat crc: 0x%x 0x%x 0x%x", inst->slice_dec_num, + inst->vsi->dec.crc[0], inst->vsi->dec.crc[1], inst->vsi->dec.crc[2]); inst->slice_dec_num++; return 0; err_scp_decode: + if (!IS_VDEC_INNER_RACING(inst->ctx->dev->dec_capability)) + vdec_msg_queue_qbuf(&inst->ctx->msg_queue.lat_ctx, lat_buf); err_free_fb_out: vdec_msg_queue_qbuf(&inst->ctx->msg_queue.lat_ctx, lat_buf); mtk_vcodec_err(inst, "slice dec number: %d err: %d", inst->slice_dec_num, err); diff --git a/drivers/media/platform/mediatek/vcodec/vdec/vdec_vp8_req_if.c b/drivers/media/platform/mediatek/vcodec/vdec/vdec_vp8_req_if.c index eef102f3f4f3..e1fe2603e92e 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec/vdec_vp8_req_if.c +++ b/drivers/media/platform/mediatek/vcodec/vdec/vdec_vp8_req_if.c @@ -237,7 +237,7 @@ static int vdec_vp8_slice_get_decode_parameters(struct vdec_vp8_slice_inst *inst struct vb2_queue *vq; struct vb2_buffer *vb; u64 referenct_ts; - int index, vb2_index; + int index; frame_header = vdec_vp8_slice_get_ctrl_ptr(inst->ctx, V4L2_CID_STATELESS_VP8_FRAME); if (IS_ERR(frame_header)) @@ -246,8 +246,8 @@ static int vdec_vp8_slice_get_decode_parameters(struct vdec_vp8_slice_inst *inst vq = v4l2_m2m_get_vq(ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); for (index = 0; index < 3; index++) { referenct_ts = vdec_vp8_slice_get_ref_by_ts(frame_header, index); - vb2_index = vb2_find_timestamp(vq, referenct_ts, 0); - if (vb2_index < 0) { + vb = vb2_find_buffer(vq, referenct_ts); + if (!vb) { if (!V4L2_VP8_FRAME_IS_KEY_FRAME(frame_header)) mtk_vcodec_err(inst, "reference invalid: index(%d) ts(%lld)", index, referenct_ts); @@ -256,7 +256,6 @@ static int vdec_vp8_slice_get_decode_parameters(struct vdec_vp8_slice_inst *inst } inst->vsi->vp8_dpb_info[index].reference_flag = 1; - vb = vq->bufs[vb2_index]; inst->vsi->vp8_dpb_info[index].y_dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0); if (ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 2) diff --git a/drivers/media/platform/mediatek/vcodec/vdec/vdec_vp9_req_lat_if.c b/drivers/media/platform/mediatek/vcodec/vdec/vdec_vp9_req_lat_if.c index 023aba4ec2c4..fb1c36a3592d 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec/vdec_vp9_req_lat_if.c +++ b/drivers/media/platform/mediatek/vcodec/vdec/vdec_vp9_req_lat_if.c @@ -439,6 +439,8 @@ struct vdec_vp9_slice_ref { * @init_vsi: vsi used for initialized VP9 instance * @vsi: vsi used for decoding/flush ... * @core_vsi: vsi used for Core stage + * + * @sc_pfc: per frame context single core * @counts_map: used map to counts_helper * @counts_helper: counts table according to newest kernel spec */ @@ -487,6 +489,7 @@ struct vdec_vp9_slice_instance { }; struct vdec_vp9_slice_vsi *core_vsi; + struct vdec_vp9_slice_pfc sc_pfc; struct vdec_vp9_slice_counts_map counts_map; struct v4l2_vp9_frame_symbol_counts counts_helper; }; @@ -523,13 +526,12 @@ static int vdec_vp9_slice_init_default_frame_ctx(struct vdec_vp9_slice_instance if (vdec_vp9_slice_default_frame_ctx) goto out; - frame_ctx = kmalloc(sizeof(*frame_ctx), GFP_KERNEL); + frame_ctx = kmemdup(remote_frame_ctx, sizeof(*frame_ctx), GFP_KERNEL); if (!frame_ctx) { ret = -ENOMEM; goto out; } - memcpy(frame_ctx, remote_frame_ctx, sizeof(*frame_ctx)); vdec_vp9_slice_default_frame_ctx = frame_ctx; out: @@ -689,7 +691,26 @@ static int vdec_vp9_slice_tile_offset(int idx, int mi_num, int tile_log2) int sbs = (mi_num + 7) >> 3; int offset = ((idx * sbs) >> tile_log2) << 3; - return offset < mi_num ? offset : mi_num; + return min(offset, mi_num); +} + +static +int vdec_vp9_slice_setup_single_from_src_to_dst(struct vdec_vp9_slice_instance *instance) +{ + struct vb2_v4l2_buffer *src; + struct vb2_v4l2_buffer *dst; + + src = v4l2_m2m_next_src_buf(instance->ctx->m2m_ctx); + if (!src) + return -EINVAL; + + dst = v4l2_m2m_next_dst_buf(instance->ctx->m2m_ctx); + if (!dst) + return -EINVAL; + + v4l2_m2m_buf_copy_metadata(src, dst, true); + + return 0; } static int vdec_vp9_slice_setup_lat_from_src_buf(struct vdec_vp9_slice_instance *instance, @@ -1567,6 +1588,33 @@ static int vdec_vp9_slice_update_prob(struct vdec_vp9_slice_instance *instance, return 0; } +static int vdec_vp9_slice_update_single(struct vdec_vp9_slice_instance *instance, + struct vdec_vp9_slice_pfc *pfc) +{ + struct vdec_vp9_slice_vsi *vsi; + + vsi = &pfc->vsi; + memcpy(&pfc->state[0], &vsi->state, sizeof(vsi->state)); + + mtk_vcodec_debug(instance, "Frame %u Y_CRC %08x %08x %08x %08x\n", + pfc->seq, + vsi->state.crc[0], vsi->state.crc[1], + vsi->state.crc[2], vsi->state.crc[3]); + mtk_vcodec_debug(instance, "Frame %u C_CRC %08x %08x %08x %08x\n", + pfc->seq, + vsi->state.crc[4], vsi->state.crc[5], + vsi->state.crc[6], vsi->state.crc[7]); + + vdec_vp9_slice_update_prob(instance, vsi); + + instance->width = vsi->frame.uh.frame_width; + instance->height = vsi->frame.uh.frame_height; + instance->frame_type = vsi->frame.uh.frame_type; + instance->show_frame = vsi->frame.uh.show_frame; + + return 0; +} + static int vdec_vp9_slice_update_lat(struct vdec_vp9_slice_instance *instance, struct vdec_lat_buf *lat_buf, struct vdec_vp9_slice_pfc *pfc) @@ -1624,7 +1672,6 @@ static int vdec_vp9_slice_setup_core_buffer(struct vdec_vp9_slice_instance *inst struct vdec_vp9_slice_reference *ref; int plane; int size; - int idx; int w; int h; int i; @@ -1667,15 +1714,16 @@ static int vdec_vp9_slice_setup_core_buffer(struct vdec_vp9_slice_instance *inst */ for (i = 0; i < 3; i++) { ref = &vsi->frame.ref[i]; - idx = vb2_find_timestamp(vq, pfc->ref_idx[i], 0); - if (idx < 0) { + vb = vb2_find_buffer(vq, pfc->ref_idx[i]); + if (!vb) { ref->frame_width = w; ref->frame_height = h; memset(&vsi->ref[i], 0, sizeof(vsi->ref[i])); } else { + int idx = vb->index; + ref->frame_width = instance->dpb[idx].width; ref->frame_height = instance->dpb[idx].height; - vb = vq->bufs[idx]; vsi->ref[i].y.dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0); if (plane == 1) @@ -1690,6 +1738,40 @@ static int vdec_vp9_slice_setup_core_buffer(struct vdec_vp9_slice_instance *inst return 0; } +static void vdec_vp9_slice_setup_single_buffer(struct vdec_vp9_slice_instance *instance, + struct vdec_vp9_slice_pfc *pfc, + struct vdec_vp9_slice_vsi *vsi, + struct mtk_vcodec_mem *bs, + struct vdec_fb *fb) +{ + int i; + + vsi->bs.buf.dma_addr = bs->dma_addr; + vsi->bs.buf.size = bs->size; + vsi->bs.frame.dma_addr = bs->dma_addr; + vsi->bs.frame.size = bs->size; + + for (i = 0; i < 2; i++) { + vsi->mv[i].dma_addr = instance->mv[i].dma_addr; + vsi->mv[i].size = instance->mv[i].size; + } + for (i = 0; i < 2; i++) { + vsi->seg[i].dma_addr = instance->seg[i].dma_addr; + vsi->seg[i].size = instance->seg[i].size; + } + vsi->tile.dma_addr = instance->tile.dma_addr; + vsi->tile.size = instance->tile.size; + vsi->prob.dma_addr = instance->prob.dma_addr; + vsi->prob.size = instance->prob.size; + vsi->counts.dma_addr = instance->counts.dma_addr; + vsi->counts.size = instance->counts.size; + + vsi->row_info.buf = 0; + vsi->row_info.size = 0; + + vdec_vp9_slice_setup_core_buffer(instance, pfc, vsi, fb, NULL); +} + static int vdec_vp9_slice_setup_core(struct vdec_vp9_slice_instance *instance, struct vdec_fb *fb, struct vdec_lat_buf *lat_buf, @@ -1716,6 +1798,43 @@ err: return ret; } +static int vdec_vp9_slice_setup_single(struct vdec_vp9_slice_instance *instance, + struct mtk_vcodec_mem *bs, + struct vdec_fb *fb, + struct vdec_vp9_slice_pfc *pfc) +{ + struct vdec_vp9_slice_vsi *vsi = &pfc->vsi; + int ret; + + ret = vdec_vp9_slice_setup_single_from_src_to_dst(instance); + if (ret) + goto err; + + ret = vdec_vp9_slice_setup_pfc(instance, pfc); + if (ret) + goto err; + + ret = vdec_vp9_slice_alloc_working_buffer(instance, vsi); + if (ret) + goto err; + + vdec_vp9_slice_setup_single_buffer(instance, pfc, vsi, bs, fb); + vdec_vp9_slice_setup_seg_buffer(instance, vsi, &instance->seg[0]); + + ret = vdec_vp9_slice_setup_prob_buffer(instance, vsi); + if (ret) + goto err; + + ret = vdec_vp9_slice_setup_tile_buffer(instance, vsi, bs); + if (ret) + goto err; + + return 0; + +err: + return ret; +} + static int vdec_vp9_slice_update_core(struct vdec_vp9_slice_instance *instance, struct vdec_lat_buf *lat_buf, struct vdec_vp9_slice_pfc *pfc) @@ -1813,8 +1932,8 @@ static int vdec_vp9_slice_flush(void *h_vdec, struct mtk_vcodec_mem *bs, struct vdec_vp9_slice_instance *instance = h_vdec; mtk_vcodec_debug(instance, "flush ...\n"); - - vdec_msg_queue_wait_lat_buf_full(&instance->ctx->msg_queue); + if (instance->ctx->dev->vdec_pdata->hw_arch != MTK_VDEC_PURE_SINGLE_CORE) + vdec_msg_queue_wait_lat_buf_full(&instance->ctx->msg_queue); return vpu_dec_reset(&instance->vpu); } @@ -1867,6 +1986,63 @@ static int vdec_vp9_slice_get_param(void *h_vdec, enum vdec_get_param_type type, return 0; } +static int vdec_vp9_slice_single_decode(void *h_vdec, struct mtk_vcodec_mem *bs, + struct vdec_fb *fb, bool *res_chg) +{ + struct vdec_vp9_slice_instance *instance = h_vdec; + struct vdec_vp9_slice_pfc *pfc = &instance->sc_pfc; + struct vdec_vp9_slice_vsi *vsi; + struct mtk_vcodec_ctx *ctx; + int ret; + + if (!instance || !instance->ctx) + return -EINVAL; + ctx = instance->ctx; + + /* bs NULL means flush decoder */ + if (!bs) + return vdec_vp9_slice_flush(h_vdec, bs, fb, res_chg); + + fb = ctx->dev->vdec_pdata->get_cap_buffer(ctx); + if (!fb) + return -EBUSY; + + vsi = &pfc->vsi; + + ret = vdec_vp9_slice_setup_single(instance, bs, fb, pfc); + if (ret) { + mtk_vcodec_err(instance, "Failed to setup VP9 single ret %d\n", ret); + return ret; + } + vdec_vp9_slice_vsi_to_remote(vsi, instance->vsi); + + ret = vpu_dec_start(&instance->vpu, NULL, 0); + if (ret) { + mtk_vcodec_err(instance, "Failed to dec VP9 ret %d\n", ret); + return ret; + } + + ret = mtk_vcodec_wait_for_done_ctx(ctx, MTK_INST_IRQ_RECEIVED, + WAIT_INTR_TIMEOUT_MS, MTK_VDEC_CORE); + /* update remote vsi if decode timeout */ + if (ret) { + mtk_vcodec_err(instance, "VP9 decode timeout %d\n", ret); + WRITE_ONCE(instance->vsi->state.timeout, 1); + } + + vpu_dec_end(&instance->vpu); + + vdec_vp9_slice_vsi_from_remote(vsi, instance->vsi, 0); + ret = vdec_vp9_slice_update_single(instance, pfc); + if (ret) { + mtk_vcodec_err(instance, "VP9 decode error: %d\n", ret); + return ret; + } + + instance->ctx->decoded_frame_cnt++; + return 0; +} + static int vdec_vp9_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, struct vdec_fb *fb, bool *res_chg) { @@ -1946,6 +2122,20 @@ static int vdec_vp9_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, return 0; } +static int vdec_vp9_slice_decode(void *h_vdec, struct mtk_vcodec_mem *bs, + struct vdec_fb *fb, bool *res_chg) +{ + struct vdec_vp9_slice_instance *instance = h_vdec; + int ret; + + if (instance->ctx->dev->vdec_pdata->hw_arch == MTK_VDEC_PURE_SINGLE_CORE) + ret = vdec_vp9_slice_single_decode(h_vdec, bs, fb, res_chg); + else + ret = vdec_vp9_slice_lat_decode(h_vdec, bs, fb, res_chg); + + return ret; +} + static int vdec_vp9_slice_core_decode(struct vdec_lat_buf *lat_buf) { struct vdec_vp9_slice_instance *instance; @@ -2024,7 +2214,7 @@ err: const struct vdec_common_if vdec_vp9_slice_lat_if = { .init = vdec_vp9_slice_init, - .decode = vdec_vp9_slice_lat_decode, + .decode = vdec_vp9_slice_decode, .get_param = vdec_vp9_slice_get_param, .deinit = vdec_vp9_slice_deinit, }; diff --git a/drivers/media/platform/mediatek/vcodec/vdec_drv_if.c b/drivers/media/platform/mediatek/vcodec/vdec_drv_if.c index 27b4b35039cf..f3807f03d880 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec_drv_if.c +++ b/drivers/media/platform/mediatek/vcodec/vdec_drv_if.c @@ -47,7 +47,7 @@ int vdec_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc) break; case V4L2_PIX_FMT_VP9_FRAME: ctx->dec_if = &vdec_vp9_slice_lat_if; - ctx->hw_id = MTK_VDEC_LAT0; + ctx->hw_id = IS_VDEC_LAT_ARCH(hw_arch) ? MTK_VDEC_LAT0 : MTK_VDEC_CORE; break; default: return -EINVAL; diff --git a/drivers/media/platform/mediatek/vcodec/vdec_vpu_if.c b/drivers/media/platform/mediatek/vcodec/vdec_vpu_if.c index 35f4d5583084..df309e8e9379 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec_vpu_if.c +++ b/drivers/media/platform/mediatek/vcodec/vdec_vpu_if.c @@ -91,6 +91,11 @@ static void vpu_dec_ipi_handler(void *data, unsigned int len, void *priv) struct vdec_vpu_inst *vpu = (struct vdec_vpu_inst *) (unsigned long)msg->ap_inst_addr; + if (!vpu) { + mtk_v4l2_err("ap_inst_addr is NULL, did the SCP hang or crash?"); + return; + } + mtk_vcodec_debug(vpu, "+ id=%X", msg->msg_id); vpu->failure = msg->status; diff --git a/drivers/media/platform/nvidia/tegra-vde/h264.c b/drivers/media/platform/nvidia/tegra-vde/h264.c index 88f81a134ba0..204e474d57f7 100644 --- a/drivers/media/platform/nvidia/tegra-vde/h264.c +++ b/drivers/media/platform/nvidia/tegra-vde/h264.c @@ -659,20 +659,19 @@ static struct vb2_buffer *get_ref_buf(struct tegra_ctx *ctx, { const struct v4l2_h264_dpb_entry *dpb = ctx->h264.decode_params->dpb; struct vb2_queue *cap_q = &ctx->fh.m2m_ctx->cap_q_ctx.q; - int buf_idx = -1; + struct vb2_buffer *vb = NULL; if (dpb[dpb_idx].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE) - buf_idx = vb2_find_timestamp(cap_q, - dpb[dpb_idx].reference_ts, 0); + vb = vb2_find_buffer(cap_q, dpb[dpb_idx].reference_ts); /* * If a DPB entry is unused or invalid, address of current destination * buffer is returned. */ - if (buf_idx < 0) + if (!vb) return &dst->vb2_buf; - return vb2_get_buffer(cap_q, buf_idx); + return vb; } static int tegra_vde_validate_vb_size(struct tegra_ctx *ctx, diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.c index 29c604b1b179..9418fcf740a8 100644 --- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.c +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.c @@ -79,6 +79,11 @@ void mxc_jpeg_enable_irq(void __iomem *reg, int slot) writel(0xFFFFFFFF, reg + MXC_SLOT_OFFSET(slot, SLOT_IRQ_EN)); } +void mxc_jpeg_disable_irq(void __iomem *reg, int slot) +{ + writel(0x0, reg + MXC_SLOT_OFFSET(slot, SLOT_IRQ_EN)); +} + void mxc_jpeg_sw_reset(void __iomem *reg) { /* @@ -100,9 +105,6 @@ void mxc_jpeg_enc_mode_conf(struct device *dev, void __iomem *reg) /* all markers and segments */ writel(0x3ff, reg + CAST_CFG_MODE); - - /* quality factor */ - writel(0x4b, reg + CAST_QUALITY); } void mxc_jpeg_enc_mode_go(struct device *dev, void __iomem *reg) @@ -114,6 +116,14 @@ void mxc_jpeg_enc_mode_go(struct device *dev, void __iomem *reg) writel(0x140, reg + CAST_MODE); } +void mxc_jpeg_enc_set_quality(struct device *dev, void __iomem *reg, u8 quality) +{ + dev_dbg(dev, "CAST Encoder Quality %d...\n", quality); + + /* quality factor */ + writel(quality, reg + CAST_QUALITY); +} + void mxc_jpeg_dec_mode_go(struct device *dev, void __iomem *reg) { dev_dbg(dev, "CAST Decoder GO...\n"); diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h index d838e875616c..ecf3b6562ba2 100644 --- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h @@ -53,10 +53,10 @@ #define CAST_REC_REGS_SEL CAST_STATUS4 #define CAST_LUMTH CAST_STATUS5 #define CAST_CHRTH CAST_STATUS6 -#define CAST_NOMFRSIZE_LO CAST_STATUS7 -#define CAST_NOMFRSIZE_HI CAST_STATUS8 -#define CAST_OFBSIZE_LO CAST_STATUS9 -#define CAST_OFBSIZE_HI CAST_STATUS10 +#define CAST_NOMFRSIZE_LO CAST_STATUS16 +#define CAST_NOMFRSIZE_HI CAST_STATUS17 +#define CAST_OFBSIZE_LO CAST_STATUS18 +#define CAST_OFBSIZE_HI CAST_STATUS19 #define MXC_MAX_SLOTS 1 /* TODO use all 4 slots*/ /* JPEG-Decoder Wrapper Slot Registers 0..3 */ @@ -119,12 +119,14 @@ int mxc_jpeg_enable(void __iomem *reg); void wait_frmdone(struct device *dev, void __iomem *reg); void mxc_jpeg_enc_mode_conf(struct device *dev, void __iomem *reg); void mxc_jpeg_enc_mode_go(struct device *dev, void __iomem *reg); +void mxc_jpeg_enc_set_quality(struct device *dev, void __iomem *reg, u8 quality); void mxc_jpeg_dec_mode_go(struct device *dev, void __iomem *reg); int mxc_jpeg_get_slot(void __iomem *reg); u32 mxc_jpeg_get_offset(void __iomem *reg, int slot); void mxc_jpeg_enable_slot(void __iomem *reg, int slot); void mxc_jpeg_set_l_endian(void __iomem *reg, int le); void mxc_jpeg_enable_irq(void __iomem *reg, int slot); +void mxc_jpeg_disable_irq(void __iomem *reg, int slot); int mxc_jpeg_set_input(void __iomem *reg, u32 in_buf, u32 bufsize); int mxc_jpeg_set_output(void __iomem *reg, u16 out_pitch, u32 out_buf, u16 w, u16 h); diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c index f36b512bae51..32fd04a3d8bb 100644 --- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c @@ -389,7 +389,6 @@ static int enum_fmt(const struct mxc_jpeg_fmt *mxc_formats, int n, if (i >= n) return -EINVAL; - strscpy(f->description, mxc_formats[i].name, sizeof(f->description)); f->pixelformat = mxc_formats[i].fourcc; return 0; @@ -520,6 +519,7 @@ static bool mxc_jpeg_alloc_slot_data(struct mxc_jpeg_dev *jpeg, GFP_ATOMIC); if (!cfg_stm) goto err; + memset(cfg_stm, 0, MXC_JPEG_MAX_CFG_STREAM); jpeg->slot_data[slot].cfg_stream_vaddr = cfg_stm; skip_alloc: @@ -558,6 +558,18 @@ static void mxc_jpeg_free_slot_data(struct mxc_jpeg_dev *jpeg, jpeg->slot_data[slot].used = false; } +static void mxc_jpeg_check_and_set_last_buffer(struct mxc_jpeg_ctx *ctx, + struct vb2_v4l2_buffer *src_buf, + struct vb2_v4l2_buffer *dst_buf) +{ + if (v4l2_m2m_is_last_draining_src_buf(ctx->fh.m2m_ctx, src_buf)) { + dst_buf->flags |= V4L2_BUF_FLAG_LAST; + v4l2_m2m_mark_stopped(ctx->fh.m2m_ctx); + notify_eos(ctx); + ctx->header_parsed = false; + } +} + static irqreturn_t mxc_jpeg_dec_irq(int irq, void *priv) { struct mxc_jpeg_dev *jpeg = priv; @@ -580,15 +592,8 @@ static irqreturn_t mxc_jpeg_dec_irq(int irq, void *priv) dev_dbg(dev, "Irq %d on slot %d.\n", irq, slot); ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev); - if (!ctx) { - dev_err(dev, - "Instance released before the end of transaction.\n"); - /* soft reset only resets internal state, not registers */ - mxc_jpeg_sw_reset(reg); - /* clear all interrupts */ - writel(0xFFFFFFFF, reg + MXC_SLOT_OFFSET(slot, SLOT_STATUS)); + if (WARN_ON(!ctx)) goto job_unlock; - } if (slot != ctx->slot) { /* TODO investigate when adding multi-instance support */ @@ -624,6 +629,7 @@ static irqreturn_t mxc_jpeg_dec_irq(int irq, void *priv) ctx->enc_state == MXC_JPEG_ENC_CONF) { ctx->enc_state = MXC_JPEG_ENCODING; dev_dbg(dev, "Encoder config finished. Start encoding...\n"); + mxc_jpeg_enc_set_quality(dev, reg, ctx->jpeg_quality); mxc_jpeg_enc_mode_go(dev, reg); goto job_unlock; } @@ -632,6 +638,7 @@ static irqreturn_t mxc_jpeg_dec_irq(int irq, void *priv) dev_dbg(dev, "Decoder DHT cfg finished. Start decoding...\n"); goto job_unlock; } + if (jpeg->mode == MXC_JPEG_ENCODE) { payload = readl(reg + MXC_SLOT_OFFSET(slot, SLOT_BUF_PTR)); vb2_set_plane_payload(&dst_buf->vb2_buf, 0, payload); @@ -659,7 +666,9 @@ static irqreturn_t mxc_jpeg_dec_irq(int irq, void *priv) buf_state = VB2_BUF_STATE_DONE; buffers_done: + mxc_jpeg_disable_irq(reg, ctx->slot); jpeg->slot_data[slot].used = false; /* unused, but don't free */ + mxc_jpeg_check_and_set_last_buffer(ctx, src_buf, dst_buf); v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); v4l2_m2m_buf_done(src_buf, buf_state); @@ -755,7 +764,13 @@ static unsigned int mxc_jpeg_setup_cfg_stream(void *cfg_stream_vaddr, u32 fourcc, u16 w, u16 h) { - unsigned int offset = 0; + /* + * There is a hardware issue that first 128 bytes of configuration data + * can't be loaded correctly. + * To avoid this issue, we need to write the configuration from + * an offset which should be no less than 0x80 (128 bytes). + */ + unsigned int offset = 0x80; u8 *cfg = (u8 *)cfg_stream_vaddr; struct mxc_jpeg_sof *sof; struct mxc_jpeg_sos *sos; @@ -887,8 +902,8 @@ static void mxc_jpeg_config_enc_desc(struct vb2_buffer *out_buf, jpeg->slot_data[slot].cfg_stream_size = mxc_jpeg_setup_cfg_stream(cfg_stream_vaddr, q_data->fmt->fourcc, - q_data->w_adjusted, - q_data->h_adjusted); + q_data->w, + q_data->h); /* chain the config descriptor with the encoding descriptor */ cfg_desc->next_descpt_ptr = desc_handle | MXC_NXT_DESCPT_EN; @@ -970,7 +985,7 @@ static bool mxc_jpeg_source_change(struct mxc_jpeg_ctx *ctx, &q_data_cap->h_adjusted, q_data_cap->h_adjusted, /* adjust up */ MXC_JPEG_MAX_HEIGHT, - q_data_cap->fmt->v_align, + 0, 0); /* setup bytesperline/sizeimage for capture queue */ @@ -1027,6 +1042,7 @@ static void mxc_jpeg_device_run(void *priv) jpeg_src_buf->jpeg_parse_error = true; } if (jpeg_src_buf->jpeg_parse_error) { + mxc_jpeg_check_and_set_last_buffer(ctx, src_buf, dst_buf); v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); @@ -1077,45 +1093,33 @@ end: spin_unlock_irqrestore(&ctx->mxc_jpeg->hw_lock, flags); } -static void mxc_jpeg_set_last_buffer_dequeued(struct mxc_jpeg_ctx *ctx) -{ - struct vb2_queue *q; - - ctx->stopped = 1; - q = v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx); - if (!list_empty(&q->done_list)) - return; - - q->last_buffer_dequeued = true; - wake_up(&q->done_wq); - ctx->stopped = 0; - ctx->header_parsed = false; -} - static int mxc_jpeg_decoder_cmd(struct file *file, void *priv, struct v4l2_decoder_cmd *cmd) { struct v4l2_fh *fh = file->private_data; struct mxc_jpeg_ctx *ctx = mxc_jpeg_fh_to_ctx(fh); - struct device *dev = ctx->mxc_jpeg->dev; int ret; ret = v4l2_m2m_ioctl_try_decoder_cmd(file, fh, cmd); if (ret < 0) return ret; - if (cmd->cmd == V4L2_DEC_CMD_STOP) { - dev_dbg(dev, "Received V4L2_DEC_CMD_STOP"); - if (v4l2_m2m_num_src_bufs_ready(fh->m2m_ctx) == 0) { - /* No more src bufs, notify app EOS */ - notify_eos(ctx); - mxc_jpeg_set_last_buffer_dequeued(ctx); - } else { - /* will send EOS later*/ - ctx->stopping = 1; - } + if (!vb2_is_streaming(v4l2_m2m_get_src_vq(fh->m2m_ctx))) + return 0; + + ret = v4l2_m2m_ioctl_decoder_cmd(file, priv, cmd); + if (ret < 0) + return ret; + + if (cmd->cmd == V4L2_DEC_CMD_STOP && + v4l2_m2m_has_stopped(fh->m2m_ctx)) { + notify_eos(ctx); + ctx->header_parsed = false; } + if (cmd->cmd == V4L2_DEC_CMD_START && + v4l2_m2m_has_stopped(fh->m2m_ctx)) + vb2_clear_last_buffer_dequeued(&fh->m2m_ctx->cap_q_ctx.q); return 0; } @@ -1124,24 +1128,27 @@ static int mxc_jpeg_encoder_cmd(struct file *file, void *priv, { struct v4l2_fh *fh = file->private_data; struct mxc_jpeg_ctx *ctx = mxc_jpeg_fh_to_ctx(fh); - struct device *dev = ctx->mxc_jpeg->dev; int ret; ret = v4l2_m2m_ioctl_try_encoder_cmd(file, fh, cmd); if (ret < 0) return ret; - if (cmd->cmd == V4L2_ENC_CMD_STOP) { - dev_dbg(dev, "Received V4L2_ENC_CMD_STOP"); - if (v4l2_m2m_num_src_bufs_ready(fh->m2m_ctx) == 0) { - /* No more src bufs, notify app EOS */ - notify_eos(ctx); - mxc_jpeg_set_last_buffer_dequeued(ctx); - } else { - /* will send EOS later*/ - ctx->stopping = 1; - } - } + if (!vb2_is_streaming(v4l2_m2m_get_src_vq(fh->m2m_ctx)) || + !vb2_is_streaming(v4l2_m2m_get_dst_vq(fh->m2m_ctx))) + return 0; + + ret = v4l2_m2m_ioctl_encoder_cmd(file, fh, cmd); + if (ret < 0) + return 0; + + if (cmd->cmd == V4L2_ENC_CMD_STOP && + v4l2_m2m_has_stopped(fh->m2m_ctx)) + notify_eos(ctx); + + if (cmd->cmd == V4L2_ENC_CMD_START && + v4l2_m2m_has_stopped(fh->m2m_ctx)) + vb2_clear_last_buffer_dequeued(&fh->m2m_ctx->cap_q_ctx.q); return 0; } @@ -1154,18 +1161,30 @@ static int mxc_jpeg_queue_setup(struct vb2_queue *q, { struct mxc_jpeg_ctx *ctx = vb2_get_drv_priv(q); struct mxc_jpeg_q_data *q_data = NULL; + struct mxc_jpeg_q_data tmp_q; int i; q_data = mxc_jpeg_get_q_data(ctx, q->type); if (!q_data) return -EINVAL; + tmp_q.fmt = q_data->fmt; + tmp_q.w = q_data->w_adjusted; + tmp_q.h = q_data->h_adjusted; + for (i = 0; i < MXC_JPEG_MAX_PLANES; i++) { + tmp_q.bytesperline[i] = q_data->bytesperline[i]; + tmp_q.sizeimage[i] = q_data->sizeimage[i]; + } + mxc_jpeg_sizeimage(&tmp_q); + for (i = 0; i < MXC_JPEG_MAX_PLANES; i++) + tmp_q.sizeimage[i] = max(tmp_q.sizeimage[i], q_data->sizeimage[i]); + /* Handle CREATE_BUFS situation - *nplanes != 0 */ if (*nplanes) { if (*nplanes != q_data->fmt->colplanes) return -EINVAL; for (i = 0; i < *nplanes; i++) { - if (sizes[i] < q_data->sizeimage[i]) + if (sizes[i] < tmp_q.sizeimage[i]) return -EINVAL; } return 0; @@ -1174,7 +1193,7 @@ static int mxc_jpeg_queue_setup(struct vb2_queue *q, /* Handle REQBUFS situation */ *nplanes = q_data->fmt->colplanes; for (i = 0; i < *nplanes; i++) - sizes[i] = q_data->sizeimage[i]; + sizes[i] = tmp_q.sizeimage[i]; return 0; } @@ -1185,6 +1204,8 @@ static int mxc_jpeg_start_streaming(struct vb2_queue *q, unsigned int count) struct mxc_jpeg_q_data *q_data = mxc_jpeg_get_q_data(ctx, q->type); int ret; + v4l2_m2m_update_start_streaming_state(ctx->fh.m2m_ctx, q); + if (ctx->mxc_jpeg->mode == MXC_JPEG_DECODE && V4L2_TYPE_IS_CAPTURE(q->type)) ctx->source_change = 0; dev_dbg(ctx->mxc_jpeg->dev, "Start streaming ctx=%p", ctx); @@ -1216,11 +1237,15 @@ static void mxc_jpeg_stop_streaming(struct vb2_queue *q) break; v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); } - pm_runtime_put_sync(&ctx->mxc_jpeg->pdev->dev); - if (V4L2_TYPE_IS_OUTPUT(q->type)) { - ctx->stopping = 0; - ctx->stopped = 0; + + v4l2_m2m_update_stop_streaming_state(ctx->fh.m2m_ctx, q); + if (V4L2_TYPE_IS_OUTPUT(q->type) && + v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) { + notify_eos(ctx); + ctx->header_parsed = false; } + + pm_runtime_put_sync(&ctx->mxc_jpeg->pdev->dev); } static int mxc_jpeg_valid_comp_id(struct device *dev, @@ -1374,11 +1399,6 @@ static int mxc_jpeg_parse(struct mxc_jpeg_ctx *ctx, struct vb2_buffer *vb) } q_data_out->w = header.frame.width; q_data_out->h = header.frame.height; - if (header.frame.width % 8 != 0 || header.frame.height % 8 != 0) { - dev_err(dev, "JPEG width or height not multiple of 8: %dx%d\n", - header.frame.width, header.frame.height); - return -EINVAL; - } if (header.frame.width > MXC_JPEG_MAX_WIDTH || header.frame.height > MXC_JPEG_MAX_HEIGHT) { dev_err(dev, "JPEG width or height should be <= 8192: %dx%d\n", @@ -1424,6 +1444,20 @@ static void mxc_jpeg_buf_queue(struct vb2_buffer *vb) struct mxc_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct mxc_jpeg_src_buf *jpeg_src_buf; + if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type) && + vb2_is_streaming(vb->vb2_queue) && + v4l2_m2m_dst_buf_is_last(ctx->fh.m2m_ctx)) { + struct mxc_jpeg_q_data *q_data; + + q_data = mxc_jpeg_get_q_data(ctx, vb->vb2_queue->type); + vbuf->field = V4L2_FIELD_NONE; + vbuf->sequence = q_data->sequence++; + v4l2_m2m_last_buffer_done(ctx->fh.m2m_ctx, vbuf); + notify_eos(ctx); + ctx->header_parsed = false; + return; + } + if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) goto end; @@ -1472,24 +1506,11 @@ static int mxc_jpeg_buf_prepare(struct vb2_buffer *vb) return -EINVAL; } } - return 0; -} - -static void mxc_jpeg_buf_finish(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct mxc_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct vb2_queue *q = vb->vb2_queue; - - if (V4L2_TYPE_IS_OUTPUT(vb->type)) - return; - if (!ctx->stopped) - return; - if (list_empty(&q->done_list)) { - vbuf->flags |= V4L2_BUF_FLAG_LAST; - ctx->stopped = 0; - ctx->header_parsed = false; + if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) { + vb2_set_plane_payload(vb, 0, 0); + vb2_set_plane_payload(vb, 1, 0); } + return 0; } static const struct vb2_ops mxc_jpeg_qops = { @@ -1498,7 +1519,6 @@ static const struct vb2_ops mxc_jpeg_qops = { .wait_finish = vb2_ops_wait_finish, .buf_out_validate = mxc_jpeg_buf_out_validate, .buf_prepare = mxc_jpeg_buf_prepare, - .buf_finish = mxc_jpeg_buf_finish, .start_streaming = mxc_jpeg_start_streaming, .stop_streaming = mxc_jpeg_stop_streaming, .buf_queue = mxc_jpeg_buf_queue, @@ -1563,6 +1583,56 @@ static void mxc_jpeg_set_default_params(struct mxc_jpeg_ctx *ctx) } } +static int mxc_jpeg_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mxc_jpeg_ctx *ctx = + container_of(ctrl->handler, struct mxc_jpeg_ctx, ctrl_handler); + + switch (ctrl->id) { + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + ctx->jpeg_quality = ctrl->val; + break; + default: + dev_err(ctx->mxc_jpeg->dev, "Invalid control, id = %d, val = %d\n", + ctrl->id, ctrl->val); + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_ctrl_ops mxc_jpeg_ctrl_ops = { + .s_ctrl = mxc_jpeg_s_ctrl, +}; + +static void mxc_jpeg_encode_ctrls(struct mxc_jpeg_ctx *ctx) +{ + v4l2_ctrl_new_std(&ctx->ctrl_handler, &mxc_jpeg_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, 1, 100, 1, 75); +} + +static int mxc_jpeg_ctrls_setup(struct mxc_jpeg_ctx *ctx) +{ + int err; + + v4l2_ctrl_handler_init(&ctx->ctrl_handler, 2); + + if (ctx->mxc_jpeg->mode == MXC_JPEG_ENCODE) + mxc_jpeg_encode_ctrls(ctx); + + if (ctx->ctrl_handler.error) { + err = ctx->ctrl_handler.error; + + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + return err; + } + + err = v4l2_ctrl_handler_setup(&ctx->ctrl_handler); + if (err) + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + return err; +} + static int mxc_jpeg_open(struct file *file) { struct mxc_jpeg_dev *mxc_jpeg = video_drvdata(file); @@ -1594,6 +1664,12 @@ static int mxc_jpeg_open(struct file *file) goto error; } + ret = mxc_jpeg_ctrls_setup(ctx); + if (ret) { + dev_err(ctx->mxc_jpeg->dev, "failed to setup mxc jpeg controls\n"); + goto err_ctrls_setup; + } + ctx->fh.ctrl_handler = &ctx->ctrl_handler; mxc_jpeg_set_default_params(ctx); ctx->slot = MXC_MAX_SLOTS; /* slot not allocated yet */ @@ -1605,6 +1681,8 @@ static int mxc_jpeg_open(struct file *file) return 0; +err_ctrls_setup: + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); error: v4l2_fh_del(&ctx->fh); v4l2_fh_exit(&ctx->fh); @@ -1646,7 +1724,6 @@ static int mxc_jpeg_enum_fmt_vid_cap(struct file *file, void *priv, if (f->index) return -EINVAL; f->pixelformat = q_data->fmt->fourcc; - strscpy(f->description, q_data->fmt->name, sizeof(f->description)); return 0; } } @@ -1684,22 +1761,17 @@ static int mxc_jpeg_try_fmt(struct v4l2_format *f, const struct mxc_jpeg_fmt *fm pix_mp->num_planes = fmt->colplanes; pix_mp->pixelformat = fmt->fourcc; - /* - * use MXC_JPEG_H_ALIGN instead of fmt->v_align, for vertical - * alignment, to loosen up the alignment to multiple of 8, - * otherwise NV12-1080p fails as 1080 is not a multiple of 16 - */ + pix_mp->width = w; + pix_mp->height = h; v4l_bound_align_image(&w, - MXC_JPEG_MIN_WIDTH, - w, /* adjust downwards*/ + w, /* adjust upwards*/ + MXC_JPEG_MAX_WIDTH, fmt->h_align, &h, - MXC_JPEG_MIN_HEIGHT, - h, /* adjust downwards*/ - MXC_JPEG_H_ALIGN, + h, /* adjust upwards*/ + MXC_JPEG_MAX_HEIGHT, + 0, 0); - pix_mp->width = w; /* negotiate the width */ - pix_mp->height = h; /* negotiate the height */ /* get user input into the tmp_q */ tmp_q.w = w; @@ -1825,35 +1897,19 @@ static int mxc_jpeg_s_fmt(struct mxc_jpeg_ctx *ctx, q_data->w_adjusted = q_data->w; q_data->h_adjusted = q_data->h; - if (jpeg->mode == MXC_JPEG_DECODE) { - /* - * align up the resolution for CAST IP, - * but leave the buffer resolution unchanged - */ - v4l_bound_align_image(&q_data->w_adjusted, - q_data->w_adjusted, /* adjust upwards */ - MXC_JPEG_MAX_WIDTH, - q_data->fmt->h_align, - &q_data->h_adjusted, - q_data->h_adjusted, /* adjust upwards */ - MXC_JPEG_MAX_HEIGHT, - q_data->fmt->v_align, - 0); - } else { - /* - * align down the resolution for CAST IP, - * but leave the buffer resolution unchanged - */ - v4l_bound_align_image(&q_data->w_adjusted, - MXC_JPEG_MIN_WIDTH, - q_data->w_adjusted, /* adjust downwards*/ - q_data->fmt->h_align, - &q_data->h_adjusted, - MXC_JPEG_MIN_HEIGHT, - q_data->h_adjusted, /* adjust downwards*/ - q_data->fmt->v_align, - 0); - } + /* + * align up the resolution for CAST IP, + * but leave the buffer resolution unchanged + */ + v4l_bound_align_image(&q_data->w_adjusted, + q_data->w_adjusted, /* adjust upwards */ + MXC_JPEG_MAX_WIDTH, + q_data->fmt->h_align, + &q_data->h_adjusted, + q_data->h_adjusted, /* adjust upwards */ + MXC_JPEG_MAX_HEIGHT, + q_data->fmt->v_align, + 0); for (i = 0; i < pix_mp->num_planes; i++) { q_data->bytesperline[i] = pix_mp->plane_fmt[i].bytesperline; @@ -1958,32 +2014,13 @@ static int mxc_jpeg_subscribe_event(struct v4l2_fh *fh, return v4l2_event_subscribe(fh, sub, 0, NULL); case V4L2_EVENT_SOURCE_CHANGE: return v4l2_src_change_event_subscribe(fh, sub); + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subscribe_event(fh, sub); default: return -EINVAL; } } -static int mxc_jpeg_dqbuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct v4l2_fh *fh = file->private_data; - struct mxc_jpeg_ctx *ctx = mxc_jpeg_fh_to_ctx(priv); - struct device *dev = ctx->mxc_jpeg->dev; - int num_src_ready = v4l2_m2m_num_src_bufs_ready(fh->m2m_ctx); - int ret; - - dev_dbg(dev, "DQBUF type=%d, index=%d", buf->type, buf->index); - if (ctx->stopping == 1 && num_src_ready == 0) { - /* No more src bufs, notify app EOS */ - notify_eos(ctx); - ctx->stopping = 0; - mxc_jpeg_set_last_buffer_dequeued(ctx); - } - - ret = v4l2_m2m_dqbuf(file, fh->m2m_ctx, buf); - return ret; -} - static const struct v4l2_ioctl_ops mxc_jpeg_ioctl_ops = { .vidioc_querycap = mxc_jpeg_querycap, .vidioc_enum_fmt_vid_cap = mxc_jpeg_enum_fmt_vid_cap, @@ -2007,7 +2044,7 @@ static const struct v4l2_ioctl_ops mxc_jpeg_ioctl_ops = { .vidioc_encoder_cmd = mxc_jpeg_encoder_cmd, .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, - .vidioc_dqbuf = mxc_jpeg_dqbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, @@ -2031,6 +2068,7 @@ static int mxc_jpeg_release(struct file *file) else dev_dbg(dev, "Release JPEG encoder instance on slot %d.", ctx->slot); + v4l2_ctrl_handler_free(&ctx->ctrl_handler); v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); v4l2_fh_del(&ctx->fh); v4l2_fh_exit(&ctx->fh); @@ -2167,12 +2205,14 @@ static int mxc_jpeg_probe(struct platform_device *pdev) jpeg->clk_ipg = devm_clk_get(dev, "ipg"); if (IS_ERR(jpeg->clk_ipg)) { dev_err(dev, "failed to get clock: ipg\n"); + ret = PTR_ERR(jpeg->clk_ipg); goto err_clk; } jpeg->clk_per = devm_clk_get(dev, "per"); if (IS_ERR(jpeg->clk_per)) { dev_err(dev, "failed to get clock: per\n"); + ret = PTR_ERR(jpeg->clk_per); goto err_clk; } diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h index 760eaf5387a1..c508d41a906f 100644 --- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h @@ -92,11 +92,11 @@ struct mxc_jpeg_ctx { struct mxc_jpeg_q_data cap_q; struct v4l2_fh fh; enum mxc_jpeg_enc_state enc_state; - unsigned int stopping; - unsigned int stopped; unsigned int slot; unsigned int source_change; bool header_parsed; + struct v4l2_ctrl_handler ctrl_handler; + u8 jpeg_quality; }; struct mxc_jpeg_slot_data { diff --git a/drivers/media/platform/nxp/imx-mipi-csis.c b/drivers/media/platform/nxp/imx-mipi-csis.c index 80b1c021d14a..905072871ed2 100644 --- a/drivers/media/platform/nxp/imx-mipi-csis.c +++ b/drivers/media/platform/nxp/imx-mipi-csis.c @@ -46,6 +46,11 @@ /* Register map definition */ +/* CSIS version */ +#define MIPI_CSIS_VERSION 0x00 +#define MIPI_CSIS_VERSION_IMX7D 0x03030505 +#define MIPI_CSIS_VERSION_IMX8MP 0x03060301 + /* CSIS common control */ #define MIPI_CSIS_CMN_CTRL 0x04 #define MIPI_CSIS_CMN_CTRL_UPDATE_SHADOW BIT(16) @@ -1155,6 +1160,32 @@ static int mipi_csis_set_fmt(struct v4l2_subdev *sd, return 0; } +static int mipi_csis_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) +{ + struct mipi_csis_device *csis = sd_to_mipi_csis_device(sd); + struct v4l2_mbus_frame_desc_entry *entry = &fd->entry[0]; + + if (pad != CSIS_PAD_SOURCE) + return -EINVAL; + + fd->type = V4L2_MBUS_FRAME_DESC_TYPE_PARALLEL; + fd->num_entries = 1; + + memset(entry, 0, sizeof(*entry)); + + mutex_lock(&csis->lock); + + entry->flags = 0; + entry->pixelcode = csis->csis_fmt->code; + entry->bus.csi2.vc = 0; + entry->bus.csi2.dt = csis->csis_fmt->data_type; + + mutex_unlock(&csis->lock); + + return 0; +} + static int mipi_csis_log_status(struct v4l2_subdev *sd) { struct mipi_csis_device *csis = sd_to_mipi_csis_device(sd); @@ -1179,6 +1210,7 @@ static const struct v4l2_subdev_pad_ops mipi_csis_pad_ops = { .enum_mbus_code = mipi_csis_enum_mbus_code, .get_fmt = mipi_csis_get_fmt, .set_fmt = mipi_csis_set_fmt, + .get_frame_desc = mipi_csis_get_frame_desc, }; static const struct v4l2_subdev_ops mipi_csis_subdev_ops = { @@ -1378,6 +1410,13 @@ static int mipi_csis_subdev_init(struct mipi_csis_device *csis) sd->dev = csis->dev; + sd->fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(csis->dev), + 1, 0, 0); + if (!sd->fwnode) { + dev_err(csis->dev, "Unable to retrieve endpoint for port@1\n"); + return -ENOENT; + } + csis->csis_fmt = &mipi_csis_formats[0]; mipi_csis_init_cfg(sd, NULL); @@ -1498,6 +1537,7 @@ cleanup: v4l2_async_unregister_subdev(&csis->sd); disable_clock: mipi_csis_clk_disable(csis); + fwnode_handle_put(csis->sd.fwnode); mutex_destroy(&csis->lock); return ret; @@ -1517,6 +1557,7 @@ static int mipi_csis_remove(struct platform_device *pdev) mipi_csis_runtime_suspend(&pdev->dev); mipi_csis_clk_disable(csis); media_entity_cleanup(&csis->sd.entity); + fwnode_handle_put(csis->sd.fwnode); mutex_destroy(&csis->lock); pm_runtime_set_suspended(&pdev->dev); diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index f993f349b66b..88f188e0f750 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -245,7 +245,7 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable) } if (!csid->testgen.enabled && - !media_entity_remote_pad(&csid->pads[MSM_CSID_PAD_SINK])) + !media_pad_remote_pad_first(&csid->pads[MSM_CSID_PAD_SINK])) return -ENOLINK; } @@ -518,7 +518,7 @@ static int csid_set_test_pattern(struct csid_device *csid, s32 value) struct csid_testgen_config *tg = &csid->testgen; /* If CSID is linked to CSIPHY, do not allow to enable test generator */ - if (value && media_entity_remote_pad(&csid->pads[MSM_CSID_PAD_SINK])) + if (value && media_pad_remote_pad_first(&csid->pads[MSM_CSID_PAD_SINK])) return -EBUSY; tg->enabled = !!value; @@ -666,7 +666,7 @@ int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, if (csid->num_supplies) { csid->supplies = devm_kmalloc_array(camss->dev, csid->num_supplies, - sizeof(csid->supplies), + sizeof(*csid->supplies), GFP_KERNEL); if (!csid->supplies) return -ENOMEM; @@ -729,7 +729,7 @@ static int csid_link_setup(struct media_entity *entity, const struct media_pad *remote, u32 flags) { if (flags & MEDIA_LNK_FL_ENABLED) - if (media_entity_remote_pad(local)) + if (media_pad_remote_pad_first(local)) return -EBUSY; if ((local->flags & MEDIA_PAD_FL_SINK) && diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index 75fcfc627400..3f726a7237f5 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -693,7 +693,7 @@ static int csiphy_link_setup(struct media_entity *entity, struct csiphy_device *csiphy; struct csid_device *csid; - if (media_entity_remote_pad(local)) + if (media_pad_remote_pad_first(local)) return -EBUSY; sd = media_entity_to_v4l2_subdev(entity); diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c index 4ee11bb979cd..b713f5b86aba 100644 --- a/drivers/media/platform/qcom/camss/camss-ispif.c +++ b/drivers/media/platform/qcom/camss/camss-ispif.c @@ -812,7 +812,7 @@ static int ispif_set_stream(struct v4l2_subdev *sd, int enable) int ret; if (enable) { - if (!media_entity_remote_pad(&line->pads[MSM_ISPIF_PAD_SINK])) + if (!media_pad_remote_pad_first(&line->pads[MSM_ISPIF_PAD_SINK])) return -ENOLINK; /* Config */ @@ -1253,6 +1253,41 @@ static enum ispif_intf ispif_get_intf(enum vfe_line_id line_id) } /* + * ispif_get_vfe_id - Get VFE HW module id + * @entity: Pointer to VFE media entity structure + * @id: Return CSID HW module id here + */ +static void ispif_get_vfe_id(struct media_entity *entity, u8 *id) +{ + struct v4l2_subdev *sd; + struct vfe_line *line; + struct vfe_device *vfe; + + sd = media_entity_to_v4l2_subdev(entity); + line = v4l2_get_subdevdata(sd); + vfe = to_vfe(line); + + *id = vfe->id; +} + +/* + * ispif_get_vfe_line_id - Get VFE line id by media entity + * @entity: Pointer to VFE media entity structure + * @id: Return VFE line id here + */ +static void ispif_get_vfe_line_id(struct media_entity *entity, + enum vfe_line_id *id) +{ + struct v4l2_subdev *sd; + struct vfe_line *line; + + sd = media_entity_to_v4l2_subdev(entity); + line = v4l2_get_subdevdata(sd); + + *id = line->id; +} + +/* * ispif_link_setup - Setup ISPIF connections * @entity: Pointer to media entity structure * @local: Pointer to local pad @@ -1266,7 +1301,7 @@ static int ispif_link_setup(struct media_entity *entity, const struct media_pad *remote, u32 flags) { if (flags & MEDIA_LNK_FL_ENABLED) { - if (media_entity_remote_pad(local)) + if (media_pad_remote_pad_first(local)) return -EBUSY; if (local->flags & MEDIA_PAD_FL_SINK) { @@ -1285,8 +1320,8 @@ static int ispif_link_setup(struct media_entity *entity, sd = media_entity_to_v4l2_subdev(entity); line = v4l2_get_subdevdata(sd); - msm_vfe_get_vfe_id(remote->entity, &line->vfe_id); - msm_vfe_get_vfe_line_id(remote->entity, &id); + ispif_get_vfe_id(remote->entity, &line->vfe_id); + ispif_get_vfe_line_id(remote->entity, &id); line->interface = ispif_get_intf(id); } } diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index 5b148e9f8134..a26e4a5d87b6 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -1423,40 +1423,6 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, } /* - * msm_vfe_get_vfe_id - Get VFE HW module id - * @entity: Pointer to VFE media entity structure - * @id: Return CSID HW module id here - */ -void msm_vfe_get_vfe_id(struct media_entity *entity, u8 *id) -{ - struct v4l2_subdev *sd; - struct vfe_line *line; - struct vfe_device *vfe; - - sd = media_entity_to_v4l2_subdev(entity); - line = v4l2_get_subdevdata(sd); - vfe = to_vfe(line); - - *id = vfe->id; -} - -/* - * msm_vfe_get_vfe_line_id - Get VFE line id by media entity - * @entity: Pointer to VFE media entity structure - * @id: Return VFE line id here - */ -void msm_vfe_get_vfe_line_id(struct media_entity *entity, enum vfe_line_id *id) -{ - struct v4l2_subdev *sd; - struct vfe_line *line; - - sd = media_entity_to_v4l2_subdev(entity); - line = v4l2_get_subdevdata(sd); - - *id = line->id; -} - -/* * vfe_link_setup - Setup VFE connections * @entity: Pointer to media entity structure * @local: Pointer to local pad @@ -1470,7 +1436,7 @@ static int vfe_link_setup(struct media_entity *entity, const struct media_pad *remote, u32 flags) { if (flags & MEDIA_LNK_FL_ENABLED) - if (media_entity_remote_pad(local)) + if (media_pad_remote_pad_first(local)) return -EBUSY; return 0; diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h index 0eba04eb9b77..cbc314c4e244 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.h +++ b/drivers/media/platform/qcom/camss/camss-vfe.h @@ -163,9 +163,6 @@ int msm_vfe_register_entities(struct vfe_device *vfe, void msm_vfe_unregister_entities(struct vfe_device *vfe); -void msm_vfe_get_vfe_id(struct media_entity *entity, u8 *id); -void msm_vfe_get_vfe_line_id(struct media_entity *entity, enum vfe_line_id *id); - /* * vfe_buf_add_pending - Add output buffer to list of pending * @output: VFE output diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index 307bb1dc4589..290df04c4d02 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -328,7 +328,7 @@ static struct v4l2_subdev *video_remote_subdev(struct camss_video *video, { struct media_pad *remote; - remote = media_entity_remote_pad(&video->pad); + remote = media_pad_remote_pad_first(&video->pad); if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) return NULL; @@ -507,7 +507,7 @@ static int video_start_streaming(struct vb2_queue *q, unsigned int count) if (!(pad->flags & MEDIA_PAD_FL_SINK)) break; - pad = media_entity_remote_pad(pad); + pad = media_pad_remote_pad_first(pad); if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; @@ -543,7 +543,7 @@ static void video_stop_streaming(struct vb2_queue *q) if (!(pad->flags & MEDIA_PAD_FL_SINK)) break; - pad = media_entity_remote_pad(pad); + pad = media_pad_remote_pad_first(pad); if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 79ad82e233cb..1118c40886d5 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -937,7 +937,7 @@ struct media_entity *camss_find_sensor(struct media_entity *entity) if (!(pad->flags & MEDIA_PAD_FL_SINK)) return NULL; - pad = media_entity_remote_pad(pad); + pad = media_pad_remote_pad_first(pad); if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) return NULL; @@ -1452,19 +1452,31 @@ static const struct media_device_ops camss_media_ops = { static int camss_configure_pd(struct camss *camss) { - int nbr_pm_domains = 0; + struct device *dev = camss->dev; int last_pm_domain = 0; int i; int ret; - if (camss->version == CAMSS_8x96 || - camss->version == CAMSS_660) - nbr_pm_domains = PM_DOMAIN_GEN1_COUNT; - else if (camss->version == CAMSS_845 || - camss->version == CAMSS_8250) - nbr_pm_domains = PM_DOMAIN_GEN2_COUNT; + camss->genpd_num = of_count_phandle_with_args(dev->of_node, + "power-domains", + "#power-domain-cells"); + if (camss->genpd_num < 0) { + dev_err(dev, "Power domains are not defined for camss\n"); + return camss->genpd_num; + } + + camss->genpd = devm_kmalloc_array(dev, camss->genpd_num, + sizeof(*camss->genpd), GFP_KERNEL); + if (!camss->genpd) + return -ENOMEM; + + camss->genpd_link = devm_kmalloc_array(dev, camss->genpd_num, + sizeof(*camss->genpd_link), + GFP_KERNEL); + if (!camss->genpd_link) + return -ENOMEM; - for (i = 0; i < nbr_pm_domains; i++) { + for (i = 0; i < camss->genpd_num; i++) { camss->genpd[i] = dev_pm_domain_attach_by_id(camss->dev, i); if (IS_ERR(camss->genpd[i])) { ret = PTR_ERR(camss->genpd[i]); @@ -1529,7 +1541,7 @@ static int camss_probe(struct platform_device *pdev) struct camss *camss; int num_subdevs, ret; - camss = kzalloc(sizeof(*camss), GFP_KERNEL); + camss = devm_kzalloc(dev, sizeof(*camss), GFP_KERNEL); if (!camss) return -ENOMEM; @@ -1567,39 +1579,30 @@ static int camss_probe(struct platform_device *pdev) camss->csid_num = 4; camss->vfe_num = 4; } else { - ret = -EINVAL; - goto err_free; + return -EINVAL; } camss->csiphy = devm_kcalloc(dev, camss->csiphy_num, sizeof(*camss->csiphy), GFP_KERNEL); - if (!camss->csiphy) { - ret = -ENOMEM; - goto err_free; - } + if (!camss->csiphy) + return -ENOMEM; camss->csid = devm_kcalloc(dev, camss->csid_num, sizeof(*camss->csid), GFP_KERNEL); - if (!camss->csid) { - ret = -ENOMEM; - goto err_free; - } + if (!camss->csid) + return -ENOMEM; if (camss->version == CAMSS_8x16 || camss->version == CAMSS_8x96) { camss->ispif = devm_kcalloc(dev, 1, sizeof(*camss->ispif), GFP_KERNEL); - if (!camss->ispif) { - ret = -ENOMEM; - goto err_free; - } + if (!camss->ispif) + return -ENOMEM; } camss->vfe = devm_kcalloc(dev, camss->vfe_num, sizeof(*camss->vfe), GFP_KERNEL); - if (!camss->vfe) { - ret = -ENOMEM; - goto err_free; - } + if (!camss->vfe) + return -ENOMEM; v4l2_async_nf_init(&camss->notifier); @@ -1681,15 +1684,12 @@ err_register_entities: v4l2_device_unregister(&camss->v4l2_dev); err_cleanup: v4l2_async_nf_cleanup(&camss->notifier); -err_free: - kfree(camss); return ret; } void camss_delete(struct camss *camss) { - int nbr_pm_domains = 0; int i; v4l2_device_unregister(&camss->v4l2_dev); @@ -1698,19 +1698,10 @@ void camss_delete(struct camss *camss) pm_runtime_disable(camss->dev); - if (camss->version == CAMSS_8x96 || - camss->version == CAMSS_660) - nbr_pm_domains = PM_DOMAIN_GEN1_COUNT; - else if (camss->version == CAMSS_845 || - camss->version == CAMSS_8250) - nbr_pm_domains = PM_DOMAIN_GEN2_COUNT; - - for (i = 0; i < nbr_pm_domains; i++) { + for (i = 0; i < camss->genpd_num; i++) { device_link_del(camss->genpd_link[i]); dev_pm_domain_detach(camss->genpd[i], true); } - - kfree(camss); } /* diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h index c9b3e0df5be8..0db80cadbbaa 100644 --- a/drivers/media/platform/qcom/camss/camss.h +++ b/drivers/media/platform/qcom/camss/camss.h @@ -69,9 +69,7 @@ struct resources_icc { enum pm_domain { PM_DOMAIN_VFE0 = 0, PM_DOMAIN_VFE1 = 1, - PM_DOMAIN_GEN1_COUNT = 2, /* CAMSS series of ISPs */ PM_DOMAIN_VFELITE = 2, /* VFELITE / TOP GDSC */ - PM_DOMAIN_GEN2_COUNT = 3, /* Titan series of ISPs */ }; enum camss_version { @@ -101,8 +99,9 @@ struct camss { int vfe_num; struct vfe_device *vfe; atomic_t ref_count; - struct device *genpd[PM_DOMAIN_GEN2_COUNT]; - struct device_link *genpd_link[PM_DOMAIN_GEN2_COUNT]; + int genpd_num; + struct device **genpd; + struct device_link **genpd_link; struct icc_path *icc_path[ICC_SM8250_COUNT]; struct icc_bw_tbl icc_bw_tbl[ICC_SM8250_COUNT]; }; diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index 877eca125803..990a1519f968 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -265,6 +265,19 @@ static void venus_assign_register_offsets(struct venus_core *core) } } +static irqreturn_t venus_isr_thread(int irq, void *dev_id) +{ + struct venus_core *core = dev_id; + irqreturn_t ret; + + ret = hfi_isr_thread(irq, dev_id); + + if (ret == IRQ_HANDLED && venus_fault_inject_ssr()) + hfi_core_trigger_ssr(core, HFI_TEST_SSR_SW_ERR_FATAL); + + return ret; +} + static int venus_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -320,7 +333,7 @@ static int venus_probe(struct platform_device *pdev) INIT_DELAYED_WORK(&core->work, venus_sys_error_handler); init_waitqueue_head(&core->sys_err_done); - ret = devm_request_threaded_irq(dev, core->irq, hfi_isr, hfi_isr_thread, + ret = devm_request_threaded_irq(dev, core->irq, hfi_isr, venus_isr_thread, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "venus", core); if (ret) @@ -832,6 +845,10 @@ static const struct reg_val sm7280_reg_preset[] = { { 0xb0088, 0 }, }; +static const struct hfi_ubwc_config sc7280_ubwc_config = { + 0, 0, {1, 1, 1, 0, 0, 0}, 8, 32, 14, 0, 0, {0, 0} +}; + static const struct venus_resources sc7280_res = { .freq_tbl = sc7280_freq_table, .freq_tbl_size = ARRAY_SIZE(sc7280_freq_table), @@ -841,6 +858,7 @@ static const struct venus_resources sc7280_res = { .bw_tbl_enc_size = ARRAY_SIZE(sc7280_bw_table_enc), .bw_tbl_dec = sc7280_bw_table_dec, .bw_tbl_dec_size = ARRAY_SIZE(sc7280_bw_table_dec), + .ubwc_conf = &sc7280_ubwc_config, .clks = {"core", "bus", "iface"}, .clks_num = 3, .vcodec0_clks = {"vcodec_core", "vcodec_bus"}, diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index d33825553edc..32551c2602a9 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -16,6 +16,7 @@ #include "dbgfs.h" #include "hfi.h" #include "hfi_platform.h" +#include "hfi_helper.h" #define VDBGL "VenusLow : " #define VDBGM "VenusMed : " @@ -57,6 +58,7 @@ struct venus_resources { unsigned int bw_tbl_dec_size; const struct reg_val *reg_tbl; unsigned int reg_tbl_size; + const struct hfi_ubwc_config *ubwc_conf; const char * const clks[VIDC_CLKS_NUM_MAX]; unsigned int clks_num; const char * const vcodec0_clks[VIDC_VCODEC_CLKS_NUM_MAX]; diff --git a/drivers/media/platform/qcom/venus/dbgfs.c b/drivers/media/platform/qcom/venus/dbgfs.c index 52de47f2ca88..726f4b730e69 100644 --- a/drivers/media/platform/qcom/venus/dbgfs.c +++ b/drivers/media/platform/qcom/venus/dbgfs.c @@ -4,13 +4,22 @@ */ #include <linux/debugfs.h> +#include <linux/fault-inject.h> #include "core.h" +#ifdef CONFIG_FAULT_INJECTION +DECLARE_FAULT_ATTR(venus_ssr_attr); +#endif + void venus_dbgfs_init(struct venus_core *core) { core->root = debugfs_create_dir("venus", NULL); debugfs_create_x32("fw_level", 0644, core->root, &venus_fw_debug); + +#ifdef CONFIG_FAULT_INJECTION + fault_create_debugfs_attr("fail_ssr", core->root, &venus_ssr_attr); +#endif } void venus_dbgfs_deinit(struct venus_core *core) diff --git a/drivers/media/platform/qcom/venus/dbgfs.h b/drivers/media/platform/qcom/venus/dbgfs.h index b7b621a8472f..c87c1355d039 100644 --- a/drivers/media/platform/qcom/venus/dbgfs.h +++ b/drivers/media/platform/qcom/venus/dbgfs.h @@ -4,8 +4,21 @@ #ifndef __VENUS_DBGFS_H__ #define __VENUS_DBGFS_H__ +#include <linux/fault-inject.h> + struct venus_core; +#ifdef CONFIG_FAULT_INJECTION +extern struct fault_attr venus_ssr_attr; +static inline bool venus_fault_inject_ssr(void) +{ + return should_fail(&venus_ssr_attr, 1); +} +#else +static inline bool venus_fault_inject_ssr(void) { return false; } +#endif + + void venus_dbgfs_init(struct venus_core *core); void venus_dbgfs_deinit(struct venus_core *core); diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 5c1104379c49..60de4200375d 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -671,8 +671,7 @@ int venus_helper_get_bufreq(struct venus_inst *inst, u32 type, unsigned int i; int ret; - if (req) - memset(req, 0, sizeof(*req)); + memset(req, 0, sizeof(*req)); if (type == HFI_BUFFER_OUTPUT || type == HFI_BUFFER_OUTPUT2) req->count_min = inst->fw_min_cnt; @@ -694,8 +693,7 @@ int venus_helper_get_bufreq(struct venus_inst *inst, u32 type, if (hprop.bufreq[i].type != type) continue; - if (req) - memcpy(req, &hprop.bufreq[i], sizeof(*req)); + memcpy(req, &hprop.bufreq[i], sizeof(*req)); ret = 0; break; } diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.c b/drivers/media/platform/qcom/venus/hfi_cmds.c index 4ecd444050bb..930b743f225e 100644 --- a/drivers/media/platform/qcom/venus/hfi_cmds.c +++ b/drivers/media/platform/qcom/venus/hfi_cmds.c @@ -58,6 +58,15 @@ void pkt_sys_coverage_config(struct hfi_sys_set_property_pkt *pkt, u32 mode) pkt->data[1] = mode; } +void pkt_sys_ubwc_config(struct hfi_sys_set_property_pkt *pkt, const struct hfi_ubwc_config *hfi) +{ + pkt->hdr.size = struct_size(pkt, data, 1) + sizeof(*hfi); + pkt->hdr.pkt_type = HFI_CMD_SYS_SET_PROPERTY; + pkt->num_properties = 1; + pkt->data[0] = HFI_PROPERTY_SYS_UBWC_CONFIG; + memcpy(&pkt->data[1], hfi, sizeof(*hfi)); +} + int pkt_sys_set_resource(struct hfi_sys_set_resource_pkt *pkt, u32 id, u32 size, u32 addr, void *cookie) { diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.h b/drivers/media/platform/qcom/venus/hfi_cmds.h index 327ed90a2788..99bc0b6db67c 100644 --- a/drivers/media/platform/qcom/venus/hfi_cmds.h +++ b/drivers/media/platform/qcom/venus/hfi_cmds.h @@ -256,6 +256,7 @@ void pkt_sys_init(struct hfi_sys_init_pkt *pkt, u32 arch_type); void pkt_sys_pc_prep(struct hfi_sys_pc_prep_pkt *pkt); void pkt_sys_idle_indicator(struct hfi_sys_set_property_pkt *pkt, u32 enable); void pkt_sys_power_control(struct hfi_sys_set_property_pkt *pkt, u32 enable); +void pkt_sys_ubwc_config(struct hfi_sys_set_property_pkt *pkt, const struct hfi_ubwc_config *hfi); int pkt_sys_set_resource(struct hfi_sys_set_resource_pkt *pkt, u32 id, u32 size, u32 addr, void *cookie); int pkt_sys_unset_resource(struct hfi_sys_release_resource_pkt *pkt, u32 id, diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h index 2daa88e3df9f..d2d6719a2ba4 100644 --- a/drivers/media/platform/qcom/venus/hfi_helper.h +++ b/drivers/media/platform/qcom/venus/hfi_helper.h @@ -427,6 +427,7 @@ #define HFI_PROPERTY_SYS_CODEC_POWER_PLANE_CTRL 0x5 #define HFI_PROPERTY_SYS_IMAGE_VERSION 0x6 #define HFI_PROPERTY_SYS_CONFIG_COVERAGE 0x7 +#define HFI_PROPERTY_SYS_UBWC_CONFIG 0x8 /* * HFI_PROPERTY_PARAM_COMMON_START @@ -626,6 +627,25 @@ struct hfi_debug_config { u32 mode; }; +struct hfi_ubwc_config { + u32 size; + u32 packet_type; + struct { + u32 max_channel_override : 1; + u32 mal_length_override : 1; + u32 hb_override : 1; + u32 bank_swzl_level_override : 1; + u32 bank_spreading_override : 1; + u32 reserved : 27; + } override_bit_info; + u32 max_channels; + u32 mal_length; + u32 highest_bank_bit; + u32 bank_swzl_level; + u32 bank_spreading; + u32 reserved[2]; +}; + struct hfi_enable { u32 enable; }; diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c index 5b8389b98299..6cf74b2bc5ae 100644 --- a/drivers/media/platform/qcom/venus/hfi_parser.c +++ b/drivers/media/platform/qcom/venus/hfi_parser.c @@ -234,6 +234,7 @@ static int hfi_platform_parser(struct venus_core *core, struct venus_inst *inst) const struct hfi_plat_caps *caps = NULL; u32 enc_codecs, dec_codecs, count = 0; unsigned int entries; + int ret; plat = hfi_platform_get(core->res->hfi_version); if (!plat) @@ -242,8 +243,9 @@ static int hfi_platform_parser(struct venus_core *core, struct venus_inst *inst) if (inst) return 0; - if (plat->codecs) - plat->codecs(&enc_codecs, &dec_codecs, &count); + ret = hfi_platform_get_codecs(core, &enc_codecs, &dec_codecs, &count); + if (ret) + return ret; if (plat->capabilities) caps = plat->capabilities(&entries); diff --git a/drivers/media/platform/qcom/venus/hfi_platform.c b/drivers/media/platform/qcom/venus/hfi_platform.c index f16f8962273c..f07f554bc5fe 100644 --- a/drivers/media/platform/qcom/venus/hfi_platform.c +++ b/drivers/media/platform/qcom/venus/hfi_platform.c @@ -2,7 +2,9 @@ /* * Copyright (c) 2020, The Linux Foundation. All rights reserved. */ +#include <linux/of_device.h> #include "hfi_platform.h" +#include "core.h" const struct hfi_platform *hfi_platform_get(enum hfi_version version) { @@ -66,3 +68,23 @@ hfi_platform_get_codec_lp_freq(enum hfi_version version, u32 codec, u32 session_ return freq; } +int +hfi_platform_get_codecs(struct venus_core *core, u32 *enc_codecs, u32 *dec_codecs, u32 *count) +{ + const struct hfi_platform *plat; + + plat = hfi_platform_get(core->res->hfi_version); + if (!plat) + return -EINVAL; + + if (plat->codecs) + plat->codecs(enc_codecs, dec_codecs, count); + + if (of_device_is_compatible(core->dev->of_node, "qcom,sc7280-venus")) { + *enc_codecs &= ~HFI_VIDEO_CODEC_VP8; + *dec_codecs &= ~HFI_VIDEO_CODEC_VP8; + } + + return 0; +} + diff --git a/drivers/media/platform/qcom/venus/hfi_platform.h b/drivers/media/platform/qcom/venus/hfi_platform.h index 1dcf4085928c..ec89a90a8129 100644 --- a/drivers/media/platform/qcom/venus/hfi_platform.h +++ b/drivers/media/platform/qcom/venus/hfi_platform.h @@ -66,4 +66,6 @@ unsigned long hfi_platform_get_codec_vsp_freq(enum hfi_version version, u32 code u32 session_type); unsigned long hfi_platform_get_codec_lp_freq(enum hfi_version version, u32 codec, u32 session_type); +int hfi_platform_get_codecs(struct venus_core *core, u32 *enc_codecs, u32 *dec_codecs, + u32 *count); #endif diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c index 9a34662fea38..2ad40b3945b0 100644 --- a/drivers/media/platform/qcom/venus/hfi_venus.c +++ b/drivers/media/platform/qcom/venus/hfi_venus.c @@ -904,6 +904,24 @@ static int venus_sys_set_power_control(struct venus_hfi_device *hdev, return 0; } +static int venus_sys_set_ubwc_config(struct venus_hfi_device *hdev) +{ + struct hfi_sys_set_property_pkt *pkt; + u8 packet[IFACEQ_VAR_SMALL_PKT_SIZE]; + const struct venus_resources *res = hdev->core->res; + int ret; + + pkt = (struct hfi_sys_set_property_pkt *)packet; + + pkt_sys_ubwc_config(pkt, res->ubwc_conf); + + ret = venus_iface_cmdq_write(hdev, pkt, false); + if (ret) + return ret; + + return 0; +} + static int venus_get_queue_size(struct venus_hfi_device *hdev, unsigned int index) { @@ -922,6 +940,7 @@ static int venus_get_queue_size(struct venus_hfi_device *hdev, static int venus_sys_set_default_properties(struct venus_hfi_device *hdev) { struct device *dev = hdev->core->dev; + const struct venus_resources *res = hdev->core->res; int ret; ret = venus_sys_set_debug(hdev, venus_fw_debug); @@ -945,6 +964,13 @@ static int venus_sys_set_default_properties(struct venus_hfi_device *hdev) dev_warn(dev, "setting hw power collapse ON failed (%d)\n", ret); + /* For specific venus core, it is mandatory to set the UBWC configuration */ + if (res->ubwc_conf) { + ret = venus_sys_set_ubwc_config(hdev); + if (ret) + dev_warn(dev, "setting ubwc config failed (%d)\n", ret); + } + return ret; } diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-core.c b/drivers/media/platform/renesas/rcar-vin/rcar-core.c index 49bdcfba010b..968a74234e92 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-core.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-core.c @@ -845,7 +845,7 @@ static int rvin_csi2_link_notify(struct media_link *link, u32 flags, continue; /* Get remote CSI-2, if any. */ - csi_pad = media_entity_remote_pad( + csi_pad = media_pad_remote_pad_first( &group->vin[i]->vdev.entity.pads[0]); if (!csi_pad) continue; @@ -1261,7 +1261,7 @@ static const struct rvin_info rcar_info_r8a77980 = { }; static const struct rvin_group_route rcar_info_r8a77990_routes[] = { - { .master = 0, .csi = RVIN_CSI40, .chsel = 0x03 }, + { .master = 4, .csi = RVIN_CSI40, .chsel = 0x03 }, { /* Sentinel */ } }; diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c b/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c index fea8f00a9152..174aa6176f54 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c @@ -1313,7 +1313,7 @@ static int rcsi2_link_setup(struct media_entity *entity, channel = id % 4; if (flags & MEDIA_LNK_FL_ENABLED) { - if (media_entity_remote_pad(local)) { + if (media_pad_remote_pad_first(local)) { dev_dbg(priv->dev, "Each VC can only be routed to one output channel\n"); return -EINVAL; diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c index 6644b498929d..8d37fbdc266a 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c @@ -1258,7 +1258,7 @@ static int rvin_set_stream(struct rvin_dev *vin, int on) return ret == -ENOIOCTLCMD ? 0 : ret; } - pad = media_entity_remote_pad(&vin->pad); + pad = media_pad_remote_pad_first(&vin->pad); if (!pad) return -EPIPE; diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c b/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c index 2e2aa9d746ee..576059f9bbe3 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c @@ -1032,7 +1032,7 @@ static void rvin_notify(struct v4l2_subdev *sd, if (!vin) continue; - pad = media_entity_remote_pad(&vin->pad); + pad = media_pad_remote_pad_first(&vin->pad); if (!pad) continue; diff --git a/drivers/media/platform/renesas/rcar_drif.c b/drivers/media/platform/renesas/rcar_drif.c index 9a0982fa5c6b..3fec41f6e964 100644 --- a/drivers/media/platform/renesas/rcar_drif.c +++ b/drivers/media/platform/renesas/rcar_drif.c @@ -3,11 +3,6 @@ * R-Car Gen3 Digital Radio Interface (DRIF) driver * * Copyright (C) 2017 Renesas Electronics Corporation - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ /* @@ -1477,7 +1472,7 @@ MODULE_DEVICE_TABLE(of, rcar_drif_of_table); static struct platform_driver rcar_drif_driver = { .driver = { .name = RCAR_DRIF_DRV_NAME, - .of_match_table = of_match_ptr(rcar_drif_of_table), + .of_match_table = rcar_drif_of_table, .pm = &rcar_drif_pm_ops, }, .probe = rcar_drif_probe, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c index a116a3362f9e..4c3bd2b1ca28 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c @@ -516,8 +516,8 @@ int vsp1_entity_link_setup(struct media_entity *entity, * higher than one for the data pipelines, except for the links to the HGO and * HGT that can be enabled in addition to a regular data link. When traversing * outgoing links this function ignores HGO and HGT entities and should thus be - * used in place of the generic media_entity_remote_pad() function to traverse - * data pipelines. + * used in place of the generic media_pad_remote_pad_first() function to + * traverse data pipelines. * * Return a pointer to the pad at the remote end of the first found enabled * link, or NULL if no enabled link has been found. diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c index 51219b1b6ea9..e8e0ee5f2277 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_video.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c @@ -50,7 +50,7 @@ vsp1_video_remote_subdev(struct media_pad *local, u32 *pad) { struct media_pad *remote; - remote = media_entity_remote_pad(local); + remote = media_pad_remote_pad_first(local); if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) return NULL; diff --git a/drivers/media/platform/rockchip/rkisp1/Kconfig b/drivers/media/platform/rockchip/rkisp1/Kconfig index dabd7e42c193..731c9acbf6ef 100644 --- a/drivers/media/platform/rockchip/rkisp1/Kconfig +++ b/drivers/media/platform/rockchip/rkisp1/Kconfig @@ -3,7 +3,7 @@ config VIDEO_ROCKCHIP_ISP1 tristate "Rockchip Image Signal Processing v1 Unit driver" depends on V4L_PLATFORM_DRIVERS depends on VIDEO_DEV && OF - depends on ARCH_ROCKCHIP || COMPILE_TEST + depends on ARCH_ROCKCHIP || ARCH_MXC || COMPILE_TEST select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select VIDEOBUF2_DMA_CONTIG diff --git a/drivers/media/platform/rockchip/rkisp1/Makefile b/drivers/media/platform/rockchip/rkisp1/Makefile index ab32a77db8f7..b3844c4f7623 100644 --- a/drivers/media/platform/rockchip/rkisp1/Makefile +++ b/drivers/media/platform/rockchip/rkisp1/Makefile @@ -1,10 +1,14 @@ # SPDX-License-Identifier: GPL-2.0 +rockchip-isp1-y := rkisp1-capture.o \ + rkisp1-common.o \ + rkisp1-csi.o \ + rkisp1-dev.o \ + rkisp1-isp.o \ + rkisp1-resizer.o \ + rkisp1-stats.o \ + rkisp1-params.o + +rockchip-isp1-$(CONFIG_DEBUG_FS) += rkisp1-debug.o + obj-$(CONFIG_VIDEO_ROCKCHIP_ISP1) += rockchip-isp1.o -rockchip-isp1-objs += rkisp1-capture.o \ - rkisp1-common.o \ - rkisp1-dev.o \ - rkisp1-isp.o \ - rkisp1-resizer.o \ - rkisp1-stats.o \ - rkisp1-params.o diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c index fee2aaacb26b..d5904c96ff3f 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c @@ -383,7 +383,7 @@ static void rkisp1_mi_config_ctrl(struct rkisp1_capture *cap) mi_ctrl |= RKISP1_CIF_MI_CTRL_INIT_BASE_EN | RKISP1_CIF_MI_CTRL_INIT_OFFSET_EN; - rkisp1_write(cap->rkisp1, mi_ctrl, RKISP1_CIF_MI_CTRL); + rkisp1_write(cap->rkisp1, RKISP1_CIF_MI_CTRL, mi_ctrl); } static u32 rkisp1_pixfmt_comp_size(const struct v4l2_pix_format_mplane *pixm, @@ -404,7 +404,7 @@ static void rkisp1_irq_frame_end_enable(struct rkisp1_capture *cap) u32 mi_imsc = rkisp1_read(cap->rkisp1, RKISP1_CIF_MI_IMSC); mi_imsc |= RKISP1_CIF_MI_FRAME(cap); - rkisp1_write(cap->rkisp1, mi_imsc, RKISP1_CIF_MI_IMSC); + rkisp1_write(cap->rkisp1, RKISP1_CIF_MI_IMSC, mi_imsc); } static void rkisp1_mp_config(struct rkisp1_capture *cap) @@ -413,12 +413,12 @@ static void rkisp1_mp_config(struct rkisp1_capture *cap) struct rkisp1_device *rkisp1 = cap->rkisp1; u32 reg; - rkisp1_write(rkisp1, rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_Y), - cap->config->mi.y_size_init); - rkisp1_write(rkisp1, rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_CB), - cap->config->mi.cb_size_init); - rkisp1_write(rkisp1, rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_CR), - cap->config->mi.cr_size_init); + rkisp1_write(rkisp1, cap->config->mi.y_size_init, + rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_Y)); + rkisp1_write(rkisp1, cap->config->mi.cb_size_init, + rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_CB)); + rkisp1_write(rkisp1, cap->config->mi.cr_size_init, + rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_CR)); rkisp1_irq_frame_end_enable(cap); @@ -429,7 +429,7 @@ static void rkisp1_mp_config(struct rkisp1_capture *cap) reg |= RKISP1_CIF_MI_XTD_FMT_CTRL_MP_CB_CR_SWAP; else reg &= ~RKISP1_CIF_MI_XTD_FMT_CTRL_MP_CB_CR_SWAP; - rkisp1_write(rkisp1, reg, RKISP1_CIF_MI_XTD_FORMAT_CTRL); + rkisp1_write(rkisp1, RKISP1_CIF_MI_XTD_FORMAT_CTRL, reg); } rkisp1_mi_config_ctrl(cap); @@ -437,11 +437,11 @@ static void rkisp1_mp_config(struct rkisp1_capture *cap) reg = rkisp1_read(rkisp1, RKISP1_CIF_MI_CTRL); reg &= ~RKISP1_MI_CTRL_MP_FMT_MASK; reg |= cap->pix.cfg->write_format; - rkisp1_write(rkisp1, reg, RKISP1_CIF_MI_CTRL); + rkisp1_write(rkisp1, RKISP1_CIF_MI_CTRL, reg); reg = rkisp1_read(rkisp1, RKISP1_CIF_MI_CTRL); reg |= RKISP1_CIF_MI_MP_AUTOUPDATE_ENABLE; - rkisp1_write(rkisp1, reg, RKISP1_CIF_MI_CTRL); + rkisp1_write(rkisp1, RKISP1_CIF_MI_CTRL, reg); } static void rkisp1_sp_config(struct rkisp1_capture *cap) @@ -450,16 +450,16 @@ static void rkisp1_sp_config(struct rkisp1_capture *cap) struct rkisp1_device *rkisp1 = cap->rkisp1; u32 mi_ctrl, reg; - rkisp1_write(rkisp1, rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_Y), - cap->config->mi.y_size_init); - rkisp1_write(rkisp1, rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_CB), - cap->config->mi.cb_size_init); - rkisp1_write(rkisp1, rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_CR), - cap->config->mi.cr_size_init); + rkisp1_write(rkisp1, cap->config->mi.y_size_init, + rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_Y)); + rkisp1_write(rkisp1, cap->config->mi.cb_size_init, + rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_CB)); + rkisp1_write(rkisp1, cap->config->mi.cr_size_init, + rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_CR)); - rkisp1_write(rkisp1, pixm->width, RKISP1_CIF_MI_SP_Y_PIC_WIDTH); - rkisp1_write(rkisp1, pixm->height, RKISP1_CIF_MI_SP_Y_PIC_HEIGHT); - rkisp1_write(rkisp1, cap->sp_y_stride, RKISP1_CIF_MI_SP_Y_LLENGTH); + rkisp1_write(rkisp1, RKISP1_CIF_MI_SP_Y_PIC_WIDTH, pixm->width); + rkisp1_write(rkisp1, RKISP1_CIF_MI_SP_Y_PIC_HEIGHT, pixm->height); + rkisp1_write(rkisp1, RKISP1_CIF_MI_SP_Y_LLENGTH, cap->sp_y_stride); rkisp1_irq_frame_end_enable(cap); @@ -470,7 +470,7 @@ static void rkisp1_sp_config(struct rkisp1_capture *cap) reg |= RKISP1_CIF_MI_XTD_FMT_CTRL_SP_CB_CR_SWAP; else reg &= ~RKISP1_CIF_MI_XTD_FMT_CTRL_SP_CB_CR_SWAP; - rkisp1_write(rkisp1, reg, RKISP1_CIF_MI_XTD_FORMAT_CTRL); + rkisp1_write(rkisp1, RKISP1_CIF_MI_XTD_FORMAT_CTRL, reg); } rkisp1_mi_config_ctrl(cap); @@ -481,7 +481,7 @@ static void rkisp1_sp_config(struct rkisp1_capture *cap) RKISP1_MI_CTRL_SP_INPUT_YUV422 | cap->pix.cfg->output_format | RKISP1_CIF_MI_SP_AUTOUPDATE_ENABLE; - rkisp1_write(rkisp1, mi_ctrl, RKISP1_CIF_MI_CTRL); + rkisp1_write(rkisp1, RKISP1_CIF_MI_CTRL, mi_ctrl); } static void rkisp1_mp_disable(struct rkisp1_capture *cap) @@ -490,7 +490,7 @@ static void rkisp1_mp_disable(struct rkisp1_capture *cap) mi_ctrl &= ~(RKISP1_CIF_MI_CTRL_MP_ENABLE | RKISP1_CIF_MI_CTRL_RAW_ENABLE); - rkisp1_write(cap->rkisp1, mi_ctrl, RKISP1_CIF_MI_CTRL); + rkisp1_write(cap->rkisp1, RKISP1_CIF_MI_CTRL, mi_ctrl); } static void rkisp1_sp_disable(struct rkisp1_capture *cap) @@ -498,7 +498,7 @@ static void rkisp1_sp_disable(struct rkisp1_capture *cap) u32 mi_ctrl = rkisp1_read(cap->rkisp1, RKISP1_CIF_MI_CTRL); mi_ctrl &= ~RKISP1_CIF_MI_CTRL_SP_ENABLE; - rkisp1_write(cap->rkisp1, mi_ctrl, RKISP1_CIF_MI_CTRL); + rkisp1_write(cap->rkisp1, RKISP1_CIF_MI_CTRL, mi_ctrl); } static void rkisp1_mp_enable(struct rkisp1_capture *cap) @@ -514,7 +514,7 @@ static void rkisp1_mp_enable(struct rkisp1_capture *cap) else mi_ctrl |= RKISP1_CIF_MI_CTRL_MP_ENABLE; - rkisp1_write(cap->rkisp1, mi_ctrl, RKISP1_CIF_MI_CTRL); + rkisp1_write(cap->rkisp1, RKISP1_CIF_MI_CTRL, mi_ctrl); } static void rkisp1_sp_enable(struct rkisp1_capture *cap) @@ -522,15 +522,14 @@ static void rkisp1_sp_enable(struct rkisp1_capture *cap) u32 mi_ctrl = rkisp1_read(cap->rkisp1, RKISP1_CIF_MI_CTRL); mi_ctrl |= RKISP1_CIF_MI_CTRL_SP_ENABLE; - rkisp1_write(cap->rkisp1, mi_ctrl, RKISP1_CIF_MI_CTRL); + rkisp1_write(cap->rkisp1, RKISP1_CIF_MI_CTRL, mi_ctrl); } static void rkisp1_mp_sp_stop(struct rkisp1_capture *cap) { if (!cap->is_streaming) return; - rkisp1_write(cap->rkisp1, - RKISP1_CIF_MI_FRAME(cap), RKISP1_CIF_MI_ICR); + rkisp1_write(cap->rkisp1, RKISP1_CIF_MI_ICR, RKISP1_CIF_MI_FRAME(cap)); cap->ops->disable(cap); } @@ -554,7 +553,7 @@ static void rkisp1_mp_set_data_path(struct rkisp1_capture *cap) dpcl = dpcl | RKISP1_CIF_VI_DPCL_CHAN_MODE_MP | RKISP1_CIF_VI_DPCL_MP_MUX_MRSZ_MI; - rkisp1_write(cap->rkisp1, dpcl, RKISP1_CIF_VI_DPCL); + rkisp1_write(cap->rkisp1, RKISP1_CIF_VI_DPCL, dpcl); } static void rkisp1_sp_set_data_path(struct rkisp1_capture *cap) @@ -562,7 +561,7 @@ static void rkisp1_sp_set_data_path(struct rkisp1_capture *cap) u32 dpcl = rkisp1_read(cap->rkisp1, RKISP1_CIF_VI_DPCL); dpcl |= RKISP1_CIF_VI_DPCL_CHAN_MODE_SP; - rkisp1_write(cap->rkisp1, dpcl, RKISP1_CIF_VI_DPCL); + rkisp1_write(cap->rkisp1, RKISP1_CIF_VI_DPCL, dpcl); } static const struct rkisp1_capture_ops rkisp1_capture_ops_mp = { @@ -628,9 +627,8 @@ static void rkisp1_set_next_buf(struct rkisp1_capture *cap) buff_addr = cap->buf.next->buff_addr; - rkisp1_write(cap->rkisp1, - buff_addr[RKISP1_PLANE_Y], - cap->config->mi.y_base_ad_init); + rkisp1_write(cap->rkisp1, cap->config->mi.y_base_ad_init, + buff_addr[RKISP1_PLANE_Y]); /* * In order to support grey format we capture * YUV422 planar format from the camera and @@ -638,39 +636,36 @@ static void rkisp1_set_next_buf(struct rkisp1_capture *cap) */ if (cap->pix.cfg->fourcc == V4L2_PIX_FMT_GREY) { rkisp1_write(cap->rkisp1, - cap->buf.dummy.dma_addr, - cap->config->mi.cb_base_ad_init); + cap->config->mi.cb_base_ad_init, + cap->buf.dummy.dma_addr); rkisp1_write(cap->rkisp1, - cap->buf.dummy.dma_addr, - cap->config->mi.cr_base_ad_init); + cap->config->mi.cr_base_ad_init, + cap->buf.dummy.dma_addr); } else { rkisp1_write(cap->rkisp1, - buff_addr[RKISP1_PLANE_CB], - cap->config->mi.cb_base_ad_init); + cap->config->mi.cb_base_ad_init, + buff_addr[RKISP1_PLANE_CB]); rkisp1_write(cap->rkisp1, - buff_addr[RKISP1_PLANE_CR], - cap->config->mi.cr_base_ad_init); + cap->config->mi.cr_base_ad_init, + buff_addr[RKISP1_PLANE_CR]); } } else { /* * Use the dummy space allocated by dma_alloc_coherent to * throw data if there is no available buffer. */ - rkisp1_write(cap->rkisp1, - cap->buf.dummy.dma_addr, - cap->config->mi.y_base_ad_init); - rkisp1_write(cap->rkisp1, - cap->buf.dummy.dma_addr, - cap->config->mi.cb_base_ad_init); - rkisp1_write(cap->rkisp1, - cap->buf.dummy.dma_addr, - cap->config->mi.cr_base_ad_init); + rkisp1_write(cap->rkisp1, cap->config->mi.y_base_ad_init, + cap->buf.dummy.dma_addr); + rkisp1_write(cap->rkisp1, cap->config->mi.cb_base_ad_init, + cap->buf.dummy.dma_addr); + rkisp1_write(cap->rkisp1, cap->config->mi.cr_base_ad_init, + cap->buf.dummy.dma_addr); } /* Set plane offsets */ - rkisp1_write(cap->rkisp1, 0, cap->config->mi.y_offs_cnt_init); - rkisp1_write(cap->rkisp1, 0, cap->config->mi.cb_offs_cnt_init); - rkisp1_write(cap->rkisp1, 0, cap->config->mi.cr_offs_cnt_init); + rkisp1_write(cap->rkisp1, cap->config->mi.y_offs_cnt_init, 0); + rkisp1_write(cap->rkisp1, cap->config->mi.cb_offs_cnt_init, 0); + rkisp1_write(cap->rkisp1, cap->config->mi.cr_offs_cnt_init, 0); } /* @@ -710,7 +705,7 @@ irqreturn_t rkisp1_capture_isr(int irq, void *ctx) if (!status) return IRQ_NONE; - rkisp1_write(rkisp1, status, RKISP1_CIF_MI_ICR); + rkisp1_write(rkisp1, RKISP1_CIF_MI_ICR, status); for (i = 0; i < ARRAY_SIZE(rkisp1->capture_devs); ++i) { struct rkisp1_capture *cap = &rkisp1->capture_devs[i]; @@ -888,8 +883,8 @@ static void rkisp1_cap_stream_enable(struct rkisp1_capture *cap) */ if (!other->is_streaming) { /* force cfg update */ - rkisp1_write(rkisp1, - RKISP1_CIF_MI_INIT_SOFT_UPD, RKISP1_CIF_MI_INIT); + rkisp1_write(rkisp1, RKISP1_CIF_MI_INIT, + RKISP1_CIF_MI_INIT_SOFT_UPD); rkisp1_set_next_buf(cap); } spin_unlock_irq(&cap->buf.lock); @@ -931,11 +926,8 @@ static void rkisp1_pipeline_stream_disable(struct rkisp1_capture *cap) * If the other capture is streaming, isp and sensor nodes shouldn't * be disabled, skip them. */ - if (rkisp1->pipe.streaming_count < 2) { - v4l2_subdev_call(rkisp1->active_sensor->sd, video, s_stream, - false); + if (rkisp1->pipe.streaming_count < 2) v4l2_subdev_call(&rkisp1->isp.sd, video, s_stream, false); - } v4l2_subdev_call(&rkisp1->resizer_devs[cap->id].sd, video, s_stream, false); @@ -971,15 +963,8 @@ static int rkisp1_pipeline_stream_enable(struct rkisp1_capture *cap) if (ret) goto err_disable_rsz; - ret = v4l2_subdev_call(rkisp1->active_sensor->sd, video, s_stream, - true); - if (ret) - goto err_disable_isp; - return 0; -err_disable_isp: - v4l2_subdev_call(&rkisp1->isp.sd, video, s_stream, false); err_disable_rsz: v4l2_subdev_call(&rkisp1->resizer_devs[cap->id].sd, video, s_stream, false); @@ -1253,11 +1238,8 @@ static int rkisp1_g_fmt_vid_cap_mplane(struct file *file, void *fh, static int rkisp1_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct rkisp1_capture *cap_dev = video_drvdata(file); - struct rkisp1_device *rkisp1 = cap_dev->rkisp1; - - strscpy(cap->driver, rkisp1->dev->driver->name, sizeof(cap->driver)); - strscpy(cap->card, rkisp1->dev->driver->name, sizeof(cap->card)); + strscpy(cap->driver, RKISP1_DRIVER_NAME, sizeof(cap->driver)); + strscpy(cap->card, RKISP1_DRIVER_NAME, sizeof(cap->card)); strscpy(cap->bus_info, RKISP1_BUS_INFO, sizeof(cap->bus_info)); return 0; @@ -1302,8 +1284,16 @@ static int rkisp1_capture_link_validate(struct media_link *link) if (sd_fmt.format.height != cap->pix.fmt.height || sd_fmt.format.width != cap->pix.fmt.width || - sd_fmt.format.code != fmt->mbus) + sd_fmt.format.code != fmt->mbus) { + dev_dbg(cap->rkisp1->dev, + "link '%s':%u -> '%s':%u not valid: 0x%04x/%ux%u != 0x%04x/%ux%u\n", + link->source->entity->name, link->source->index, + link->sink->entity->name, link->sink->index, + sd_fmt.format.code, sd_fmt.format.width, + sd_fmt.format.height, fmt->mbus, cap->pix.fmt.width, + cap->pix.fmt.height); return -EPIPE; + } return 0; } @@ -1326,8 +1316,12 @@ static const struct v4l2_file_operations rkisp1_fops = { static void rkisp1_unregister_capture(struct rkisp1_capture *cap) { + if (!video_is_registered(&cap->vnode.vdev)) + return; + media_entity_cleanup(&cap->vnode.vdev.entity); vb2_video_unregister_device(&cap->vnode.vdev); + mutex_destroy(&cap->vnode.vlock); } void rkisp1_capture_devs_unregister(struct rkisp1_device *rkisp1) @@ -1381,27 +1375,31 @@ static int rkisp1_register_capture(struct rkisp1_capture *cap) if (ret) { dev_err(cap->rkisp1->dev, "vb2 queue init failed (err=%d)\n", ret); - return ret; + goto error; } vdev->queue = q; + ret = media_entity_pads_init(&vdev->entity, 1, &node->pad); + if (ret) + goto error; + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) { dev_err(cap->rkisp1->dev, "failed to register %s, ret=%d\n", vdev->name, ret); - return ret; + goto error; } + v4l2_info(v4l2_dev, "registered %s as /dev/video%d\n", vdev->name, vdev->num); - ret = media_entity_pads_init(&vdev->entity, 1, &node->pad); - if (ret) { - video_unregister_device(vdev); - return ret; - } - return 0; + +error: + media_entity_cleanup(&vdev->entity); + mutex_destroy(&node->vlock); + return ret; } static void @@ -1436,26 +1434,21 @@ rkisp1_capture_init(struct rkisp1_device *rkisp1, enum rkisp1_stream_id id) int rkisp1_capture_devs_register(struct rkisp1_device *rkisp1) { - struct rkisp1_capture *cap; - unsigned int i, j; + unsigned int i; int ret; for (i = 0; i < ARRAY_SIZE(rkisp1->capture_devs); i++) { + struct rkisp1_capture *cap = &rkisp1->capture_devs[i]; + rkisp1_capture_init(rkisp1, i); - cap = &rkisp1->capture_devs[i]; - cap->rkisp1 = rkisp1; + ret = rkisp1_register_capture(cap); - if (ret) - goto err_unreg_capture_devs; + if (ret) { + rkisp1_capture_devs_unregister(rkisp1); + return ret; + } } return 0; -err_unreg_capture_devs: - for (j = 0; j < i; j++) { - cap = &rkisp1->capture_devs[j]; - rkisp1_unregister_capture(cap); - } - - return ret; } diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.c index cf889666e166..f956b90a407a 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.c @@ -5,10 +5,153 @@ * Copyright (C) 2019 Collabora, Ltd. */ +#include <media/mipi-csi2.h> #include <media/v4l2-rect.h> #include "rkisp1-common.h" +static const struct rkisp1_mbus_info rkisp1_formats[] = { + { + .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, + .pixel_enc = V4L2_PIXEL_ENC_YUV, + .direction = RKISP1_ISP_SD_SRC, + }, { + .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, + .pixel_enc = V4L2_PIXEL_ENC_BAYER, + .mipi_dt = MIPI_CSI2_DT_RAW10, + .bayer_pat = RKISP1_RAW_RGGB, + .bus_width = 10, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, + }, { + .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, + .pixel_enc = V4L2_PIXEL_ENC_BAYER, + .mipi_dt = MIPI_CSI2_DT_RAW10, + .bayer_pat = RKISP1_RAW_BGGR, + .bus_width = 10, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, + }, { + .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, + .pixel_enc = V4L2_PIXEL_ENC_BAYER, + .mipi_dt = MIPI_CSI2_DT_RAW10, + .bayer_pat = RKISP1_RAW_GBRG, + .bus_width = 10, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, + }, { + .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, + .pixel_enc = V4L2_PIXEL_ENC_BAYER, + .mipi_dt = MIPI_CSI2_DT_RAW10, + .bayer_pat = RKISP1_RAW_GRBG, + .bus_width = 10, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, + }, { + .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12, + .pixel_enc = V4L2_PIXEL_ENC_BAYER, + .mipi_dt = MIPI_CSI2_DT_RAW12, + .bayer_pat = RKISP1_RAW_RGGB, + .bus_width = 12, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, + }, { + .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, + .pixel_enc = V4L2_PIXEL_ENC_BAYER, + .mipi_dt = MIPI_CSI2_DT_RAW12, + .bayer_pat = RKISP1_RAW_BGGR, + .bus_width = 12, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, + }, { + .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12, + .pixel_enc = V4L2_PIXEL_ENC_BAYER, + .mipi_dt = MIPI_CSI2_DT_RAW12, + .bayer_pat = RKISP1_RAW_GBRG, + .bus_width = 12, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, + }, { + .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, + .pixel_enc = V4L2_PIXEL_ENC_BAYER, + .mipi_dt = MIPI_CSI2_DT_RAW12, + .bayer_pat = RKISP1_RAW_GRBG, + .bus_width = 12, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, + }, { + .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, + .pixel_enc = V4L2_PIXEL_ENC_BAYER, + .mipi_dt = MIPI_CSI2_DT_RAW8, + .bayer_pat = RKISP1_RAW_RGGB, + .bus_width = 8, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, + }, { + .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, + .pixel_enc = V4L2_PIXEL_ENC_BAYER, + .mipi_dt = MIPI_CSI2_DT_RAW8, + .bayer_pat = RKISP1_RAW_BGGR, + .bus_width = 8, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, + }, { + .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, + .pixel_enc = V4L2_PIXEL_ENC_BAYER, + .mipi_dt = MIPI_CSI2_DT_RAW8, + .bayer_pat = RKISP1_RAW_GBRG, + .bus_width = 8, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, + }, { + .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, + .pixel_enc = V4L2_PIXEL_ENC_BAYER, + .mipi_dt = MIPI_CSI2_DT_RAW8, + .bayer_pat = RKISP1_RAW_GRBG, + .bus_width = 8, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, + }, { + .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16, + .pixel_enc = V4L2_PIXEL_ENC_YUV, + .mipi_dt = MIPI_CSI2_DT_YUV422_8B, + .yuv_seq = RKISP1_CIF_ISP_ACQ_PROP_YCBYCR, + .bus_width = 16, + .direction = RKISP1_ISP_SD_SINK, + }, { + .mbus_code = MEDIA_BUS_FMT_YVYU8_1X16, + .pixel_enc = V4L2_PIXEL_ENC_YUV, + .mipi_dt = MIPI_CSI2_DT_YUV422_8B, + .yuv_seq = RKISP1_CIF_ISP_ACQ_PROP_YCRYCB, + .bus_width = 16, + .direction = RKISP1_ISP_SD_SINK, + }, { + .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16, + .pixel_enc = V4L2_PIXEL_ENC_YUV, + .mipi_dt = MIPI_CSI2_DT_YUV422_8B, + .yuv_seq = RKISP1_CIF_ISP_ACQ_PROP_CBYCRY, + .bus_width = 16, + .direction = RKISP1_ISP_SD_SINK, + }, { + .mbus_code = MEDIA_BUS_FMT_VYUY8_1X16, + .pixel_enc = V4L2_PIXEL_ENC_YUV, + .mipi_dt = MIPI_CSI2_DT_YUV422_8B, + .yuv_seq = RKISP1_CIF_ISP_ACQ_PROP_CRYCBY, + .bus_width = 16, + .direction = RKISP1_ISP_SD_SINK, + }, +}; + +const struct rkisp1_mbus_info *rkisp1_mbus_info_get_by_index(unsigned int index) +{ + if (index >= ARRAY_SIZE(rkisp1_formats)) + return NULL; + + return &rkisp1_formats[index]; +} + +const struct rkisp1_mbus_info *rkisp1_mbus_info_get_by_code(u32 mbus_code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(rkisp1_formats); i++) { + const struct rkisp1_mbus_info *fmt = &rkisp1_formats[i]; + + if (fmt->mbus_code == mbus_code) + return fmt; + } + + return NULL; +} + static const struct v4l2_rect rkisp1_sd_min_crop = { .width = RKISP1_ISP_MIN_WIDTH, .height = RKISP1_ISP_MIN_HEIGHT, diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h index d8fa3f1a5a85..8056997d5c29 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h @@ -23,18 +23,20 @@ #include "rkisp1-regs.h" +struct dentry; + /* - * flags on the 'direction' field in struct 'rkisp1_isp_mbus_info' that indicate + * flags on the 'direction' field in struct rkisp1_mbus_info' that indicate * on which pad the media bus format is supported */ -#define RKISP1_ISP_SD_SRC BIT(0) -#define RKISP1_ISP_SD_SINK BIT(1) +#define RKISP1_ISP_SD_SRC BIT(0) +#define RKISP1_ISP_SD_SINK BIT(1) /* min and max values for the widths and heights of the entities */ -#define RKISP1_ISP_MAX_WIDTH 4032 -#define RKISP1_ISP_MAX_HEIGHT 3024 -#define RKISP1_ISP_MIN_WIDTH 32 -#define RKISP1_ISP_MIN_HEIGHT 32 +#define RKISP1_ISP_MAX_WIDTH 4032 +#define RKISP1_ISP_MAX_HEIGHT 3024 +#define RKISP1_ISP_MIN_WIDTH 32 +#define RKISP1_ISP_MIN_HEIGHT 32 #define RKISP1_RSZ_MP_SRC_MAX_WIDTH 4416 #define RKISP1_RSZ_MP_SRC_MAX_HEIGHT 3312 @@ -44,20 +46,20 @@ #define RKISP1_RSZ_SRC_MIN_HEIGHT 16 /* the default width and height of all the entities */ -#define RKISP1_DEFAULT_WIDTH 800 -#define RKISP1_DEFAULT_HEIGHT 600 +#define RKISP1_DEFAULT_WIDTH 800 +#define RKISP1_DEFAULT_HEIGHT 600 -#define RKISP1_DRIVER_NAME "rkisp1" -#define RKISP1_BUS_INFO "platform:" RKISP1_DRIVER_NAME +#define RKISP1_DRIVER_NAME "rkisp1" +#define RKISP1_BUS_INFO "platform:" RKISP1_DRIVER_NAME /* maximum number of clocks */ -#define RKISP1_MAX_BUS_CLK 8 +#define RKISP1_MAX_BUS_CLK 8 /* a bitmask of the ready stats */ -#define RKISP1_STATS_MEAS_MASK (RKISP1_CIF_ISP_AWB_DONE | \ - RKISP1_CIF_ISP_AFM_FIN | \ - RKISP1_CIF_ISP_EXP_END | \ - RKISP1_CIF_ISP_HIST_MEASURE_RDY) +#define RKISP1_STATS_MEAS_MASK (RKISP1_CIF_ISP_AWB_DONE | \ + RKISP1_CIF_ISP_AFM_FIN | \ + RKISP1_CIF_ISP_EXP_END | \ + RKISP1_CIF_ISP_HIST_MEASURE_RDY) /* enum for the resizer pads */ enum rkisp1_rsz_pad { @@ -66,6 +68,13 @@ enum rkisp1_rsz_pad { RKISP1_RSZ_PAD_MAX }; +/* enum for the csi receiver pads */ +enum rkisp1_csi_pad { + RKISP1_CSI_PAD_SINK, + RKISP1_CSI_PAD_SRC, + RKISP1_CSI_PAD_NUM +}; + /* enum for the capture id */ enum rkisp1_stream_id { RKISP1_MAINPATH, @@ -90,25 +99,89 @@ enum rkisp1_isp_pad { }; /* + * enum rkisp1_feature - ISP features + * + * @RKISP1_FEATURE_MIPI_CSI2: The ISP has an internal MIPI CSI-2 receiver + * + * The ISP features are stored in a bitmask in &rkisp1_info.features and allow + * the driver to implement support for features present in some ISP versions + * only. + */ +enum rkisp1_feature { + RKISP1_FEATURE_MIPI_CSI2 = BIT(0), +}; + +/* + * struct rkisp1_info - Model-specific ISP Information + * + * @clks: array of ISP clock names + * @clk_size: number of entries in the @clks array + * @isrs: array of ISP interrupt descriptors + * @isr_size: number of entries in the @isrs array + * @isp_ver: ISP version + * @features: bitmask of rkisp1_feature features implemented by the ISP + * + * This structure contains information about the ISP specific to a particular + * ISP model, version, or integration in a particular SoC. + */ +struct rkisp1_info { + const char * const *clks; + unsigned int clk_size; + const struct rkisp1_isr_data *isrs; + unsigned int isr_size; + enum rkisp1_cif_isp_version isp_ver; + unsigned int features; +}; + +/* * struct rkisp1_sensor_async - A container for the v4l2_async_subdev to add to the notifier * of the v4l2-async API * * @asd: async_subdev variable for the sensor + * @index: index of the sensor (counting sensor found in DT) + * @source_ep: fwnode for the sensor source endpoint * @lanes: number of lanes * @mbus_type: type of bus (currently only CSI2 is supported) * @mbus_flags: media bus (V4L2_MBUS_*) flags * @sd: a pointer to v4l2_subdev struct of the sensor * @pixel_rate_ctrl: pixel rate of the sensor, used to initialize the phy - * @dphy: a pointer to the phy + * @port: port number (0: MIPI, 1: Parallel) */ struct rkisp1_sensor_async { struct v4l2_async_subdev asd; + unsigned int index; + struct fwnode_handle *source_ep; unsigned int lanes; enum v4l2_mbus_type mbus_type; unsigned int mbus_flags; struct v4l2_subdev *sd; struct v4l2_ctrl *pixel_rate_ctrl; + unsigned int port; +}; + +/* + * struct rkisp1_csi - CSI receiver subdev + * + * @rkisp1: pointer to the rkisp1 device + * @dphy: a pointer to the phy + * @is_dphy_errctrl_disabled: if dphy errctrl is disabled (avoid endless interrupt) + * @sd: v4l2_subdev variable + * @pads: media pads + * @pad_cfg: configurations for the pads + * @sink_fmt: input format + * @lock: protects pad_cfg and sink_fmt + * @source: source in-use, set when starting streaming + */ +struct rkisp1_csi { + struct rkisp1_device *rkisp1; struct phy *dphy; + bool is_dphy_errctrl_disabled; + struct v4l2_subdev sd; + struct media_pad pads[RKISP1_CSI_PAD_NUM]; + struct v4l2_subdev_pad_config pad_cfg[RKISP1_CSI_PAD_NUM]; + const struct rkisp1_mbus_info *sink_fmt; + struct mutex lock; + struct v4l2_subdev *source; }; /* @@ -121,17 +194,16 @@ struct rkisp1_sensor_async { * @sink_fmt: input format * @src_fmt: output format * @ops_lock: ops serialization - * @is_dphy_errctrl_disabled: if dphy errctrl is disabled (avoid endless interrupt) * @frame_sequence: used to synchronize frame_id between video devices. */ struct rkisp1_isp { struct v4l2_subdev sd; + struct rkisp1_device *rkisp1; struct media_pad pads[RKISP1_ISP_PAD_MAX]; struct v4l2_subdev_pad_config pad_cfg[RKISP1_ISP_PAD_MAX]; - const struct rkisp1_isp_mbus_info *sink_fmt; - const struct rkisp1_isp_mbus_info *src_fmt; + const struct rkisp1_mbus_info *sink_fmt; + const struct rkisp1_mbus_info *src_fmt; struct mutex ops_lock; /* serialize the subdevice ops */ - bool is_dphy_errctrl_disabled; __u32 frame_sequence; }; @@ -313,6 +385,7 @@ struct rkisp1_params { * struct rkisp1_resizer - Resizer subdev * * @sd: v4l2_subdev variable + * @regs_base: base register address offset * @id: id of the resizer, one of RKISP1_SELFPATH, RKISP1_MAINPATH * @rkisp1: pointer to the rkisp1 device * @pads: media pads @@ -323,6 +396,7 @@ struct rkisp1_params { */ struct rkisp1_resizer { struct v4l2_subdev sd; + u32 regs_base; enum rkisp1_stream_id id; struct rkisp1_device *rkisp1; struct media_pad pads[RKISP1_RSZ_PAD_MAX]; @@ -373,7 +447,8 @@ struct rkisp1_debug { * @v4l2_dev: v4l2_device variable * @media_dev: media_device variable * @notifier: a notifier to register on the v4l2-async API to be notified on the sensor - * @active_sensor: sensor in-use, set when streaming on + * @source: source subdev in-use, set when starting streaming + * @csi: internal CSI-2 receiver * @isp: ISP sub-device * @resizer_devs: resizer sub-devices * @capture_devs: capture devices @@ -382,6 +457,7 @@ struct rkisp1_debug { * @pipe: media pipeline * @stream_lock: serializes {start/stop}_streaming callbacks between the capture devices. * @debug: debug params to be exposed on debugfs + * @info: version-specific ISP information */ struct rkisp1_device { void __iomem *base_addr; @@ -391,7 +467,8 @@ struct rkisp1_device { struct v4l2_device v4l2_dev; struct media_device media_dev; struct v4l2_async_notifier notifier; - struct rkisp1_sensor_async *active_sensor; + struct v4l2_subdev *source; + struct rkisp1_csi csi; struct rkisp1_isp isp; struct rkisp1_resizer resizer_devs[2]; struct rkisp1_capture capture_devs[2]; @@ -400,11 +477,12 @@ struct rkisp1_device { struct media_pipeline pipe; struct mutex stream_lock; /* serialize {start/stop}_streaming cb between capture devices */ struct rkisp1_debug debug; + const struct rkisp1_info *info; }; /* - * struct rkisp1_isp_mbus_info - ISP media bus info, Translates media bus code to hardware - * format values + * struct rkisp1_mbus_info - ISP media bus info, Translates media bus code to hardware + * format values * * @mbus_code: media bus code * @pixel_enc: pixel encoding @@ -414,7 +492,7 @@ struct rkisp1_device { * @bayer_pat: bayer pattern * @direction: a bitmask of the flags indicating on which pad the format is supported on */ -struct rkisp1_isp_mbus_info { +struct rkisp1_mbus_info { u32 mbus_code; enum v4l2_pixel_encoding pixel_enc; u32 mipi_dt; @@ -425,7 +503,7 @@ struct rkisp1_isp_mbus_info { }; static inline void -rkisp1_write(struct rkisp1_device *rkisp1, u32 val, unsigned int addr) +rkisp1_write(struct rkisp1_device *rkisp1, unsigned int addr, u32 val) { writel(val, rkisp1->base_addr + addr); } @@ -447,6 +525,13 @@ int rkisp1_cap_enum_mbus_codes(struct rkisp1_capture *cap, struct v4l2_subdev_mbus_code_enum *code); /* + * rkisp1_mbus_info_get_by_index - Retrieve the ith supported mbus info + * + * @index: index of the mbus info to fetch + */ +const struct rkisp1_mbus_info *rkisp1_mbus_info_get_by_index(unsigned int index); + +/* * rkisp1_sd_adjust_crop_rect - adjust a rectangle to fit into another rectangle. * * @crop: rectangle to adjust. @@ -465,11 +550,11 @@ void rkisp1_sd_adjust_crop(struct v4l2_rect *crop, const struct v4l2_mbus_framefmt *bounds); /* - * rkisp1_isp_mbus_info - get the isp info of the media bus code + * rkisp1_mbus_info_get_by_code - get the isp info of the media bus code * * @mbus_code: the media bus code */ -const struct rkisp1_isp_mbus_info *rkisp1_isp_mbus_info_get(u32 mbus_code); +const struct rkisp1_mbus_info *rkisp1_mbus_info_get_by_code(u32 mbus_code); /* rkisp1_params_configure - configure the params when stream starts. * This function is called by the isp entity upon stream starts. @@ -493,7 +578,7 @@ void rkisp1_params_disable(struct rkisp1_params *params); /* irq handlers */ irqreturn_t rkisp1_isp_isr(int irq, void *ctx); -irqreturn_t rkisp1_mipi_isr(int irq, void *ctx); +irqreturn_t rkisp1_csi_isr(int irq, void *ctx); irqreturn_t rkisp1_capture_isr(int irq, void *ctx); void rkisp1_stats_isr(struct rkisp1_stats *stats, u32 isp_ris); void rkisp1_params_isr(struct rkisp1_device *rkisp1); @@ -514,4 +599,16 @@ void rkisp1_stats_unregister(struct rkisp1_device *rkisp1); int rkisp1_params_register(struct rkisp1_device *rkisp1); void rkisp1_params_unregister(struct rkisp1_device *rkisp1); +#if IS_ENABLED(CONFIG_DEBUG_FS) +void rkisp1_debug_init(struct rkisp1_device *rkisp1); +void rkisp1_debug_cleanup(struct rkisp1_device *rkisp1); +#else +static inline void rkisp1_debug_init(struct rkisp1_device *rkisp1) +{ +} +static inline void rkisp1_debug_cleanup(struct rkisp1_device *rkisp1) +{ +} +#endif + #endif /* _RKISP1_COMMON_H */ diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c new file mode 100644 index 000000000000..d7acc94e10f8 --- /dev/null +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c @@ -0,0 +1,536 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Rockchip ISP1 Driver - CSI-2 Receiver + * + * Copyright (C) 2019 Collabora, Ltd. + * Copyright (C) 2022 Ideas on Board + * + * Based on Rockchip ISP1 driver by Rockchip Electronics Co., Ltd. + * Copyright (C) 2017 Rockchip Electronics Co., Ltd. + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/lockdep.h> +#include <linux/phy/phy.h> +#include <linux/phy/phy-mipi-dphy.h> + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-fwnode.h> + +#include "rkisp1-common.h" +#include "rkisp1-csi.h" + +#define RKISP1_CSI_DEV_NAME RKISP1_DRIVER_NAME "_csi" + +#define RKISP1_CSI_DEF_FMT MEDIA_BUS_FMT_SRGGB10_1X10 + +static inline struct rkisp1_csi *to_rkisp1_csi(struct v4l2_subdev *sd) +{ + return container_of(sd, struct rkisp1_csi, sd); +} + +static struct v4l2_mbus_framefmt * +rkisp1_csi_get_pad_fmt(struct rkisp1_csi *csi, + struct v4l2_subdev_state *sd_state, + unsigned int pad, u32 which) +{ + struct v4l2_subdev_state state = { + .pads = csi->pad_cfg + }; + + lockdep_assert_held(&csi->lock); + + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(&csi->sd, sd_state, pad); + else + return v4l2_subdev_get_try_format(&csi->sd, &state, pad); +} + +int rkisp1_csi_link_sensor(struct rkisp1_device *rkisp1, struct v4l2_subdev *sd, + struct rkisp1_sensor_async *s_asd, + unsigned int source_pad) +{ + struct rkisp1_csi *csi = &rkisp1->csi; + int ret; + + s_asd->pixel_rate_ctrl = v4l2_ctrl_find(sd->ctrl_handler, + V4L2_CID_PIXEL_RATE); + if (!s_asd->pixel_rate_ctrl) { + dev_err(rkisp1->dev, "No pixel rate control in subdev %s\n", + sd->name); + return -EINVAL; + } + + /* Create the link from the sensor to the CSI receiver. */ + ret = media_create_pad_link(&sd->entity, source_pad, + &csi->sd.entity, RKISP1_CSI_PAD_SINK, + !s_asd->index ? MEDIA_LNK_FL_ENABLED : 0); + if (ret) { + dev_err(csi->rkisp1->dev, "failed to link src pad of %s\n", + sd->name); + return ret; + } + + return 0; +} + +static int rkisp1_csi_config(struct rkisp1_csi *csi, + const struct rkisp1_sensor_async *sensor) +{ + struct rkisp1_device *rkisp1 = csi->rkisp1; + unsigned int lanes = sensor->lanes; + u32 mipi_ctrl; + + if (lanes < 1 || lanes > 4) + return -EINVAL; + + mipi_ctrl = RKISP1_CIF_MIPI_CTRL_NUM_LANES(lanes - 1) | + RKISP1_CIF_MIPI_CTRL_SHUTDOWNLANES(0xf) | + RKISP1_CIF_MIPI_CTRL_ERR_SOT_SYNC_HS_SKIP | + RKISP1_CIF_MIPI_CTRL_CLOCKLANE_ENA; + + rkisp1_write(rkisp1, RKISP1_CIF_MIPI_CTRL, mipi_ctrl); + + /* V12 could also use a newer csi2-host, but we don't want that yet */ + if (rkisp1->info->isp_ver == RKISP1_V12) + rkisp1_write(rkisp1, RKISP1_CIF_ISP_CSI0_CTRL0, 0); + + /* Configure Data Type and Virtual Channel */ + rkisp1_write(rkisp1, RKISP1_CIF_MIPI_IMG_DATA_SEL, + RKISP1_CIF_MIPI_DATA_SEL_DT(csi->sink_fmt->mipi_dt) | + RKISP1_CIF_MIPI_DATA_SEL_VC(0)); + + /* Clear MIPI interrupts */ + rkisp1_write(rkisp1, RKISP1_CIF_MIPI_ICR, ~0); + + /* + * Disable RKISP1_CIF_MIPI_ERR_DPHY interrupt here temporary for + * isp bus may be dead when switch isp. + */ + rkisp1_write(rkisp1, RKISP1_CIF_MIPI_IMSC, + RKISP1_CIF_MIPI_FRAME_END | RKISP1_CIF_MIPI_ERR_CSI | + RKISP1_CIF_MIPI_ERR_DPHY | + RKISP1_CIF_MIPI_SYNC_FIFO_OVFLW(0x03) | + RKISP1_CIF_MIPI_ADD_DATA_OVFLW); + + dev_dbg(rkisp1->dev, "\n MIPI_CTRL 0x%08x\n" + " MIPI_IMG_DATA_SEL 0x%08x\n" + " MIPI_STATUS 0x%08x\n" + " MIPI_IMSC 0x%08x\n", + rkisp1_read(rkisp1, RKISP1_CIF_MIPI_CTRL), + rkisp1_read(rkisp1, RKISP1_CIF_MIPI_IMG_DATA_SEL), + rkisp1_read(rkisp1, RKISP1_CIF_MIPI_STATUS), + rkisp1_read(rkisp1, RKISP1_CIF_MIPI_IMSC)); + + return 0; +} + +static void rkisp1_csi_enable(struct rkisp1_csi *csi) +{ + struct rkisp1_device *rkisp1 = csi->rkisp1; + u32 val; + + val = rkisp1_read(rkisp1, RKISP1_CIF_MIPI_CTRL); + rkisp1_write(rkisp1, RKISP1_CIF_MIPI_CTRL, + val | RKISP1_CIF_MIPI_CTRL_OUTPUT_ENA); +} + +static void rkisp1_csi_disable(struct rkisp1_csi *csi) +{ + struct rkisp1_device *rkisp1 = csi->rkisp1; + u32 val; + + /* Mask and clear interrupts. */ + rkisp1_write(rkisp1, RKISP1_CIF_MIPI_IMSC, 0); + rkisp1_write(rkisp1, RKISP1_CIF_MIPI_ICR, ~0); + + val = rkisp1_read(rkisp1, RKISP1_CIF_MIPI_CTRL); + rkisp1_write(rkisp1, RKISP1_CIF_MIPI_CTRL, + val & (~RKISP1_CIF_MIPI_CTRL_OUTPUT_ENA)); +} + +static int rkisp1_csi_start(struct rkisp1_csi *csi, + const struct rkisp1_sensor_async *sensor) +{ + struct rkisp1_device *rkisp1 = csi->rkisp1; + union phy_configure_opts opts; + struct phy_configure_opts_mipi_dphy *cfg = &opts.mipi_dphy; + s64 pixel_clock; + int ret; + + ret = rkisp1_csi_config(csi, sensor); + if (ret) + return ret; + + pixel_clock = v4l2_ctrl_g_ctrl_int64(sensor->pixel_rate_ctrl); + if (!pixel_clock) { + dev_err(rkisp1->dev, "Invalid pixel rate value\n"); + return -EINVAL; + } + + phy_mipi_dphy_get_default_config(pixel_clock, csi->sink_fmt->bus_width, + sensor->lanes, cfg); + phy_set_mode(csi->dphy, PHY_MODE_MIPI_DPHY); + phy_configure(csi->dphy, &opts); + phy_power_on(csi->dphy); + + rkisp1_csi_enable(csi); + + /* + * CIF spec says to wait for sufficient time after enabling + * the MIPI interface and before starting the sensor output. + */ + usleep_range(1000, 1200); + + return 0; +} + +static void rkisp1_csi_stop(struct rkisp1_csi *csi) +{ + rkisp1_csi_disable(csi); + + phy_power_off(csi->dphy); +} + +irqreturn_t rkisp1_csi_isr(int irq, void *ctx) +{ + struct device *dev = ctx; + struct rkisp1_device *rkisp1 = dev_get_drvdata(dev); + u32 val, status; + + status = rkisp1_read(rkisp1, RKISP1_CIF_MIPI_MIS); + if (!status) + return IRQ_NONE; + + rkisp1_write(rkisp1, RKISP1_CIF_MIPI_ICR, status); + + /* + * Disable DPHY errctrl interrupt, because this dphy + * erctrl signal is asserted until the next changes + * of line state. This time is may be too long and cpu + * is hold in this interrupt. + */ + if (status & RKISP1_CIF_MIPI_ERR_CTRL(0x0f)) { + val = rkisp1_read(rkisp1, RKISP1_CIF_MIPI_IMSC); + rkisp1_write(rkisp1, RKISP1_CIF_MIPI_IMSC, + val & ~RKISP1_CIF_MIPI_ERR_CTRL(0x0f)); + rkisp1->csi.is_dphy_errctrl_disabled = true; + } + + /* + * Enable DPHY errctrl interrupt again, if mipi have receive + * the whole frame without any error. + */ + if (status == RKISP1_CIF_MIPI_FRAME_END) { + /* + * Enable DPHY errctrl interrupt again, if mipi have receive + * the whole frame without any error. + */ + if (rkisp1->csi.is_dphy_errctrl_disabled) { + val = rkisp1_read(rkisp1, RKISP1_CIF_MIPI_IMSC); + val |= RKISP1_CIF_MIPI_ERR_CTRL(0x0f); + rkisp1_write(rkisp1, RKISP1_CIF_MIPI_IMSC, val); + rkisp1->csi.is_dphy_errctrl_disabled = false; + } + } else { + rkisp1->debug.mipi_error++; + } + + return IRQ_HANDLED; +} + +/* ---------------------------------------------------------------------------- + * Subdev pad operations + */ + +static int rkisp1_csi_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct rkisp1_csi *csi = to_rkisp1_csi(sd); + unsigned int i; + int pos = 0; + + if (code->pad == RKISP1_CSI_PAD_SRC) { + const struct v4l2_mbus_framefmt *sink_fmt; + + if (code->index) + return -EINVAL; + + mutex_lock(&csi->lock); + + sink_fmt = rkisp1_csi_get_pad_fmt(csi, sd_state, + RKISP1_CSI_PAD_SINK, + code->which); + code->code = sink_fmt->code; + + mutex_unlock(&csi->lock); + + return 0; + } + + for (i = 0; ; i++) { + const struct rkisp1_mbus_info *fmt = + rkisp1_mbus_info_get_by_index(i); + + if (!fmt) + return -EINVAL; + + if (!(fmt->direction & RKISP1_ISP_SD_SINK)) + continue; + + if (code->index == pos) { + code->code = fmt->mbus_code; + return 0; + } + + pos++; + } + + return -EINVAL; +} + +static int rkisp1_csi_init_config(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; + + sink_fmt = v4l2_subdev_get_try_format(sd, sd_state, + RKISP1_CSI_PAD_SINK); + src_fmt = v4l2_subdev_get_try_format(sd, sd_state, + RKISP1_CSI_PAD_SRC); + + sink_fmt->width = RKISP1_DEFAULT_WIDTH; + sink_fmt->height = RKISP1_DEFAULT_HEIGHT; + sink_fmt->field = V4L2_FIELD_NONE; + sink_fmt->code = RKISP1_CSI_DEF_FMT; + + *src_fmt = *sink_fmt; + + return 0; +} + +static int rkisp1_csi_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct rkisp1_csi *csi = to_rkisp1_csi(sd); + + mutex_lock(&csi->lock); + fmt->format = *rkisp1_csi_get_pad_fmt(csi, sd_state, fmt->pad, + fmt->which); + mutex_unlock(&csi->lock); + + return 0; +} + +static int rkisp1_csi_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct rkisp1_csi *csi = to_rkisp1_csi(sd); + const struct rkisp1_mbus_info *mbus_info; + struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; + + /* The format on the source pad always matches the sink pad. */ + if (fmt->pad == RKISP1_CSI_PAD_SRC) + return rkisp1_csi_get_fmt(sd, sd_state, fmt); + + mutex_lock(&csi->lock); + + sink_fmt = rkisp1_csi_get_pad_fmt(csi, sd_state, RKISP1_CSI_PAD_SINK, + fmt->which); + + sink_fmt->code = fmt->format.code; + + mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code); + if (!mbus_info || !(mbus_info->direction & RKISP1_ISP_SD_SINK)) { + sink_fmt->code = RKISP1_CSI_DEF_FMT; + mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code); + } + + sink_fmt->width = clamp_t(u32, fmt->format.width, + RKISP1_ISP_MIN_WIDTH, + RKISP1_ISP_MAX_WIDTH); + sink_fmt->height = clamp_t(u32, fmt->format.height, + RKISP1_ISP_MIN_HEIGHT, + RKISP1_ISP_MAX_HEIGHT); + + fmt->format = *sink_fmt; + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + csi->sink_fmt = mbus_info; + + /* Propagate the format to the source pad. */ + src_fmt = rkisp1_csi_get_pad_fmt(csi, sd_state, RKISP1_CSI_PAD_SRC, + fmt->which); + *src_fmt = *sink_fmt; + + mutex_unlock(&csi->lock); + + return 0; +} + +/* ---------------------------------------------------------------------------- + * Subdev video operations + */ + +static int rkisp1_csi_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct rkisp1_csi *csi = to_rkisp1_csi(sd); + struct rkisp1_device *rkisp1 = csi->rkisp1; + struct rkisp1_sensor_async *source_asd; + struct media_pad *source_pad; + struct v4l2_subdev *source; + int ret; + + if (!enable) { + v4l2_subdev_call(csi->source, video, s_stream, false); + + rkisp1_csi_stop(csi); + + return 0; + } + + source_pad = media_entity_remote_source_pad_unique(&sd->entity); + if (IS_ERR(source_pad)) { + dev_dbg(rkisp1->dev, "Failed to get source for CSI: %ld\n", + PTR_ERR(source_pad)); + return -EPIPE; + } + + source = media_entity_to_v4l2_subdev(source_pad->entity); + if (!source) { + /* This should really not happen, so is not worth a message. */ + return -EPIPE; + } + + source_asd = container_of(source->asd, struct rkisp1_sensor_async, asd); + if (source_asd->mbus_type != V4L2_MBUS_CSI2_DPHY) + return -EINVAL; + + mutex_lock(&csi->lock); + ret = rkisp1_csi_start(csi, source_asd); + mutex_unlock(&csi->lock); + if (ret) + return ret; + + ret = v4l2_subdev_call(source, video, s_stream, true); + if (ret) { + rkisp1_csi_stop(csi); + return ret; + } + + csi->source = source; + + return 0; +} + +/* ---------------------------------------------------------------------------- + * Registration + */ + +static const struct media_entity_operations rkisp1_csi_media_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_video_ops rkisp1_csi_video_ops = { + .s_stream = rkisp1_csi_s_stream, +}; + +static const struct v4l2_subdev_pad_ops rkisp1_csi_pad_ops = { + .enum_mbus_code = rkisp1_csi_enum_mbus_code, + .init_cfg = rkisp1_csi_init_config, + .get_fmt = rkisp1_csi_get_fmt, + .set_fmt = rkisp1_csi_set_fmt, +}; + +static const struct v4l2_subdev_ops rkisp1_csi_ops = { + .video = &rkisp1_csi_video_ops, + .pad = &rkisp1_csi_pad_ops, +}; + +int rkisp1_csi_register(struct rkisp1_device *rkisp1) +{ + struct rkisp1_csi *csi = &rkisp1->csi; + struct v4l2_subdev_state state = {}; + struct media_pad *pads; + struct v4l2_subdev *sd; + int ret; + + csi->rkisp1 = rkisp1; + mutex_init(&csi->lock); + + sd = &csi->sd; + v4l2_subdev_init(sd, &rkisp1_csi_ops); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->entity.ops = &rkisp1_csi_media_ops; + sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + sd->owner = THIS_MODULE; + strscpy(sd->name, RKISP1_CSI_DEV_NAME, sizeof(sd->name)); + + pads = csi->pads; + pads[RKISP1_CSI_PAD_SINK].flags = MEDIA_PAD_FL_SINK | + MEDIA_PAD_FL_MUST_CONNECT; + pads[RKISP1_CSI_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE | + MEDIA_PAD_FL_MUST_CONNECT; + + csi->sink_fmt = rkisp1_mbus_info_get_by_code(RKISP1_CSI_DEF_FMT); + + ret = media_entity_pads_init(&sd->entity, RKISP1_CSI_PAD_NUM, pads); + if (ret) + goto error; + + state.pads = csi->pad_cfg; + rkisp1_csi_init_config(sd, &state); + + ret = v4l2_device_register_subdev(&csi->rkisp1->v4l2_dev, sd); + if (ret) { + dev_err(sd->dev, "Failed to register csi receiver subdev\n"); + goto error; + } + + return 0; + +error: + media_entity_cleanup(&sd->entity); + mutex_destroy(&csi->lock); + csi->rkisp1 = NULL; + return ret; +} + +void rkisp1_csi_unregister(struct rkisp1_device *rkisp1) +{ + struct rkisp1_csi *csi = &rkisp1->csi; + + if (!csi->rkisp1) + return; + + v4l2_device_unregister_subdev(&csi->sd); + media_entity_cleanup(&csi->sd.entity); + mutex_destroy(&csi->lock); +} + +int rkisp1_csi_init(struct rkisp1_device *rkisp1) +{ + struct rkisp1_csi *csi = &rkisp1->csi; + + csi->rkisp1 = rkisp1; + + csi->dphy = devm_phy_get(rkisp1->dev, "dphy"); + if (IS_ERR(csi->dphy)) + return dev_err_probe(rkisp1->dev, PTR_ERR(csi->dphy), + "Couldn't get the MIPI D-PHY\n"); + + phy_init(csi->dphy); + + return 0; +} + +void rkisp1_csi_cleanup(struct rkisp1_device *rkisp1) +{ + struct rkisp1_csi *csi = &rkisp1->csi; + + phy_exit(csi->dphy); +} diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.h new file mode 100644 index 000000000000..1f5f2af31a7d --- /dev/null +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ +/* + * Rockchip ISP1 Driver - CSI-2 Receiver + * + * Copyright (C) 2019 Collabora, Ltd. + * Copyright (C) 2022 Ideas on Board + * + * Based on Rockchip ISP1 driver by Rockchip Electronics Co., Ltd. + * Copyright (C) 2017 Rockchip Electronics Co., Ltd. + */ +#ifndef _RKISP1_CSI_H +#define _RKISP1_CSI_H + +struct rkisp1_csi; +struct rkisp1_device; +struct rkisp1_sensor_async; + +int rkisp1_csi_init(struct rkisp1_device *rkisp1); +void rkisp1_csi_cleanup(struct rkisp1_device *rkisp1); + +int rkisp1_csi_register(struct rkisp1_device *rkisp1); +void rkisp1_csi_unregister(struct rkisp1_device *rkisp1); + +int rkisp1_csi_link_sensor(struct rkisp1_device *rkisp1, struct v4l2_subdev *sd, + struct rkisp1_sensor_async *s_asd, + unsigned int source_pad); + +#endif /* _RKISP1_CSI_H */ diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-debug.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-debug.c new file mode 100644 index 000000000000..71df3dc95e6f --- /dev/null +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-debug.c @@ -0,0 +1,243 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Rockchip ISP1 Driver - Base driver + * + * Copyright (C) 2019 Collabora, Ltd. + * + * Based on Rockchip ISP1 driver by Rockchip Electronics Co., Ltd. + * Copyright (C) 2017 Rockchip Electronics Co., Ltd. + */ + +#include <linux/debugfs.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/minmax.h> +#include <linux/pm_runtime.h> +#include <linux/seq_file.h> +#include <linux/string.h> + +#include "rkisp1-common.h" +#include "rkisp1-regs.h" + +struct rkisp1_debug_register { + u32 reg; + u32 shd; + const char * const name; +}; + +#define RKISP1_DEBUG_REG(name) { RKISP1_CIF_##name, 0, #name } +#define RKISP1_DEBUG_SHD_REG(name) { \ + RKISP1_CIF_##name, RKISP1_CIF_##name##_SHD, #name \ +} + +/* Keep this up-to-date when adding new registers. */ +#define RKISP1_MAX_REG_LENGTH 21 + +static int rkisp1_debug_dump_regs(struct rkisp1_device *rkisp1, + struct seq_file *m, unsigned int offset, + const struct rkisp1_debug_register *regs) +{ + const int width = RKISP1_MAX_REG_LENGTH; + u32 val, shd; + int ret; + + ret = pm_runtime_get_if_in_use(rkisp1->dev); + if (ret <= 0) + return ret ? : -ENODATA; + + for (; regs->name; ++regs) { + val = rkisp1_read(rkisp1, offset + regs->reg); + + if (regs->shd) { + shd = rkisp1_read(rkisp1, offset + regs->shd); + seq_printf(m, "%*s: 0x%08x/0x%08x\n", width, regs->name, + val, shd); + } else { + seq_printf(m, "%*s: 0x%08x\n", width, regs->name, val); + } + } + + pm_runtime_put(rkisp1->dev); + + return 0; +} + +static int rkisp1_debug_dump_core_regs_show(struct seq_file *m, void *p) +{ + static const struct rkisp1_debug_register registers[] = { + RKISP1_DEBUG_REG(VI_CCL), + RKISP1_DEBUG_REG(VI_ICCL), + RKISP1_DEBUG_REG(VI_IRCL), + RKISP1_DEBUG_REG(VI_DPCL), + RKISP1_DEBUG_REG(MI_CTRL), + RKISP1_DEBUG_REG(MI_BYTE_CNT), + RKISP1_DEBUG_REG(MI_CTRL_SHD), + RKISP1_DEBUG_REG(MI_RIS), + RKISP1_DEBUG_REG(MI_STATUS), + RKISP1_DEBUG_REG(MI_DMA_CTRL), + RKISP1_DEBUG_REG(MI_DMA_STATUS), + { /* Sentinel */ }, + }; + struct rkisp1_device *rkisp1 = m->private; + + return rkisp1_debug_dump_regs(rkisp1, m, 0, registers); +} +DEFINE_SHOW_ATTRIBUTE(rkisp1_debug_dump_core_regs); + +static int rkisp1_debug_dump_isp_regs_show(struct seq_file *m, void *p) +{ + static const struct rkisp1_debug_register registers[] = { + RKISP1_DEBUG_REG(ISP_CTRL), + RKISP1_DEBUG_REG(ISP_ACQ_PROP), + RKISP1_DEBUG_REG(ISP_FLAGS_SHD), + RKISP1_DEBUG_REG(ISP_RIS), + RKISP1_DEBUG_REG(ISP_ERR), + { /* Sentinel */ }, + }; + struct rkisp1_device *rkisp1 = m->private; + + return rkisp1_debug_dump_regs(rkisp1, m, 0, registers); +} +DEFINE_SHOW_ATTRIBUTE(rkisp1_debug_dump_isp_regs); + +static int rkisp1_debug_dump_rsz_regs_show(struct seq_file *m, void *p) +{ + static const struct rkisp1_debug_register registers[] = { + RKISP1_DEBUG_SHD_REG(RSZ_CTRL), + RKISP1_DEBUG_SHD_REG(RSZ_SCALE_HY), + RKISP1_DEBUG_SHD_REG(RSZ_SCALE_HCB), + RKISP1_DEBUG_SHD_REG(RSZ_SCALE_HCR), + RKISP1_DEBUG_SHD_REG(RSZ_SCALE_VY), + RKISP1_DEBUG_SHD_REG(RSZ_SCALE_VC), + RKISP1_DEBUG_SHD_REG(RSZ_PHASE_HY), + RKISP1_DEBUG_SHD_REG(RSZ_PHASE_HC), + RKISP1_DEBUG_SHD_REG(RSZ_PHASE_VY), + RKISP1_DEBUG_SHD_REG(RSZ_PHASE_VC), + { /* Sentinel */ }, + }; + struct rkisp1_resizer *rsz = m->private; + + return rkisp1_debug_dump_regs(rsz->rkisp1, m, rsz->regs_base, registers); +} +DEFINE_SHOW_ATTRIBUTE(rkisp1_debug_dump_rsz_regs); + +static int rkisp1_debug_dump_mi_mp_show(struct seq_file *m, void *p) +{ + static const struct rkisp1_debug_register registers[] = { + RKISP1_DEBUG_REG(MI_MP_Y_BASE_AD_INIT), + RKISP1_DEBUG_REG(MI_MP_Y_BASE_AD_INIT2), + RKISP1_DEBUG_REG(MI_MP_Y_BASE_AD_SHD), + RKISP1_DEBUG_REG(MI_MP_Y_SIZE_INIT), + RKISP1_DEBUG_REG(MI_MP_Y_SIZE_INIT), + RKISP1_DEBUG_REG(MI_MP_Y_SIZE_SHD), + RKISP1_DEBUG_REG(MI_MP_Y_OFFS_CNT_SHD), + { /* Sentinel */ }, + }; + struct rkisp1_device *rkisp1 = m->private; + + return rkisp1_debug_dump_regs(rkisp1, m, 0, registers); +} +DEFINE_SHOW_ATTRIBUTE(rkisp1_debug_dump_mi_mp); + +#define RKISP1_DEBUG_DATA_COUNT_BINS 32 +#define RKISP1_DEBUG_DATA_COUNT_STEP (4096 / RKISP1_DEBUG_DATA_COUNT_BINS) + +static int rkisp1_debug_input_status_show(struct seq_file *m, void *p) +{ + struct rkisp1_device *rkisp1 = m->private; + u16 data_count[RKISP1_DEBUG_DATA_COUNT_BINS] = { }; + unsigned int hsync_count = 0; + unsigned int vsync_count = 0; + unsigned int i; + u32 data; + u32 val; + int ret; + + ret = pm_runtime_get_if_in_use(rkisp1->dev); + if (ret <= 0) + return ret ? : -ENODATA; + + /* Sample the ISP input port status 10000 times with a 1µs interval. */ + for (i = 0; i < 10000; ++i) { + val = rkisp1_read(rkisp1, RKISP1_CIF_ISP_FLAGS_SHD); + + data = (val & RKISP1_CIF_ISP_FLAGS_SHD_S_DATA_MASK) + >> RKISP1_CIF_ISP_FLAGS_SHD_S_DATA_SHIFT; + data_count[data / RKISP1_DEBUG_DATA_COUNT_STEP]++; + + if (val & RKISP1_CIF_ISP_FLAGS_SHD_S_HSYNC) + hsync_count++; + if (val & RKISP1_CIF_ISP_FLAGS_SHD_S_VSYNC) + vsync_count++; + + udelay(1); + } + + pm_runtime_put(rkisp1->dev); + + seq_printf(m, "vsync: %u, hsync: %u\n", vsync_count, hsync_count); + seq_puts(m, "data:\n"); + for (i = 0; i < ARRAY_SIZE(data_count); ++i) + seq_printf(m, "- [%04u:%04u]: %u\n", + i * RKISP1_DEBUG_DATA_COUNT_STEP, + (i + 1) * RKISP1_DEBUG_DATA_COUNT_STEP - 1, + data_count[i]); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(rkisp1_debug_input_status); + +void rkisp1_debug_init(struct rkisp1_device *rkisp1) +{ + struct rkisp1_debug *debug = &rkisp1->debug; + struct dentry *regs_dir; + + debug->debugfs_dir = debugfs_create_dir(dev_name(rkisp1->dev), NULL); + + debugfs_create_ulong("data_loss", 0444, debug->debugfs_dir, + &debug->data_loss); + debugfs_create_ulong("outform_size_err", 0444, debug->debugfs_dir, + &debug->outform_size_error); + debugfs_create_ulong("img_stabilization_size_error", 0444, + debug->debugfs_dir, + &debug->img_stabilization_size_error); + debugfs_create_ulong("inform_size_error", 0444, debug->debugfs_dir, + &debug->inform_size_error); + debugfs_create_ulong("irq_delay", 0444, debug->debugfs_dir, + &debug->irq_delay); + debugfs_create_ulong("mipi_error", 0444, debug->debugfs_dir, + &debug->mipi_error); + debugfs_create_ulong("stats_error", 0444, debug->debugfs_dir, + &debug->stats_error); + debugfs_create_ulong("mp_stop_timeout", 0444, debug->debugfs_dir, + &debug->stop_timeout[RKISP1_MAINPATH]); + debugfs_create_ulong("sp_stop_timeout", 0444, debug->debugfs_dir, + &debug->stop_timeout[RKISP1_SELFPATH]); + debugfs_create_ulong("mp_frame_drop", 0444, debug->debugfs_dir, + &debug->frame_drop[RKISP1_MAINPATH]); + debugfs_create_ulong("sp_frame_drop", 0444, debug->debugfs_dir, + &debug->frame_drop[RKISP1_SELFPATH]); + debugfs_create_file("input_status", 0444, debug->debugfs_dir, rkisp1, + &rkisp1_debug_input_status_fops); + + regs_dir = debugfs_create_dir("regs", debug->debugfs_dir); + + debugfs_create_file("core", 0444, regs_dir, rkisp1, + &rkisp1_debug_dump_core_regs_fops); + debugfs_create_file("isp", 0444, regs_dir, rkisp1, + &rkisp1_debug_dump_isp_regs_fops); + debugfs_create_file("mrsz", 0444, regs_dir, + &rkisp1->resizer_devs[RKISP1_MAINPATH], + &rkisp1_debug_dump_rsz_regs_fops); + debugfs_create_file("srsz", 0444, regs_dir, + &rkisp1->resizer_devs[RKISP1_SELFPATH], + &rkisp1_debug_dump_rsz_regs_fops); + + debugfs_create_file("mi_mp", 0444, regs_dir, rkisp1, + &rkisp1_debug_dump_mi_mp_fops); +} + +void rkisp1_debug_cleanup(struct rkisp1_device *rkisp1) +{ + debugfs_remove_recursive(rkisp1->debug.debugfs_dir); +} diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c index 3f5cfa7eb937..f2475c6235ea 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c @@ -9,18 +9,18 @@ */ #include <linux/clk.h> -#include <linux/debugfs.h> #include <linux/interrupt.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_graph.h> #include <linux/of_platform.h> #include <linux/pinctrl/consumer.h> -#include <linux/phy/phy.h> -#include <linux/phy/phy-mipi-dphy.h> +#include <linux/pm_runtime.h> #include <media/v4l2-fwnode.h> +#include <media/v4l2-mc.h> #include "rkisp1-common.h" +#include "rkisp1-csi.h" /* * ISP Details @@ -68,18 +68,28 @@ * * Media Topology * -------------- - * +----------+ +----------+ - * | Sensor 2 | | Sensor X | - * ------------ ... ------------ - * | 0 | | 0 | - * +----------+ +----------+ +-----------+ - * \ | | params | - * \ | | (output) | - * +----------+ \ | +-----------+ - * | Sensor 1 | v v | - * ------------ +------+------+ | - * | 0 |----->| 0 | 1 |<---------+ - * +----------+ |------+------| + * + * +----------+ +----------+ + * | Sensor 1 | | Sensor X | + * ------------ ... ------------ + * | 0 | | 0 | + * +----------+ +----------+ + * | | + * \----\ /----/ + * | | + * v v + * +-------------+ + * | 0 | + * --------------- + * | CSI-2 RX | + * --------------- +-----------+ + * | 1 | | params | + * +-------------+ | (output) | + * | +-----------+ + * v | + * +------+------+ | + * | 0 | 1 |<---------+ + * |------+------| * | ISP | * |------+------| * +-------------| 2 | 3 |----------+ @@ -106,88 +116,10 @@ struct rkisp1_isr_data { irqreturn_t (*isr)(int irq, void *ctx); }; -struct rkisp1_match_data { - const char * const *clks; - unsigned int clk_size; - const struct rkisp1_isr_data *isrs; - unsigned int isr_size; - enum rkisp1_cif_isp_version isp_ver; -}; - /* ---------------------------------------------------------------------------- * Sensor DT bindings */ -static int rkisp1_create_links(struct rkisp1_device *rkisp1) -{ - struct media_entity *source, *sink; - unsigned int flags, source_pad; - struct v4l2_subdev *sd; - unsigned int i; - int ret; - - /* sensor links */ - flags = MEDIA_LNK_FL_ENABLED; - list_for_each_entry(sd, &rkisp1->v4l2_dev.subdevs, list) { - if (sd == &rkisp1->isp.sd || - sd == &rkisp1->resizer_devs[RKISP1_MAINPATH].sd || - sd == &rkisp1->resizer_devs[RKISP1_SELFPATH].sd) - continue; - - ret = media_entity_get_fwnode_pad(&sd->entity, sd->fwnode, - MEDIA_PAD_FL_SOURCE); - if (ret < 0) { - dev_err(rkisp1->dev, "failed to find src pad for %s\n", - sd->name); - return ret; - } - source_pad = ret; - - ret = media_create_pad_link(&sd->entity, source_pad, - &rkisp1->isp.sd.entity, - RKISP1_ISP_PAD_SINK_VIDEO, - flags); - if (ret) - return ret; - - flags = 0; - } - - flags = MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE; - - /* create ISP->RSZ->CAP links */ - for (i = 0; i < 2; i++) { - source = &rkisp1->isp.sd.entity; - sink = &rkisp1->resizer_devs[i].sd.entity; - ret = media_create_pad_link(source, RKISP1_ISP_PAD_SOURCE_VIDEO, - sink, RKISP1_RSZ_PAD_SINK, - MEDIA_LNK_FL_ENABLED); - if (ret) - return ret; - - source = sink; - sink = &rkisp1->capture_devs[i].vnode.vdev.entity; - ret = media_create_pad_link(source, RKISP1_RSZ_PAD_SRC, - sink, 0, flags); - if (ret) - return ret; - } - - /* params links */ - source = &rkisp1->params.vnode.vdev.entity; - sink = &rkisp1->isp.sd.entity; - ret = media_create_pad_link(source, 0, sink, - RKISP1_ISP_PAD_SINK_PARAMS, flags); - if (ret) - return ret; - - /* 3A stats links */ - source = &rkisp1->isp.sd.entity; - sink = &rkisp1->stats.vnode.vdev.entity; - return media_create_pad_link(source, RKISP1_ISP_PAD_SOURCE_STATS, - sink, 0, flags); -} - static int rkisp1_subdev_notifier_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) @@ -196,116 +128,171 @@ static int rkisp1_subdev_notifier_bound(struct v4l2_async_notifier *notifier, container_of(notifier, struct rkisp1_device, notifier); struct rkisp1_sensor_async *s_asd = container_of(asd, struct rkisp1_sensor_async, asd); + int source_pad; + int ret; - s_asd->pixel_rate_ctrl = v4l2_ctrl_find(sd->ctrl_handler, - V4L2_CID_PIXEL_RATE); s_asd->sd = sd; - s_asd->dphy = devm_phy_get(rkisp1->dev, "dphy"); - if (IS_ERR(s_asd->dphy)) { - if (PTR_ERR(s_asd->dphy) != -EPROBE_DEFER) - dev_err(rkisp1->dev, "Couldn't get the MIPI D-PHY\n"); - return PTR_ERR(s_asd->dphy); + + source_pad = media_entity_get_fwnode_pad(&sd->entity, s_asd->source_ep, + MEDIA_PAD_FL_SOURCE); + if (source_pad < 0) { + dev_err(rkisp1->dev, "failed to find source pad for %s\n", + sd->name); + return source_pad; } - phy_init(s_asd->dphy); + if (s_asd->port == 0) + return rkisp1_csi_link_sensor(rkisp1, sd, s_asd, source_pad); - return 0; -} - -static void rkisp1_subdev_notifier_unbind(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) -{ - struct rkisp1_sensor_async *s_asd = - container_of(asd, struct rkisp1_sensor_async, asd); + ret = media_create_pad_link(&sd->entity, source_pad, + &rkisp1->isp.sd.entity, + RKISP1_ISP_PAD_SINK_VIDEO, + !s_asd->index ? MEDIA_LNK_FL_ENABLED : 0); + if (ret) { + dev_err(rkisp1->dev, "failed to link source pad of %s\n", + sd->name); + return ret; + } - phy_exit(s_asd->dphy); + return 0; } static int rkisp1_subdev_notifier_complete(struct v4l2_async_notifier *notifier) { struct rkisp1_device *rkisp1 = container_of(notifier, struct rkisp1_device, notifier); - int ret; - ret = rkisp1_create_links(rkisp1); - if (ret) - return ret; - - ret = v4l2_device_register_subdev_nodes(&rkisp1->v4l2_dev); - if (ret) - return ret; + return v4l2_device_register_subdev_nodes(&rkisp1->v4l2_dev); +} - dev_dbg(rkisp1->dev, "Async subdev notifier completed\n"); +static void rkisp1_subdev_notifier_destroy(struct v4l2_async_subdev *asd) +{ + struct rkisp1_sensor_async *rk_asd = + container_of(asd, struct rkisp1_sensor_async, asd); - return 0; + fwnode_handle_put(rk_asd->source_ep); } static const struct v4l2_async_notifier_operations rkisp1_subdev_notifier_ops = { .bound = rkisp1_subdev_notifier_bound, - .unbind = rkisp1_subdev_notifier_unbind, .complete = rkisp1_subdev_notifier_complete, + .destroy = rkisp1_subdev_notifier_destroy, }; -static int rkisp1_subdev_notifier(struct rkisp1_device *rkisp1) +static int rkisp1_subdev_notifier_register(struct rkisp1_device *rkisp1) { struct v4l2_async_notifier *ntf = &rkisp1->notifier; - unsigned int next_id = 0; - int ret; + struct fwnode_handle *fwnode = dev_fwnode(rkisp1->dev); + struct fwnode_handle *ep; + unsigned int index = 0; + int ret = 0; v4l2_async_nf_init(ntf); - while (1) { - struct v4l2_fwnode_endpoint vep = { - .bus_type = V4L2_MBUS_CSI2_DPHY - }; + ntf->ops = &rkisp1_subdev_notifier_ops; + + fwnode_graph_for_each_endpoint(fwnode, ep) { + struct fwnode_handle *port; + struct v4l2_fwnode_endpoint vep = { }; struct rkisp1_sensor_async *rk_asd; - struct fwnode_handle *ep; + struct fwnode_handle *source; + u32 reg = 0; + + /* Select the bus type based on the port. */ + port = fwnode_get_parent(ep); + fwnode_property_read_u32(port, "reg", ®); + fwnode_handle_put(port); + + switch (reg) { + case 0: + /* MIPI CSI-2 port */ + if (!(rkisp1->info->features & RKISP1_FEATURE_MIPI_CSI2)) { + dev_err(rkisp1->dev, + "internal CSI must be available for port 0\n"); + ret = -EINVAL; + break; + } + + vep.bus_type = V4L2_MBUS_CSI2_DPHY; + break; - ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(rkisp1->dev), - 0, next_id, - FWNODE_GRAPH_ENDPOINT_NEXT); - if (!ep) + case 1: + /* + * Parallel port. The bus-type property in DT is + * mandatory for port 1, it will be used to determine if + * it's PARALLEL or BT656. + */ + vep.bus_type = V4L2_MBUS_UNKNOWN; break; + } + /* Parse the endpoint and validate the bus type. */ ret = v4l2_fwnode_endpoint_parse(ep, &vep); - if (ret) - goto err_parse; + if (ret) { + dev_err(rkisp1->dev, "failed to parse endpoint %pfw\n", + ep); + break; + } - rk_asd = v4l2_async_nf_add_fwnode_remote(ntf, ep, - struct - rkisp1_sensor_async); + if (vep.base.port == 1) { + if (vep.bus_type != V4L2_MBUS_PARALLEL && + vep.bus_type != V4L2_MBUS_BT656) { + dev_err(rkisp1->dev, + "port 1 must be parallel or BT656\n"); + ret = -EINVAL; + break; + } + } + + /* Add the async subdev to the notifier. */ + source = fwnode_graph_get_remote_endpoint(ep); + if (!source) { + dev_err(rkisp1->dev, + "endpoint %pfw has no remote endpoint\n", + ep); + ret = -ENODEV; + break; + } + + rk_asd = v4l2_async_nf_add_fwnode(ntf, source, + struct rkisp1_sensor_async); if (IS_ERR(rk_asd)) { + fwnode_handle_put(source); ret = PTR_ERR(rk_asd); - goto err_parse; + break; } + rk_asd->index = index++; + rk_asd->source_ep = source; rk_asd->mbus_type = vep.bus_type; - rk_asd->mbus_flags = vep.bus.mipi_csi2.flags; - rk_asd->lanes = vep.bus.mipi_csi2.num_data_lanes; - - dev_dbg(rkisp1->dev, "registered ep id %d with %d lanes\n", - vep.base.id, rk_asd->lanes); + rk_asd->port = vep.base.port; - next_id = vep.base.id + 1; + if (vep.bus_type == V4L2_MBUS_CSI2_DPHY) { + rk_asd->mbus_flags = vep.bus.mipi_csi2.flags; + rk_asd->lanes = vep.bus.mipi_csi2.num_data_lanes; + } else { + rk_asd->mbus_flags = vep.bus.parallel.flags; + } - fwnode_handle_put(ep); + dev_dbg(rkisp1->dev, "registered ep id %d, bus type %u, %u lanes\n", + vep.base.id, rk_asd->mbus_type, rk_asd->lanes); + } - continue; -err_parse: + if (ret) { fwnode_handle_put(ep); v4l2_async_nf_cleanup(ntf); return ret; } - if (next_id == 0) + if (!index) dev_dbg(rkisp1->dev, "no remote subdevice found\n"); - ntf->ops = &rkisp1_subdev_notifier_ops; + ret = v4l2_async_nf_register(&rkisp1->v4l2_dev, ntf); if (ret) { v4l2_async_nf_cleanup(ntf); return ret; } + return 0; } @@ -346,48 +333,110 @@ static const struct dev_pm_ops rkisp1_pm_ops = { * Core */ +static int rkisp1_create_links(struct rkisp1_device *rkisp1) +{ + unsigned int i; + int ret; + + if (rkisp1->info->features & RKISP1_FEATURE_MIPI_CSI2) { + /* Link the CSI receiver to the ISP. */ + ret = media_create_pad_link(&rkisp1->csi.sd.entity, + RKISP1_CSI_PAD_SRC, + &rkisp1->isp.sd.entity, + RKISP1_ISP_PAD_SINK_VIDEO, + MEDIA_LNK_FL_ENABLED); + if (ret) + return ret; + } + + /* create ISP->RSZ->CAP links */ + for (i = 0; i < 2; i++) { + struct media_entity *resizer = + &rkisp1->resizer_devs[i].sd.entity; + struct media_entity *capture = + &rkisp1->capture_devs[i].vnode.vdev.entity; + + ret = media_create_pad_link(&rkisp1->isp.sd.entity, + RKISP1_ISP_PAD_SOURCE_VIDEO, + resizer, RKISP1_RSZ_PAD_SINK, + MEDIA_LNK_FL_ENABLED); + if (ret) + return ret; + + ret = media_create_pad_link(resizer, RKISP1_RSZ_PAD_SRC, + capture, 0, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) + return ret; + } + + /* params links */ + ret = media_create_pad_link(&rkisp1->params.vnode.vdev.entity, 0, + &rkisp1->isp.sd.entity, + RKISP1_ISP_PAD_SINK_PARAMS, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) + return ret; + + /* 3A stats links */ + return media_create_pad_link(&rkisp1->isp.sd.entity, + RKISP1_ISP_PAD_SOURCE_STATS, + &rkisp1->stats.vnode.vdev.entity, 0, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); +} + +static void rkisp1_entities_unregister(struct rkisp1_device *rkisp1) +{ + if (rkisp1->info->features & RKISP1_FEATURE_MIPI_CSI2) + rkisp1_csi_unregister(rkisp1); + rkisp1_params_unregister(rkisp1); + rkisp1_stats_unregister(rkisp1); + rkisp1_capture_devs_unregister(rkisp1); + rkisp1_resizer_devs_unregister(rkisp1); + rkisp1_isp_unregister(rkisp1); +} + static int rkisp1_entities_register(struct rkisp1_device *rkisp1) { int ret; ret = rkisp1_isp_register(rkisp1); if (ret) - return ret; + goto error; ret = rkisp1_resizer_devs_register(rkisp1); if (ret) - goto err_unreg_isp_subdev; + goto error; ret = rkisp1_capture_devs_register(rkisp1); if (ret) - goto err_unreg_resizer_devs; + goto error; ret = rkisp1_stats_register(rkisp1); if (ret) - goto err_unreg_capture_devs; + goto error; ret = rkisp1_params_register(rkisp1); if (ret) - goto err_unreg_stats; + goto error; - ret = rkisp1_subdev_notifier(rkisp1); - if (ret) { - dev_err(rkisp1->dev, - "Failed to register subdev notifier(%d)\n", ret); - goto err_unreg_params; + if (rkisp1->info->features & RKISP1_FEATURE_MIPI_CSI2) { + ret = rkisp1_csi_register(rkisp1); + if (ret) + goto error; } + ret = rkisp1_create_links(rkisp1); + if (ret) + goto error; + return 0; -err_unreg_params: - rkisp1_params_unregister(rkisp1); -err_unreg_stats: - rkisp1_stats_unregister(rkisp1); -err_unreg_capture_devs: - rkisp1_capture_devs_unregister(rkisp1); -err_unreg_resizer_devs: - rkisp1_resizer_devs_unregister(rkisp1); -err_unreg_isp_subdev: - rkisp1_isp_unregister(rkisp1); + +error: + rkisp1_entities_unregister(rkisp1); return ret; } @@ -401,7 +450,7 @@ static irqreturn_t rkisp1_isr(int irq, void *ctx) */ rkisp1_capture_isr(irq, ctx); rkisp1_isp_isr(irq, ctx); - rkisp1_mipi_isr(irq, ctx); + rkisp1_csi_isr(irq, ctx); return IRQ_HANDLED; } @@ -416,15 +465,16 @@ static const char * const px30_isp_clks[] = { static const struct rkisp1_isr_data px30_isp_isrs[] = { { "isp", rkisp1_isp_isr }, { "mi", rkisp1_capture_isr }, - { "mipi", rkisp1_mipi_isr }, + { "mipi", rkisp1_csi_isr }, }; -static const struct rkisp1_match_data px30_isp_match_data = { +static const struct rkisp1_info px30_isp_info = { .clks = px30_isp_clks, .clk_size = ARRAY_SIZE(px30_isp_clks), .isrs = px30_isp_isrs, .isr_size = ARRAY_SIZE(px30_isp_isrs), .isp_ver = RKISP1_V12, + .features = RKISP1_FEATURE_MIPI_CSI2, }; static const char * const rk3399_isp_clks[] = { @@ -437,74 +487,45 @@ static const struct rkisp1_isr_data rk3399_isp_isrs[] = { { NULL, rkisp1_isr }, }; -static const struct rkisp1_match_data rk3399_isp_match_data = { +static const struct rkisp1_info rk3399_isp_info = { .clks = rk3399_isp_clks, .clk_size = ARRAY_SIZE(rk3399_isp_clks), .isrs = rk3399_isp_isrs, .isr_size = ARRAY_SIZE(rk3399_isp_isrs), .isp_ver = RKISP1_V10, + .features = RKISP1_FEATURE_MIPI_CSI2, }; static const struct of_device_id rkisp1_of_match[] = { { .compatible = "rockchip,px30-cif-isp", - .data = &px30_isp_match_data, + .data = &px30_isp_info, }, { .compatible = "rockchip,rk3399-cif-isp", - .data = &rk3399_isp_match_data, + .data = &rk3399_isp_info, }, {}, }; MODULE_DEVICE_TABLE(of, rkisp1_of_match); -static void rkisp1_debug_init(struct rkisp1_device *rkisp1) -{ - struct rkisp1_debug *debug = &rkisp1->debug; - - debug->debugfs_dir = debugfs_create_dir(dev_name(rkisp1->dev), NULL); - debugfs_create_ulong("data_loss", 0444, debug->debugfs_dir, - &debug->data_loss); - debugfs_create_ulong("outform_size_err", 0444, debug->debugfs_dir, - &debug->outform_size_error); - debugfs_create_ulong("img_stabilization_size_error", 0444, - debug->debugfs_dir, - &debug->img_stabilization_size_error); - debugfs_create_ulong("inform_size_error", 0444, debug->debugfs_dir, - &debug->inform_size_error); - debugfs_create_ulong("irq_delay", 0444, debug->debugfs_dir, - &debug->irq_delay); - debugfs_create_ulong("mipi_error", 0444, debug->debugfs_dir, - &debug->mipi_error); - debugfs_create_ulong("stats_error", 0444, debug->debugfs_dir, - &debug->stats_error); - debugfs_create_ulong("mp_stop_timeout", 0444, debug->debugfs_dir, - &debug->stop_timeout[RKISP1_MAINPATH]); - debugfs_create_ulong("sp_stop_timeout", 0444, debug->debugfs_dir, - &debug->stop_timeout[RKISP1_SELFPATH]); - debugfs_create_ulong("mp_frame_drop", 0444, debug->debugfs_dir, - &debug->frame_drop[RKISP1_MAINPATH]); - debugfs_create_ulong("sp_frame_drop", 0444, debug->debugfs_dir, - &debug->frame_drop[RKISP1_SELFPATH]); -} - static int rkisp1_probe(struct platform_device *pdev) { - const struct rkisp1_match_data *match_data; + const struct rkisp1_info *info; struct device *dev = &pdev->dev; struct rkisp1_device *rkisp1; struct v4l2_device *v4l2_dev; unsigned int i; int ret, irq; - - match_data = of_device_get_match_data(&pdev->dev); - if (!match_data) - return -ENODEV; + u32 cif_id; rkisp1 = devm_kzalloc(dev, sizeof(*rkisp1), GFP_KERNEL); if (!rkisp1) return -ENOMEM; + info = of_device_get_match_data(dev); + rkisp1->info = info; + dev_set_drvdata(dev, rkisp1); rkisp1->dev = dev; @@ -514,14 +535,14 @@ static int rkisp1_probe(struct platform_device *pdev) if (IS_ERR(rkisp1->base_addr)) return PTR_ERR(rkisp1->base_addr); - for (i = 0; i < match_data->isr_size; i++) { - irq = (match_data->isrs[i].name) ? - platform_get_irq_byname(pdev, match_data->isrs[i].name) : - platform_get_irq(pdev, i); + for (i = 0; i < info->isr_size; i++) { + irq = info->isrs[i].name + ? platform_get_irq_byname(pdev, info->isrs[i].name) + : platform_get_irq(pdev, i); if (irq < 0) return irq; - ret = devm_request_irq(dev, irq, match_data->isrs[i].isr, IRQF_SHARED, + ret = devm_request_irq(dev, irq, info->isrs[i].isr, IRQF_SHARED, dev_driver_string(dev), dev); if (ret) { dev_err(dev, "request irq failed: %d\n", ret); @@ -529,16 +550,25 @@ static int rkisp1_probe(struct platform_device *pdev) } } - for (i = 0; i < match_data->clk_size; i++) - rkisp1->clks[i].id = match_data->clks[i]; - ret = devm_clk_bulk_get(dev, match_data->clk_size, rkisp1->clks); + for (i = 0; i < info->clk_size; i++) + rkisp1->clks[i].id = info->clks[i]; + ret = devm_clk_bulk_get(dev, info->clk_size, rkisp1->clks); if (ret) return ret; - rkisp1->clk_size = match_data->clk_size; + rkisp1->clk_size = info->clk_size; pm_runtime_enable(&pdev->dev); - rkisp1->media_dev.hw_revision = match_data->isp_ver; + ret = pm_runtime_resume_and_get(&pdev->dev); + if (ret) + goto err_pm_runtime_disable; + + cif_id = rkisp1_read(rkisp1, RKISP1_CIF_VI_ID); + dev_dbg(rkisp1->dev, "CIF_ID 0x%08x\n", cif_id); + + pm_runtime_put(&pdev->dev); + + rkisp1->media_dev.hw_revision = info->isp_ver; strscpy(rkisp1->media_dev.model, RKISP1_DRIVER_NAME, sizeof(rkisp1->media_dev.model)); rkisp1->media_dev.dev = &pdev->dev; @@ -552,7 +582,7 @@ static int rkisp1_probe(struct platform_device *pdev) ret = v4l2_device_register(rkisp1->dev, &rkisp1->v4l2_dev); if (ret) - return ret; + goto err_pm_runtime_disable; ret = media_device_register(&rkisp1->media_dev); if (ret) { @@ -560,18 +590,34 @@ static int rkisp1_probe(struct platform_device *pdev) goto err_unreg_v4l2_dev; } + if (rkisp1->info->features & RKISP1_FEATURE_MIPI_CSI2) { + ret = rkisp1_csi_init(rkisp1); + if (ret) + goto err_unreg_media_dev; + } + ret = rkisp1_entities_register(rkisp1); if (ret) - goto err_unreg_media_dev; + goto err_cleanup_csi; + + ret = rkisp1_subdev_notifier_register(rkisp1); + if (ret) + goto err_unreg_entities; rkisp1_debug_init(rkisp1); return 0; +err_unreg_entities: + rkisp1_entities_unregister(rkisp1); +err_cleanup_csi: + if (rkisp1->info->features & RKISP1_FEATURE_MIPI_CSI2) + rkisp1_csi_cleanup(rkisp1); err_unreg_media_dev: media_device_unregister(&rkisp1->media_dev); err_unreg_v4l2_dev: v4l2_device_unregister(&rkisp1->v4l2_dev); +err_pm_runtime_disable: pm_runtime_disable(&pdev->dev); return ret; } @@ -583,18 +629,16 @@ static int rkisp1_remove(struct platform_device *pdev) v4l2_async_nf_unregister(&rkisp1->notifier); v4l2_async_nf_cleanup(&rkisp1->notifier); - rkisp1_params_unregister(rkisp1); - rkisp1_stats_unregister(rkisp1); - rkisp1_capture_devs_unregister(rkisp1); - rkisp1_resizer_devs_unregister(rkisp1); - rkisp1_isp_unregister(rkisp1); + rkisp1_entities_unregister(rkisp1); + if (rkisp1->info->features & RKISP1_FEATURE_MIPI_CSI2) + rkisp1_csi_cleanup(rkisp1); + rkisp1_debug_cleanup(rkisp1); media_device_unregister(&rkisp1->media_dev); v4l2_device_unregister(&rkisp1->v4l2_dev); pm_runtime_disable(&pdev->dev); - debugfs_remove_recursive(rkisp1->debug.debugfs_dir); return 0; } diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c index 4415c7248c2f..383a3ec83ca9 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c @@ -9,13 +9,10 @@ */ #include <linux/iopoll.h> -#include <linux/phy/phy.h> -#include <linux/phy/phy-mipi-dphy.h> #include <linux/pm_runtime.h> #include <linux/videodev2.h> #include <linux/vmalloc.h> -#include <media/mipi-csi2.h> #include <media/v4l2-event.h> #include "rkisp1-common.h" @@ -56,158 +53,10 @@ * +---------------------------------------------------------+ */ -static const struct rkisp1_isp_mbus_info rkisp1_isp_formats[] = { - { - .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, - .pixel_enc = V4L2_PIXEL_ENC_YUV, - .direction = RKISP1_ISP_SD_SRC, - }, { - .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, - .pixel_enc = V4L2_PIXEL_ENC_BAYER, - .mipi_dt = MIPI_CSI2_DT_RAW10, - .bayer_pat = RKISP1_RAW_RGGB, - .bus_width = 10, - .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, - }, { - .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, - .pixel_enc = V4L2_PIXEL_ENC_BAYER, - .mipi_dt = MIPI_CSI2_DT_RAW10, - .bayer_pat = RKISP1_RAW_BGGR, - .bus_width = 10, - .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, - }, { - .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, - .pixel_enc = V4L2_PIXEL_ENC_BAYER, - .mipi_dt = MIPI_CSI2_DT_RAW10, - .bayer_pat = RKISP1_RAW_GBRG, - .bus_width = 10, - .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, - }, { - .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, - .pixel_enc = V4L2_PIXEL_ENC_BAYER, - .mipi_dt = MIPI_CSI2_DT_RAW10, - .bayer_pat = RKISP1_RAW_GRBG, - .bus_width = 10, - .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, - }, { - .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12, - .pixel_enc = V4L2_PIXEL_ENC_BAYER, - .mipi_dt = MIPI_CSI2_DT_RAW12, - .bayer_pat = RKISP1_RAW_RGGB, - .bus_width = 12, - .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, - }, { - .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, - .pixel_enc = V4L2_PIXEL_ENC_BAYER, - .mipi_dt = MIPI_CSI2_DT_RAW12, - .bayer_pat = RKISP1_RAW_BGGR, - .bus_width = 12, - .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, - }, { - .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12, - .pixel_enc = V4L2_PIXEL_ENC_BAYER, - .mipi_dt = MIPI_CSI2_DT_RAW12, - .bayer_pat = RKISP1_RAW_GBRG, - .bus_width = 12, - .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, - }, { - .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, - .pixel_enc = V4L2_PIXEL_ENC_BAYER, - .mipi_dt = MIPI_CSI2_DT_RAW12, - .bayer_pat = RKISP1_RAW_GRBG, - .bus_width = 12, - .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, - }, { - .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, - .pixel_enc = V4L2_PIXEL_ENC_BAYER, - .mipi_dt = MIPI_CSI2_DT_RAW8, - .bayer_pat = RKISP1_RAW_RGGB, - .bus_width = 8, - .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, - }, { - .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, - .pixel_enc = V4L2_PIXEL_ENC_BAYER, - .mipi_dt = MIPI_CSI2_DT_RAW8, - .bayer_pat = RKISP1_RAW_BGGR, - .bus_width = 8, - .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, - }, { - .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, - .pixel_enc = V4L2_PIXEL_ENC_BAYER, - .mipi_dt = MIPI_CSI2_DT_RAW8, - .bayer_pat = RKISP1_RAW_GBRG, - .bus_width = 8, - .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, - }, { - .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, - .pixel_enc = V4L2_PIXEL_ENC_BAYER, - .mipi_dt = MIPI_CSI2_DT_RAW8, - .bayer_pat = RKISP1_RAW_GRBG, - .bus_width = 8, - .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, - }, { - .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16, - .pixel_enc = V4L2_PIXEL_ENC_YUV, - .mipi_dt = MIPI_CSI2_DT_YUV422_8B, - .yuv_seq = RKISP1_CIF_ISP_ACQ_PROP_YCBYCR, - .bus_width = 16, - .direction = RKISP1_ISP_SD_SINK, - }, { - .mbus_code = MEDIA_BUS_FMT_YVYU8_1X16, - .pixel_enc = V4L2_PIXEL_ENC_YUV, - .mipi_dt = MIPI_CSI2_DT_YUV422_8B, - .yuv_seq = RKISP1_CIF_ISP_ACQ_PROP_YCRYCB, - .bus_width = 16, - .direction = RKISP1_ISP_SD_SINK, - }, { - .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16, - .pixel_enc = V4L2_PIXEL_ENC_YUV, - .mipi_dt = MIPI_CSI2_DT_YUV422_8B, - .yuv_seq = RKISP1_CIF_ISP_ACQ_PROP_CBYCRY, - .bus_width = 16, - .direction = RKISP1_ISP_SD_SINK, - }, { - .mbus_code = MEDIA_BUS_FMT_VYUY8_1X16, - .pixel_enc = V4L2_PIXEL_ENC_YUV, - .mipi_dt = MIPI_CSI2_DT_YUV422_8B, - .yuv_seq = RKISP1_CIF_ISP_ACQ_PROP_CRYCBY, - .bus_width = 16, - .direction = RKISP1_ISP_SD_SINK, - }, -}; - /* ---------------------------------------------------------------------------- * Helpers */ -const struct rkisp1_isp_mbus_info *rkisp1_isp_mbus_info_get(u32 mbus_code) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(rkisp1_isp_formats); i++) { - const struct rkisp1_isp_mbus_info *fmt = &rkisp1_isp_formats[i]; - - if (fmt->mbus_code == mbus_code) - return fmt; - } - - return NULL; -} - -static struct v4l2_subdev *rkisp1_get_remote_sensor(struct v4l2_subdev *sd) -{ - struct media_pad *local, *remote; - struct media_entity *sensor_me; - - local = &sd->entity.pads[RKISP1_ISP_PAD_SINK_VIDEO]; - remote = media_entity_remote_pad(local); - if (!remote) - return NULL; - - sensor_me = remote->entity; - return media_entity_to_v4l2_subdev(sensor_me); -} - static struct v4l2_mbus_framefmt * rkisp1_isp_get_pad_fmt(struct rkisp1_isp *isp, struct v4l2_subdev_state *sd_state, @@ -215,7 +64,8 @@ rkisp1_isp_get_pad_fmt(struct rkisp1_isp *isp, { struct v4l2_subdev_state state = { .pads = isp->pad_cfg - }; + }; + if (which == V4L2_SUBDEV_FORMAT_TRY) return v4l2_subdev_get_try_format(&isp->sd, sd_state, pad); else @@ -229,7 +79,8 @@ rkisp1_isp_get_pad_crop(struct rkisp1_isp *isp, { struct v4l2_subdev_state state = { .pads = isp->pad_cfg - }; + }; + if (which == V4L2_SUBDEV_FORMAT_TRY) return v4l2_subdev_get_try_crop(&isp->sd, sd_state, pad); else @@ -245,73 +96,73 @@ rkisp1_isp_get_pad_crop(struct rkisp1_isp *isp, * This should only be called when configuring CIF * or at the frame end interrupt */ -static void rkisp1_config_ism(struct rkisp1_device *rkisp1) +static void rkisp1_config_ism(struct rkisp1_isp *isp) { - struct v4l2_rect *src_crop = - rkisp1_isp_get_pad_crop(&rkisp1->isp, NULL, + const struct v4l2_rect *src_crop = + rkisp1_isp_get_pad_crop(isp, NULL, RKISP1_ISP_PAD_SOURCE_VIDEO, V4L2_SUBDEV_FORMAT_ACTIVE); + struct rkisp1_device *rkisp1 = isp->rkisp1; u32 val; - rkisp1_write(rkisp1, 0, RKISP1_CIF_ISP_IS_RECENTER); - rkisp1_write(rkisp1, 0, RKISP1_CIF_ISP_IS_MAX_DX); - rkisp1_write(rkisp1, 0, RKISP1_CIF_ISP_IS_MAX_DY); - rkisp1_write(rkisp1, 0, RKISP1_CIF_ISP_IS_DISPLACE); - rkisp1_write(rkisp1, src_crop->left, RKISP1_CIF_ISP_IS_H_OFFS); - rkisp1_write(rkisp1, src_crop->top, RKISP1_CIF_ISP_IS_V_OFFS); - rkisp1_write(rkisp1, src_crop->width, RKISP1_CIF_ISP_IS_H_SIZE); - rkisp1_write(rkisp1, src_crop->height, RKISP1_CIF_ISP_IS_V_SIZE); + rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_RECENTER, 0); + rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_MAX_DX, 0); + rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_MAX_DY, 0); + rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_DISPLACE, 0); + rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_H_OFFS, src_crop->left); + rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_V_OFFS, src_crop->top); + rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_H_SIZE, src_crop->width); + rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_V_SIZE, src_crop->height); /* IS(Image Stabilization) is always on, working as output crop */ - rkisp1_write(rkisp1, 1, RKISP1_CIF_ISP_IS_CTRL); + rkisp1_write(rkisp1, RKISP1_CIF_ISP_IS_CTRL, 1); val = rkisp1_read(rkisp1, RKISP1_CIF_ISP_CTRL); val |= RKISP1_CIF_ISP_CTRL_ISP_CFG_UPD; - rkisp1_write(rkisp1, val, RKISP1_CIF_ISP_CTRL); + rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL, val); } /* * configure ISP blocks with input format, size...... */ -static int rkisp1_config_isp(struct rkisp1_device *rkisp1) -{ - u32 isp_ctrl = 0, irq_mask = 0, acq_mult = 0, signal = 0; - const struct rkisp1_isp_mbus_info *src_fmt, *sink_fmt; - struct rkisp1_sensor_async *sensor; - struct v4l2_mbus_framefmt *sink_frm; - struct v4l2_rect *sink_crop; +static int rkisp1_config_isp(struct rkisp1_isp *isp, + enum v4l2_mbus_type mbus_type, u32 mbus_flags) +{ + struct rkisp1_device *rkisp1 = isp->rkisp1; + u32 isp_ctrl = 0, irq_mask = 0, acq_mult = 0, acq_prop = 0; + const struct rkisp1_mbus_info *sink_fmt = isp->sink_fmt; + const struct rkisp1_mbus_info *src_fmt = isp->src_fmt; + const struct v4l2_mbus_framefmt *sink_frm; + const struct v4l2_rect *sink_crop; - sensor = rkisp1->active_sensor; - sink_fmt = rkisp1->isp.sink_fmt; - src_fmt = rkisp1->isp.src_fmt; - sink_frm = rkisp1_isp_get_pad_fmt(&rkisp1->isp, NULL, + sink_frm = rkisp1_isp_get_pad_fmt(isp, NULL, RKISP1_ISP_PAD_SINK_VIDEO, V4L2_SUBDEV_FORMAT_ACTIVE); - sink_crop = rkisp1_isp_get_pad_crop(&rkisp1->isp, NULL, + sink_crop = rkisp1_isp_get_pad_crop(isp, NULL, RKISP1_ISP_PAD_SINK_VIDEO, V4L2_SUBDEV_FORMAT_ACTIVE); if (sink_fmt->pixel_enc == V4L2_PIXEL_ENC_BAYER) { acq_mult = 1; if (src_fmt->pixel_enc == V4L2_PIXEL_ENC_BAYER) { - if (sensor->mbus_type == V4L2_MBUS_BT656) + if (mbus_type == V4L2_MBUS_BT656) isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_RAW_PICT_ITU656; else isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_RAW_PICT; } else { - rkisp1_write(rkisp1, RKISP1_CIF_ISP_DEMOSAIC_TH(0xc), - RKISP1_CIF_ISP_DEMOSAIC); + rkisp1_write(rkisp1, RKISP1_CIF_ISP_DEMOSAIC, + RKISP1_CIF_ISP_DEMOSAIC_TH(0xc)); - if (sensor->mbus_type == V4L2_MBUS_BT656) + if (mbus_type == V4L2_MBUS_BT656) isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_BAYER_ITU656; else isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_BAYER_ITU601; } } else if (sink_fmt->pixel_enc == V4L2_PIXEL_ENC_YUV) { acq_mult = 2; - if (sensor->mbus_type == V4L2_MBUS_CSI2_DPHY) { + if (mbus_type == V4L2_MBUS_CSI2_DPHY) { isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_ITU601; } else { - if (sensor->mbus_type == V4L2_MBUS_BT656) + if (mbus_type == V4L2_MBUS_BT656) isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_ITU656; else isp_ctrl = RKISP1_CIF_ISP_CTRL_ISP_MODE_ITU601; @@ -321,50 +172,65 @@ static int rkisp1_config_isp(struct rkisp1_device *rkisp1) } /* Set up input acquisition properties */ - if (sensor->mbus_type == V4L2_MBUS_BT656 || - sensor->mbus_type == V4L2_MBUS_PARALLEL) { - if (sensor->mbus_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) - signal = RKISP1_CIF_ISP_ACQ_PROP_POS_EDGE; + if (mbus_type == V4L2_MBUS_BT656 || mbus_type == V4L2_MBUS_PARALLEL) { + if (mbus_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) + acq_prop |= RKISP1_CIF_ISP_ACQ_PROP_POS_EDGE; + + switch (sink_fmt->bus_width) { + case 8: + acq_prop |= RKISP1_CIF_ISP_ACQ_PROP_IN_SEL_8B_ZERO; + break; + case 10: + acq_prop |= RKISP1_CIF_ISP_ACQ_PROP_IN_SEL_10B_ZERO; + break; + case 12: + acq_prop |= RKISP1_CIF_ISP_ACQ_PROP_IN_SEL_12B; + break; + default: + dev_err(rkisp1->dev, "Invalid bus width %u\n", + sink_fmt->bus_width); + return -EINVAL; + } } - if (sensor->mbus_type == V4L2_MBUS_PARALLEL) { - if (sensor->mbus_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) - signal |= RKISP1_CIF_ISP_ACQ_PROP_VSYNC_LOW; + if (mbus_type == V4L2_MBUS_PARALLEL) { + if (mbus_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) + acq_prop |= RKISP1_CIF_ISP_ACQ_PROP_VSYNC_LOW; - if (sensor->mbus_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) - signal |= RKISP1_CIF_ISP_ACQ_PROP_HSYNC_LOW; + if (mbus_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) + acq_prop |= RKISP1_CIF_ISP_ACQ_PROP_HSYNC_LOW; } - rkisp1_write(rkisp1, isp_ctrl, RKISP1_CIF_ISP_CTRL); - rkisp1_write(rkisp1, signal | sink_fmt->yuv_seq | + rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL, isp_ctrl); + rkisp1_write(rkisp1, RKISP1_CIF_ISP_ACQ_PROP, + acq_prop | sink_fmt->yuv_seq | RKISP1_CIF_ISP_ACQ_PROP_BAYER_PAT(sink_fmt->bayer_pat) | - RKISP1_CIF_ISP_ACQ_PROP_FIELD_SEL_ALL, - RKISP1_CIF_ISP_ACQ_PROP); - rkisp1_write(rkisp1, 0, RKISP1_CIF_ISP_ACQ_NR_FRAMES); + RKISP1_CIF_ISP_ACQ_PROP_FIELD_SEL_ALL); + rkisp1_write(rkisp1, RKISP1_CIF_ISP_ACQ_NR_FRAMES, 0); /* Acquisition Size */ - rkisp1_write(rkisp1, 0, RKISP1_CIF_ISP_ACQ_H_OFFS); - rkisp1_write(rkisp1, 0, RKISP1_CIF_ISP_ACQ_V_OFFS); - rkisp1_write(rkisp1, - acq_mult * sink_frm->width, RKISP1_CIF_ISP_ACQ_H_SIZE); - rkisp1_write(rkisp1, sink_frm->height, RKISP1_CIF_ISP_ACQ_V_SIZE); + rkisp1_write(rkisp1, RKISP1_CIF_ISP_ACQ_H_OFFS, 0); + rkisp1_write(rkisp1, RKISP1_CIF_ISP_ACQ_V_OFFS, 0); + rkisp1_write(rkisp1, RKISP1_CIF_ISP_ACQ_H_SIZE, + acq_mult * sink_frm->width); + rkisp1_write(rkisp1, RKISP1_CIF_ISP_ACQ_V_SIZE, sink_frm->height); /* ISP Out Area */ - rkisp1_write(rkisp1, sink_crop->left, RKISP1_CIF_ISP_OUT_H_OFFS); - rkisp1_write(rkisp1, sink_crop->top, RKISP1_CIF_ISP_OUT_V_OFFS); - rkisp1_write(rkisp1, sink_crop->width, RKISP1_CIF_ISP_OUT_H_SIZE); - rkisp1_write(rkisp1, sink_crop->height, RKISP1_CIF_ISP_OUT_V_SIZE); + rkisp1_write(rkisp1, RKISP1_CIF_ISP_OUT_H_OFFS, sink_crop->left); + rkisp1_write(rkisp1, RKISP1_CIF_ISP_OUT_V_OFFS, sink_crop->top); + rkisp1_write(rkisp1, RKISP1_CIF_ISP_OUT_H_SIZE, sink_crop->width); + rkisp1_write(rkisp1, RKISP1_CIF_ISP_OUT_V_SIZE, sink_crop->height); irq_mask |= RKISP1_CIF_ISP_FRAME | RKISP1_CIF_ISP_V_START | RKISP1_CIF_ISP_PIC_SIZE_ERROR; - rkisp1_write(rkisp1, irq_mask, RKISP1_CIF_ISP_IMSC); + rkisp1_write(rkisp1, RKISP1_CIF_ISP_IMSC, irq_mask); if (src_fmt->pixel_enc == V4L2_PIXEL_ENC_BAYER) { rkisp1_params_disable(&rkisp1->params); } else { struct v4l2_mbus_framefmt *src_frm; - src_frm = rkisp1_isp_get_pad_fmt(&rkisp1->isp, NULL, + src_frm = rkisp1_isp_get_pad_fmt(isp, NULL, RKISP1_ISP_PAD_SINK_VIDEO, V4L2_SUBDEV_FORMAT_ACTIVE); rkisp1_params_configure(&rkisp1->params, sink_fmt->bayer_pat, @@ -374,213 +240,117 @@ static int rkisp1_config_isp(struct rkisp1_device *rkisp1) return 0; } -static int rkisp1_config_dvp(struct rkisp1_device *rkisp1) -{ - const struct rkisp1_isp_mbus_info *sink_fmt = rkisp1->isp.sink_fmt; - u32 val, input_sel; - - switch (sink_fmt->bus_width) { - case 8: - input_sel = RKISP1_CIF_ISP_ACQ_PROP_IN_SEL_8B_ZERO; - break; - case 10: - input_sel = RKISP1_CIF_ISP_ACQ_PROP_IN_SEL_10B_ZERO; - break; - case 12: - input_sel = RKISP1_CIF_ISP_ACQ_PROP_IN_SEL_12B; - break; - default: - dev_err(rkisp1->dev, "Invalid bus width\n"); - return -EINVAL; - } - - val = rkisp1_read(rkisp1, RKISP1_CIF_ISP_ACQ_PROP); - rkisp1_write(rkisp1, val | input_sel, RKISP1_CIF_ISP_ACQ_PROP); - - return 0; -} - -static int rkisp1_config_mipi(struct rkisp1_device *rkisp1) -{ - const struct rkisp1_isp_mbus_info *sink_fmt = rkisp1->isp.sink_fmt; - unsigned int lanes = rkisp1->active_sensor->lanes; - u32 mipi_ctrl; - - if (lanes < 1 || lanes > 4) - return -EINVAL; - - mipi_ctrl = RKISP1_CIF_MIPI_CTRL_NUM_LANES(lanes - 1) | - RKISP1_CIF_MIPI_CTRL_SHUTDOWNLANES(0xf) | - RKISP1_CIF_MIPI_CTRL_ERR_SOT_SYNC_HS_SKIP | - RKISP1_CIF_MIPI_CTRL_CLOCKLANE_ENA; - - rkisp1_write(rkisp1, mipi_ctrl, RKISP1_CIF_MIPI_CTRL); - - /* V12 could also use a newer csi2-host, but we don't want that yet */ - if (rkisp1->media_dev.hw_revision == RKISP1_V12) - rkisp1_write(rkisp1, 0, RKISP1_CIF_ISP_CSI0_CTRL0); - - /* Configure Data Type and Virtual Channel */ - rkisp1_write(rkisp1, - RKISP1_CIF_MIPI_DATA_SEL_DT(sink_fmt->mipi_dt) | - RKISP1_CIF_MIPI_DATA_SEL_VC(0), - RKISP1_CIF_MIPI_IMG_DATA_SEL); - - /* Clear MIPI interrupts */ - rkisp1_write(rkisp1, ~0, RKISP1_CIF_MIPI_ICR); - /* - * Disable RKISP1_CIF_MIPI_ERR_DPHY interrupt here temporary for - * isp bus may be dead when switch isp. - */ - rkisp1_write(rkisp1, - RKISP1_CIF_MIPI_FRAME_END | RKISP1_CIF_MIPI_ERR_CSI | - RKISP1_CIF_MIPI_ERR_DPHY | - RKISP1_CIF_MIPI_SYNC_FIFO_OVFLW(0x03) | - RKISP1_CIF_MIPI_ADD_DATA_OVFLW, - RKISP1_CIF_MIPI_IMSC); - - dev_dbg(rkisp1->dev, "\n MIPI_CTRL 0x%08x\n" - " MIPI_IMG_DATA_SEL 0x%08x\n" - " MIPI_STATUS 0x%08x\n" - " MIPI_IMSC 0x%08x\n", - rkisp1_read(rkisp1, RKISP1_CIF_MIPI_CTRL), - rkisp1_read(rkisp1, RKISP1_CIF_MIPI_IMG_DATA_SEL), - rkisp1_read(rkisp1, RKISP1_CIF_MIPI_STATUS), - rkisp1_read(rkisp1, RKISP1_CIF_MIPI_IMSC)); - - return 0; -} - /* Configure MUX */ -static int rkisp1_config_path(struct rkisp1_device *rkisp1) +static void rkisp1_config_path(struct rkisp1_isp *isp, + enum v4l2_mbus_type mbus_type) { - struct rkisp1_sensor_async *sensor = rkisp1->active_sensor; + struct rkisp1_device *rkisp1 = isp->rkisp1; u32 dpcl = rkisp1_read(rkisp1, RKISP1_CIF_VI_DPCL); - int ret = 0; - if (sensor->mbus_type == V4L2_MBUS_BT656 || - sensor->mbus_type == V4L2_MBUS_PARALLEL) { - ret = rkisp1_config_dvp(rkisp1); + if (mbus_type == V4L2_MBUS_BT656 || mbus_type == V4L2_MBUS_PARALLEL) dpcl |= RKISP1_CIF_VI_DPCL_IF_SEL_PARALLEL; - } else if (sensor->mbus_type == V4L2_MBUS_CSI2_DPHY) { - ret = rkisp1_config_mipi(rkisp1); + else if (mbus_type == V4L2_MBUS_CSI2_DPHY) dpcl |= RKISP1_CIF_VI_DPCL_IF_SEL_MIPI; - } - - rkisp1_write(rkisp1, dpcl, RKISP1_CIF_VI_DPCL); - return ret; + rkisp1_write(rkisp1, RKISP1_CIF_VI_DPCL, dpcl); } /* Hardware configure Entry */ -static int rkisp1_config_cif(struct rkisp1_device *rkisp1) +static int rkisp1_config_cif(struct rkisp1_isp *isp, + enum v4l2_mbus_type mbus_type, u32 mbus_flags) { - u32 cif_id; int ret; - cif_id = rkisp1_read(rkisp1, RKISP1_CIF_VI_ID); - dev_dbg(rkisp1->dev, "CIF_ID 0x%08x\n", cif_id); - - ret = rkisp1_config_isp(rkisp1); + ret = rkisp1_config_isp(isp, mbus_type, mbus_flags); if (ret) return ret; - ret = rkisp1_config_path(rkisp1); - if (ret) - return ret; - rkisp1_config_ism(rkisp1); + + rkisp1_config_path(isp, mbus_type); + rkisp1_config_ism(isp); return 0; } -static void rkisp1_isp_stop(struct rkisp1_device *rkisp1) +static void rkisp1_isp_stop(struct rkisp1_isp *isp) { + struct rkisp1_device *rkisp1 = isp->rkisp1; u32 val; /* * ISP(mi) stop in mi frame end -> Stop ISP(mipi) -> * Stop ISP(isp) ->wait for ISP isp off */ - /* stop and clear MI, MIPI, and ISP interrupts */ - rkisp1_write(rkisp1, 0, RKISP1_CIF_MIPI_IMSC); - rkisp1_write(rkisp1, ~0, RKISP1_CIF_MIPI_ICR); - - rkisp1_write(rkisp1, 0, RKISP1_CIF_ISP_IMSC); - rkisp1_write(rkisp1, ~0, RKISP1_CIF_ISP_ICR); - - rkisp1_write(rkisp1, 0, RKISP1_CIF_MI_IMSC); - rkisp1_write(rkisp1, ~0, RKISP1_CIF_MI_ICR); - val = rkisp1_read(rkisp1, RKISP1_CIF_MIPI_CTRL); - rkisp1_write(rkisp1, val & (~RKISP1_CIF_MIPI_CTRL_OUTPUT_ENA), - RKISP1_CIF_MIPI_CTRL); + /* stop and clear MI and ISP interrupts */ + rkisp1_write(rkisp1, RKISP1_CIF_ISP_IMSC, 0); + rkisp1_write(rkisp1, RKISP1_CIF_ISP_ICR, ~0); + + rkisp1_write(rkisp1, RKISP1_CIF_MI_IMSC, 0); + rkisp1_write(rkisp1, RKISP1_CIF_MI_ICR, ~0); + /* stop ISP */ val = rkisp1_read(rkisp1, RKISP1_CIF_ISP_CTRL); val &= ~(RKISP1_CIF_ISP_CTRL_ISP_INFORM_ENABLE | RKISP1_CIF_ISP_CTRL_ISP_ENABLE); - rkisp1_write(rkisp1, val, RKISP1_CIF_ISP_CTRL); + rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL, val); val = rkisp1_read(rkisp1, RKISP1_CIF_ISP_CTRL); - rkisp1_write(rkisp1, val | RKISP1_CIF_ISP_CTRL_ISP_CFG_UPD, - RKISP1_CIF_ISP_CTRL); + rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL, + val | RKISP1_CIF_ISP_CTRL_ISP_CFG_UPD); readx_poll_timeout(readl, rkisp1->base_addr + RKISP1_CIF_ISP_RIS, val, val & RKISP1_CIF_ISP_OFF, 20, 100); - rkisp1_write(rkisp1, - RKISP1_CIF_IRCL_MIPI_SW_RST | RKISP1_CIF_IRCL_ISP_SW_RST, - RKISP1_CIF_IRCL); - rkisp1_write(rkisp1, 0x0, RKISP1_CIF_IRCL); + rkisp1_write(rkisp1, RKISP1_CIF_VI_IRCL, + RKISP1_CIF_VI_IRCL_MIPI_SW_RST | + RKISP1_CIF_VI_IRCL_ISP_SW_RST); + rkisp1_write(rkisp1, RKISP1_CIF_VI_IRCL, 0x0); } -static void rkisp1_config_clk(struct rkisp1_device *rkisp1) +static void rkisp1_config_clk(struct rkisp1_isp *isp) { - u32 val = RKISP1_CIF_ICCL_ISP_CLK | RKISP1_CIF_ICCL_CP_CLK | - RKISP1_CIF_ICCL_MRSZ_CLK | RKISP1_CIF_ICCL_SRSZ_CLK | - RKISP1_CIF_ICCL_JPEG_CLK | RKISP1_CIF_ICCL_MI_CLK | - RKISP1_CIF_ICCL_IE_CLK | RKISP1_CIF_ICCL_MIPI_CLK | - RKISP1_CIF_ICCL_DCROP_CLK; + struct rkisp1_device *rkisp1 = isp->rkisp1; + + u32 val = RKISP1_CIF_VI_ICCL_ISP_CLK | RKISP1_CIF_VI_ICCL_CP_CLK | + RKISP1_CIF_VI_ICCL_MRSZ_CLK | RKISP1_CIF_VI_ICCL_SRSZ_CLK | + RKISP1_CIF_VI_ICCL_JPEG_CLK | RKISP1_CIF_VI_ICCL_MI_CLK | + RKISP1_CIF_VI_ICCL_IE_CLK | RKISP1_CIF_VI_ICCL_MIPI_CLK | + RKISP1_CIF_VI_ICCL_DCROP_CLK; - rkisp1_write(rkisp1, val, RKISP1_CIF_ICCL); + rkisp1_write(rkisp1, RKISP1_CIF_VI_ICCL, val); /* ensure sp and mp can run at the same time in V12 */ - if (rkisp1->media_dev.hw_revision == RKISP1_V12) { + if (rkisp1->info->isp_ver == RKISP1_V12) { val = RKISP1_CIF_CLK_CTRL_MI_Y12 | RKISP1_CIF_CLK_CTRL_MI_SP | RKISP1_CIF_CLK_CTRL_MI_RAW0 | RKISP1_CIF_CLK_CTRL_MI_RAW1 | RKISP1_CIF_CLK_CTRL_MI_READ | RKISP1_CIF_CLK_CTRL_MI_RAWRD | RKISP1_CIF_CLK_CTRL_CP | RKISP1_CIF_CLK_CTRL_IE; - rkisp1_write(rkisp1, val, RKISP1_CIF_VI_ISP_CLK_CTRL_V12); + rkisp1_write(rkisp1, RKISP1_CIF_VI_ISP_CLK_CTRL_V12, val); } } -static void rkisp1_isp_start(struct rkisp1_device *rkisp1) +static void rkisp1_isp_start(struct rkisp1_isp *isp) { - struct rkisp1_sensor_async *sensor = rkisp1->active_sensor; + struct rkisp1_device *rkisp1 = isp->rkisp1; u32 val; - rkisp1_config_clk(rkisp1); + rkisp1_config_clk(isp); - /* Activate MIPI */ - if (sensor->mbus_type == V4L2_MBUS_CSI2_DPHY) { - val = rkisp1_read(rkisp1, RKISP1_CIF_MIPI_CTRL); - rkisp1_write(rkisp1, val | RKISP1_CIF_MIPI_CTRL_OUTPUT_ENA, - RKISP1_CIF_MIPI_CTRL); - } /* Activate ISP */ val = rkisp1_read(rkisp1, RKISP1_CIF_ISP_CTRL); val |= RKISP1_CIF_ISP_CTRL_ISP_CFG_UPD | RKISP1_CIF_ISP_CTRL_ISP_ENABLE | RKISP1_CIF_ISP_CTRL_ISP_INFORM_ENABLE; - rkisp1_write(rkisp1, val, RKISP1_CIF_ISP_CTRL); - - /* - * CIF spec says to wait for sufficient time after enabling - * the MIPI interface and before starting the sensor output. - */ - usleep_range(1000, 1200); + rkisp1_write(rkisp1, RKISP1_CIF_ISP_CTRL, val); } /* ---------------------------------------------------------------------------- * Subdev pad operations */ +static inline struct rkisp1_isp *to_rkisp1_isp(struct v4l2_subdev *sd) +{ + return container_of(sd, struct rkisp1_isp, sd); +} + static int rkisp1_isp_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) @@ -599,11 +369,12 @@ static int rkisp1_isp_enum_mbus_code(struct v4l2_subdev *sd, return 0; } - if (code->index >= ARRAY_SIZE(rkisp1_isp_formats)) - return -EINVAL; + for (i = 0; ; i++) { + const struct rkisp1_mbus_info *fmt = + rkisp1_mbus_info_get_by_index(i); - for (i = 0; i < ARRAY_SIZE(rkisp1_isp_formats); i++) { - const struct rkisp1_isp_mbus_info *fmt = &rkisp1_isp_formats[i]; + if (!fmt) + return -EINVAL; if (fmt->direction & dir) pos++; @@ -625,7 +396,7 @@ static int rkisp1_isp_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { - const struct rkisp1_isp_mbus_info *mbus_info; + const struct rkisp1_mbus_info *mbus_info; if (fse->pad == RKISP1_ISP_PAD_SINK_PARAMS || fse->pad == RKISP1_ISP_PAD_SOURCE_STATS) @@ -634,7 +405,7 @@ static int rkisp1_isp_enum_frame_size(struct v4l2_subdev *sd, if (fse->index > 0) return -EINVAL; - mbus_info = rkisp1_isp_mbus_info_get(fse->code); + mbus_info = rkisp1_mbus_info_get_by_code(fse->code); if (!mbus_info) return -EINVAL; @@ -701,7 +472,7 @@ static void rkisp1_isp_set_src_fmt(struct rkisp1_isp *isp, struct v4l2_mbus_framefmt *format, unsigned int which) { - const struct rkisp1_isp_mbus_info *mbus_info; + const struct rkisp1_mbus_info *mbus_info; struct v4l2_mbus_framefmt *src_fmt; const struct v4l2_rect *src_crop; @@ -711,10 +482,10 @@ static void rkisp1_isp_set_src_fmt(struct rkisp1_isp *isp, RKISP1_ISP_PAD_SOURCE_VIDEO, which); src_fmt->code = format->code; - mbus_info = rkisp1_isp_mbus_info_get(src_fmt->code); + mbus_info = rkisp1_mbus_info_get_by_code(src_fmt->code); if (!mbus_info || !(mbus_info->direction & RKISP1_ISP_SD_SRC)) { src_fmt->code = RKISP1_DEF_SRC_PAD_FMT; - mbus_info = rkisp1_isp_mbus_info_get(src_fmt->code); + mbus_info = rkisp1_mbus_info_get_by_code(src_fmt->code); } if (which == V4L2_SUBDEV_FORMAT_ACTIVE) isp->src_fmt = mbus_info; @@ -771,7 +542,7 @@ static void rkisp1_isp_set_sink_crop(struct rkisp1_isp *isp, struct v4l2_rect *r, unsigned int which) { struct v4l2_rect *sink_crop, *src_crop; - struct v4l2_mbus_framefmt *sink_fmt; + const struct v4l2_mbus_framefmt *sink_fmt; sink_crop = rkisp1_isp_get_pad_crop(isp, sd_state, RKISP1_ISP_PAD_SINK_VIDEO, @@ -799,7 +570,7 @@ static void rkisp1_isp_set_sink_fmt(struct rkisp1_isp *isp, struct v4l2_mbus_framefmt *format, unsigned int which) { - const struct rkisp1_isp_mbus_info *mbus_info; + const struct rkisp1_mbus_info *mbus_info; struct v4l2_mbus_framefmt *sink_fmt; struct v4l2_rect *sink_crop; @@ -807,10 +578,10 @@ static void rkisp1_isp_set_sink_fmt(struct rkisp1_isp *isp, RKISP1_ISP_PAD_SINK_VIDEO, which); sink_fmt->code = format->code; - mbus_info = rkisp1_isp_mbus_info_get(sink_fmt->code); + mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code); if (!mbus_info || !(mbus_info->direction & RKISP1_ISP_SD_SINK)) { sink_fmt->code = RKISP1_DEF_SINK_PAD_FMT; - mbus_info = rkisp1_isp_mbus_info_get(sink_fmt->code); + mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code); } if (which == V4L2_SUBDEV_FORMAT_ACTIVE) isp->sink_fmt = mbus_info; @@ -835,7 +606,7 @@ static int rkisp1_isp_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { - struct rkisp1_isp *isp = container_of(sd, struct rkisp1_isp, sd); + struct rkisp1_isp *isp = to_rkisp1_isp(sd); mutex_lock(&isp->ops_lock); fmt->format = *rkisp1_isp_get_pad_fmt(isp, sd_state, fmt->pad, @@ -848,7 +619,7 @@ static int rkisp1_isp_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { - struct rkisp1_isp *isp = container_of(sd, struct rkisp1_isp, sd); + struct rkisp1_isp *isp = to_rkisp1_isp(sd); mutex_lock(&isp->ops_lock); if (fmt->pad == RKISP1_ISP_PAD_SINK_VIDEO) @@ -869,7 +640,7 @@ static int rkisp1_isp_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { - struct rkisp1_isp *isp = container_of(sd, struct rkisp1_isp, sd); + struct rkisp1_isp *isp = to_rkisp1_isp(sd); int ret = 0; if (sel->pad != RKISP1_ISP_PAD_SOURCE_VIDEO && @@ -909,15 +680,13 @@ static int rkisp1_isp_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { - struct rkisp1_device *rkisp1 = - container_of(sd->v4l2_dev, struct rkisp1_device, v4l2_dev); - struct rkisp1_isp *isp = container_of(sd, struct rkisp1_isp, sd); + struct rkisp1_isp *isp = to_rkisp1_isp(sd); int ret = 0; if (sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; - dev_dbg(rkisp1->dev, "%s: pad: %d sel(%d,%d)/%dx%d\n", __func__, + dev_dbg(isp->rkisp1->dev, "%s: pad: %d sel(%d,%d)/%dx%d\n", __func__, sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height); mutex_lock(&isp->ops_lock); if (sel->pad == RKISP1_ISP_PAD_SINK_VIDEO) @@ -954,77 +723,62 @@ static const struct v4l2_subdev_pad_ops rkisp1_isp_pad_ops = { * Stream operations */ -static int rkisp1_mipi_csi2_start(struct rkisp1_isp *isp, - struct rkisp1_sensor_async *sensor) -{ - struct rkisp1_device *rkisp1 = - container_of(isp->sd.v4l2_dev, struct rkisp1_device, v4l2_dev); - union phy_configure_opts opts; - struct phy_configure_opts_mipi_dphy *cfg = &opts.mipi_dphy; - s64 pixel_clock; - - if (!sensor->pixel_rate_ctrl) { - dev_warn(rkisp1->dev, "No pixel rate control in sensor subdev\n"); - return -EPIPE; - } - - pixel_clock = v4l2_ctrl_g_ctrl_int64(sensor->pixel_rate_ctrl); - if (!pixel_clock) { - dev_err(rkisp1->dev, "Invalid pixel rate value\n"); - return -EINVAL; - } - - phy_mipi_dphy_get_default_config(pixel_clock, isp->sink_fmt->bus_width, - sensor->lanes, cfg); - phy_set_mode(sensor->dphy, PHY_MODE_MIPI_DPHY); - phy_configure(sensor->dphy, &opts); - phy_power_on(sensor->dphy); - - return 0; -} - -static void rkisp1_mipi_csi2_stop(struct rkisp1_sensor_async *sensor) -{ - phy_power_off(sensor->dphy); -} - static int rkisp1_isp_s_stream(struct v4l2_subdev *sd, int enable) { - struct rkisp1_device *rkisp1 = - container_of(sd->v4l2_dev, struct rkisp1_device, v4l2_dev); - struct rkisp1_isp *isp = &rkisp1->isp; - struct v4l2_subdev *sensor_sd; - int ret = 0; + struct rkisp1_isp *isp = to_rkisp1_isp(sd); + struct rkisp1_device *rkisp1 = isp->rkisp1; + struct media_pad *source_pad; + struct media_pad *sink_pad; + enum v4l2_mbus_type mbus_type; + u32 mbus_flags; + int ret; if (!enable) { - rkisp1_isp_stop(rkisp1); - rkisp1_mipi_csi2_stop(rkisp1->active_sensor); + v4l2_subdev_call(rkisp1->source, video, s_stream, false); + rkisp1_isp_stop(isp); return 0; } - sensor_sd = rkisp1_get_remote_sensor(sd); - if (!sensor_sd) { - dev_warn(rkisp1->dev, "No link between isp and sensor\n"); - return -ENODEV; + sink_pad = &isp->pads[RKISP1_ISP_PAD_SINK_VIDEO]; + source_pad = media_pad_remote_pad_unique(sink_pad); + if (IS_ERR(source_pad)) { + dev_dbg(rkisp1->dev, "Failed to get source for ISP: %ld\n", + PTR_ERR(source_pad)); + return -EPIPE; } - rkisp1->active_sensor = container_of(sensor_sd->asd, - struct rkisp1_sensor_async, asd); + rkisp1->source = media_entity_to_v4l2_subdev(source_pad->entity); + if (!rkisp1->source) { + /* This should really not happen, so is not worth a message. */ + return -EPIPE; + } - if (rkisp1->active_sensor->mbus_type != V4L2_MBUS_CSI2_DPHY) - return -EINVAL; + if (rkisp1->source == &rkisp1->csi.sd) { + mbus_type = V4L2_MBUS_CSI2_DPHY; + mbus_flags = 0; + } else { + const struct rkisp1_sensor_async *asd; - rkisp1->isp.frame_sequence = -1; + asd = container_of(rkisp1->source->asd, + struct rkisp1_sensor_async, asd); + + mbus_type = asd->mbus_type; + mbus_flags = asd->mbus_flags; + } + + isp->frame_sequence = -1; mutex_lock(&isp->ops_lock); - ret = rkisp1_config_cif(rkisp1); + ret = rkisp1_config_cif(isp, mbus_type, mbus_flags); if (ret) goto mutex_unlock; - ret = rkisp1_mipi_csi2_start(&rkisp1->isp, rkisp1->active_sensor); - if (ret) - goto mutex_unlock; + rkisp1_isp_start(isp); - rkisp1_isp_start(rkisp1); + ret = v4l2_subdev_call(rkisp1->source, video, s_stream, true); + if (ret) { + rkisp1_isp_stop(isp); + goto mutex_unlock; + } mutex_unlock: mutex_unlock(&isp->ops_lock); @@ -1067,12 +821,14 @@ int rkisp1_isp_register(struct rkisp1_device *rkisp1) { struct v4l2_subdev_state state = { .pads = rkisp1->isp.pad_cfg - }; + }; struct rkisp1_isp *isp = &rkisp1->isp; struct media_pad *pads = isp->pads; struct v4l2_subdev *sd = &isp->sd; int ret; + isp->rkisp1 = rkisp1; + v4l2_subdev_init(sd, &rkisp1_isp_ops); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; sd->entity.ops = &rkisp1_isp_media_ops; @@ -1086,95 +842,54 @@ int rkisp1_isp_register(struct rkisp1_device *rkisp1) pads[RKISP1_ISP_PAD_SOURCE_VIDEO].flags = MEDIA_PAD_FL_SOURCE; pads[RKISP1_ISP_PAD_SOURCE_STATS].flags = MEDIA_PAD_FL_SOURCE; - isp->sink_fmt = rkisp1_isp_mbus_info_get(RKISP1_DEF_SINK_PAD_FMT); - isp->src_fmt = rkisp1_isp_mbus_info_get(RKISP1_DEF_SRC_PAD_FMT); + isp->sink_fmt = rkisp1_mbus_info_get_by_code(RKISP1_DEF_SINK_PAD_FMT); + isp->src_fmt = rkisp1_mbus_info_get_by_code(RKISP1_DEF_SRC_PAD_FMT); mutex_init(&isp->ops_lock); ret = media_entity_pads_init(&sd->entity, RKISP1_ISP_PAD_MAX, pads); if (ret) - return ret; + goto error; ret = v4l2_device_register_subdev(&rkisp1->v4l2_dev, sd); if (ret) { dev_err(rkisp1->dev, "Failed to register isp subdev\n"); - goto err_cleanup_media_entity; + goto error; } rkisp1_isp_init_config(sd, &state); + return 0; -err_cleanup_media_entity: +error: media_entity_cleanup(&sd->entity); - + mutex_destroy(&isp->ops_lock); + isp->sd.v4l2_dev = NULL; return ret; } void rkisp1_isp_unregister(struct rkisp1_device *rkisp1) { - struct v4l2_subdev *sd = &rkisp1->isp.sd; + struct rkisp1_isp *isp = &rkisp1->isp; - v4l2_device_unregister_subdev(sd); - media_entity_cleanup(&sd->entity); + if (!isp->sd.v4l2_dev) + return; + + v4l2_device_unregister_subdev(&isp->sd); + media_entity_cleanup(&isp->sd.entity); + mutex_destroy(&isp->ops_lock); } /* ---------------------------------------------------------------------------- * Interrupt handlers */ -irqreturn_t rkisp1_mipi_isr(int irq, void *ctx) -{ - struct device *dev = ctx; - struct rkisp1_device *rkisp1 = dev_get_drvdata(dev); - u32 val, status; - - status = rkisp1_read(rkisp1, RKISP1_CIF_MIPI_MIS); - if (!status) - return IRQ_NONE; - - rkisp1_write(rkisp1, status, RKISP1_CIF_MIPI_ICR); - - /* - * Disable DPHY errctrl interrupt, because this dphy - * erctrl signal is asserted until the next changes - * of line state. This time is may be too long and cpu - * is hold in this interrupt. - */ - if (status & RKISP1_CIF_MIPI_ERR_CTRL(0x0f)) { - val = rkisp1_read(rkisp1, RKISP1_CIF_MIPI_IMSC); - rkisp1_write(rkisp1, val & ~RKISP1_CIF_MIPI_ERR_CTRL(0x0f), - RKISP1_CIF_MIPI_IMSC); - rkisp1->isp.is_dphy_errctrl_disabled = true; - } - - /* - * Enable DPHY errctrl interrupt again, if mipi have receive - * the whole frame without any error. - */ - if (status == RKISP1_CIF_MIPI_FRAME_END) { - /* - * Enable DPHY errctrl interrupt again, if mipi have receive - * the whole frame without any error. - */ - if (rkisp1->isp.is_dphy_errctrl_disabled) { - val = rkisp1_read(rkisp1, RKISP1_CIF_MIPI_IMSC); - val |= RKISP1_CIF_MIPI_ERR_CTRL(0x0f); - rkisp1_write(rkisp1, val, RKISP1_CIF_MIPI_IMSC); - rkisp1->isp.is_dphy_errctrl_disabled = false; - } - } else { - rkisp1->debug.mipi_error++; - } - - return IRQ_HANDLED; -} - static void rkisp1_isp_queue_event_sof(struct rkisp1_isp *isp) { struct v4l2_event event = { .type = V4L2_EVENT_FRAME_SYNC, }; - event.u.frame_sync.frame_sequence = isp->frame_sequence; + event.u.frame_sync.frame_sequence = isp->frame_sequence; v4l2_event_queue(isp->sd.devnode, &event); } @@ -1188,7 +903,7 @@ irqreturn_t rkisp1_isp_isr(int irq, void *ctx) if (!status) return IRQ_NONE; - rkisp1_write(rkisp1, status, RKISP1_CIF_ISP_ICR); + rkisp1_write(rkisp1, RKISP1_CIF_ISP_ICR, status); /* Vertical sync signal, starting generating new frame */ if (status & RKISP1_CIF_ISP_V_START) { @@ -1208,7 +923,7 @@ irqreturn_t rkisp1_isp_isr(int irq, void *ctx) rkisp1->debug.img_stabilization_size_error++; if (isp_err & RKISP1_CIF_ISP_ERR_OUTFORM_SIZE) rkisp1->debug.outform_size_error++; - rkisp1_write(rkisp1, isp_err, RKISP1_CIF_ISP_ERR_CLR); + rkisp1_write(rkisp1, RKISP1_CIF_ISP_ERR_CLR, isp_err); } else if (status & RKISP1_CIF_ISP_DATA_LOSS) { /* keep track of data_loss in debugfs */ rkisp1->debug.data_loss++; diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c index 8f62f09e635f..9da7dc1bc690 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c @@ -37,7 +37,7 @@ rkisp1_param_set_bits(struct rkisp1_params *params, u32 reg, u32 bit_mask) u32 val; val = rkisp1_read(params->rkisp1, reg); - rkisp1_write(params->rkisp1, val | bit_mask, reg); + rkisp1_write(params->rkisp1, reg, val | bit_mask); } static inline void @@ -46,7 +46,7 @@ rkisp1_param_clear_bits(struct rkisp1_params *params, u32 reg, u32 bit_mask) u32 val; val = rkisp1_read(params->rkisp1, reg); - rkisp1_write(params->rkisp1, val & ~bit_mask, reg); + rkisp1_write(params->rkisp1, reg, val & ~bit_mask); } /* ISP BP interface function */ @@ -60,35 +60,35 @@ static void rkisp1_dpcc_config(struct rkisp1_params *params, mode = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_DPCC_MODE); mode &= RKISP1_CIF_ISP_DPCC_ENA; mode |= arg->mode & ~RKISP1_CIF_ISP_DPCC_ENA; - rkisp1_write(params->rkisp1, mode, RKISP1_CIF_ISP_DPCC_MODE); - rkisp1_write(params->rkisp1, arg->output_mode, - RKISP1_CIF_ISP_DPCC_OUTPUT_MODE); - rkisp1_write(params->rkisp1, arg->set_use, - RKISP1_CIF_ISP_DPCC_SET_USE); - - rkisp1_write(params->rkisp1, arg->methods[0].method, - RKISP1_CIF_ISP_DPCC_METHODS_SET_1); - rkisp1_write(params->rkisp1, arg->methods[1].method, - RKISP1_CIF_ISP_DPCC_METHODS_SET_2); - rkisp1_write(params->rkisp1, arg->methods[2].method, - RKISP1_CIF_ISP_DPCC_METHODS_SET_3); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPCC_MODE, mode); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPCC_OUTPUT_MODE, + arg->output_mode); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPCC_SET_USE, + arg->set_use); + + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPCC_METHODS_SET_1, + arg->methods[0].method); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPCC_METHODS_SET_2, + arg->methods[1].method); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPCC_METHODS_SET_3, + arg->methods[2].method); for (i = 0; i < RKISP1_CIF_ISP_DPCC_METHODS_MAX; i++) { - rkisp1_write(params->rkisp1, arg->methods[i].line_thresh, - RKISP1_ISP_DPCC_LINE_THRESH(i)); - rkisp1_write(params->rkisp1, arg->methods[i].line_mad_fac, - RKISP1_ISP_DPCC_LINE_MAD_FAC(i)); - rkisp1_write(params->rkisp1, arg->methods[i].pg_fac, - RKISP1_ISP_DPCC_PG_FAC(i)); - rkisp1_write(params->rkisp1, arg->methods[i].rnd_thresh, - RKISP1_ISP_DPCC_RND_THRESH(i)); - rkisp1_write(params->rkisp1, arg->methods[i].rg_fac, - RKISP1_ISP_DPCC_RG_FAC(i)); + rkisp1_write(params->rkisp1, RKISP1_ISP_DPCC_LINE_THRESH(i), + arg->methods[i].line_thresh); + rkisp1_write(params->rkisp1, RKISP1_ISP_DPCC_LINE_MAD_FAC(i), + arg->methods[i].line_mad_fac); + rkisp1_write(params->rkisp1, RKISP1_ISP_DPCC_PG_FAC(i), + arg->methods[i].pg_fac); + rkisp1_write(params->rkisp1, RKISP1_ISP_DPCC_RND_THRESH(i), + arg->methods[i].rnd_thresh); + rkisp1_write(params->rkisp1, RKISP1_ISP_DPCC_RG_FAC(i), + arg->methods[i].rg_fac); } - rkisp1_write(params->rkisp1, arg->rnd_offs, - RKISP1_CIF_ISP_DPCC_RND_OFFS); - rkisp1_write(params->rkisp1, arg->ro_limits, - RKISP1_CIF_ISP_DPCC_RO_LIMITS); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPCC_RND_OFFS, + arg->rnd_offs); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPCC_RO_LIMITS, + arg->ro_limits); } /* ISP black level subtraction interface function */ @@ -107,44 +107,44 @@ static void rkisp1_bls_config(struct rkisp1_params *params, switch (params->raw_type) { case RKISP1_RAW_BGGR: - rkisp1_write(params->rkisp1, - pval->r, RKISP1_CIF_ISP_BLS_D_FIXED); - rkisp1_write(params->rkisp1, - pval->gr, RKISP1_CIF_ISP_BLS_C_FIXED); - rkisp1_write(params->rkisp1, - pval->gb, RKISP1_CIF_ISP_BLS_B_FIXED); - rkisp1_write(params->rkisp1, - pval->b, RKISP1_CIF_ISP_BLS_A_FIXED); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_D_FIXED, + pval->r); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_C_FIXED, + pval->gr); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_B_FIXED, + pval->gb); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_A_FIXED, + pval->b); break; case RKISP1_RAW_GBRG: - rkisp1_write(params->rkisp1, - pval->r, RKISP1_CIF_ISP_BLS_C_FIXED); - rkisp1_write(params->rkisp1, - pval->gr, RKISP1_CIF_ISP_BLS_D_FIXED); - rkisp1_write(params->rkisp1, - pval->gb, RKISP1_CIF_ISP_BLS_A_FIXED); - rkisp1_write(params->rkisp1, - pval->b, RKISP1_CIF_ISP_BLS_B_FIXED); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_C_FIXED, + pval->r); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_D_FIXED, + pval->gr); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_A_FIXED, + pval->gb); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_B_FIXED, + pval->b); break; case RKISP1_RAW_GRBG: - rkisp1_write(params->rkisp1, - pval->r, RKISP1_CIF_ISP_BLS_B_FIXED); - rkisp1_write(params->rkisp1, - pval->gr, RKISP1_CIF_ISP_BLS_A_FIXED); - rkisp1_write(params->rkisp1, - pval->gb, RKISP1_CIF_ISP_BLS_D_FIXED); - rkisp1_write(params->rkisp1, - pval->b, RKISP1_CIF_ISP_BLS_C_FIXED); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_B_FIXED, + pval->r); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_A_FIXED, + pval->gr); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_D_FIXED, + pval->gb); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_C_FIXED, + pval->b); break; case RKISP1_RAW_RGGB: - rkisp1_write(params->rkisp1, - pval->r, RKISP1_CIF_ISP_BLS_A_FIXED); - rkisp1_write(params->rkisp1, - pval->gr, RKISP1_CIF_ISP_BLS_B_FIXED); - rkisp1_write(params->rkisp1, - pval->gb, RKISP1_CIF_ISP_BLS_C_FIXED); - rkisp1_write(params->rkisp1, - pval->b, RKISP1_CIF_ISP_BLS_D_FIXED); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_A_FIXED, + pval->r); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_B_FIXED, + pval->gr); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_C_FIXED, + pval->gb); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_D_FIXED, + pval->b); break; default: break; @@ -152,35 +152,35 @@ static void rkisp1_bls_config(struct rkisp1_params *params, } else { if (arg->en_windows & BIT(1)) { - rkisp1_write(params->rkisp1, arg->bls_window2.h_offs, - RKISP1_CIF_ISP_BLS_H2_START); - rkisp1_write(params->rkisp1, arg->bls_window2.h_size, - RKISP1_CIF_ISP_BLS_H2_STOP); - rkisp1_write(params->rkisp1, arg->bls_window2.v_offs, - RKISP1_CIF_ISP_BLS_V2_START); - rkisp1_write(params->rkisp1, arg->bls_window2.v_size, - RKISP1_CIF_ISP_BLS_V2_STOP); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_H2_START, + arg->bls_window2.h_offs); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_H2_STOP, + arg->bls_window2.h_size); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_V2_START, + arg->bls_window2.v_offs); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_V2_STOP, + arg->bls_window2.v_size); new_control |= RKISP1_CIF_ISP_BLS_WINDOW_2; } if (arg->en_windows & BIT(0)) { - rkisp1_write(params->rkisp1, arg->bls_window1.h_offs, - RKISP1_CIF_ISP_BLS_H1_START); - rkisp1_write(params->rkisp1, arg->bls_window1.h_size, - RKISP1_CIF_ISP_BLS_H1_STOP); - rkisp1_write(params->rkisp1, arg->bls_window1.v_offs, - RKISP1_CIF_ISP_BLS_V1_START); - rkisp1_write(params->rkisp1, arg->bls_window1.v_size, - RKISP1_CIF_ISP_BLS_V1_STOP); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_H1_START, + arg->bls_window1.h_offs); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_H1_STOP, + arg->bls_window1.h_size); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_V1_START, + arg->bls_window1.v_offs); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_V1_STOP, + arg->bls_window1.v_size); new_control |= RKISP1_CIF_ISP_BLS_WINDOW_1; } - rkisp1_write(params->rkisp1, arg->bls_samples, - RKISP1_CIF_ISP_BLS_SAMPLES); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_SAMPLES, + arg->bls_samples); new_control |= RKISP1_CIF_ISP_BLS_MODE_MEASURED; } - rkisp1_write(params->rkisp1, new_control, RKISP1_CIF_ISP_BLS_CTRL); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_CTRL, new_control); } /* ISP LS correction interface function */ @@ -196,14 +196,10 @@ rkisp1_lsc_matrix_config_v10(struct rkisp1_params *params, sram_addr = (isp_lsc_status & RKISP1_CIF_ISP_LSC_ACTIVE_TABLE) ? RKISP1_CIF_ISP_LSC_TABLE_ADDRESS_0 : RKISP1_CIF_ISP_LSC_TABLE_ADDRESS_153; - rkisp1_write(params->rkisp1, sram_addr, - RKISP1_CIF_ISP_LSC_R_TABLE_ADDR); - rkisp1_write(params->rkisp1, sram_addr, - RKISP1_CIF_ISP_LSC_GR_TABLE_ADDR); - rkisp1_write(params->rkisp1, sram_addr, - RKISP1_CIF_ISP_LSC_GB_TABLE_ADDR); - rkisp1_write(params->rkisp1, sram_addr, - RKISP1_CIF_ISP_LSC_B_TABLE_ADDR); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_R_TABLE_ADDR, sram_addr); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_GR_TABLE_ADDR, sram_addr); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_GB_TABLE_ADDR, sram_addr); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_B_TABLE_ADDR, sram_addr); /* program data tables (table size is 9 * 17 = 153) */ for (i = 0; i < RKISP1_CIF_ISP_LSC_SAMPLES_MAX; i++) { @@ -214,45 +210,45 @@ rkisp1_lsc_matrix_config_v10(struct rkisp1_params *params, for (j = 0; j < RKISP1_CIF_ISP_LSC_SAMPLES_MAX - 1; j += 2) { data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(pconfig->r_data_tbl[i][j], pconfig->r_data_tbl[i][j + 1]); - rkisp1_write(params->rkisp1, data, - RKISP1_CIF_ISP_LSC_R_TABLE_DATA); + rkisp1_write(params->rkisp1, + RKISP1_CIF_ISP_LSC_R_TABLE_DATA, data); data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(pconfig->gr_data_tbl[i][j], pconfig->gr_data_tbl[i][j + 1]); - rkisp1_write(params->rkisp1, data, - RKISP1_CIF_ISP_LSC_GR_TABLE_DATA); + rkisp1_write(params->rkisp1, + RKISP1_CIF_ISP_LSC_GR_TABLE_DATA, data); data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(pconfig->gb_data_tbl[i][j], pconfig->gb_data_tbl[i][j + 1]); - rkisp1_write(params->rkisp1, data, - RKISP1_CIF_ISP_LSC_GB_TABLE_DATA); + rkisp1_write(params->rkisp1, + RKISP1_CIF_ISP_LSC_GB_TABLE_DATA, data); data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(pconfig->b_data_tbl[i][j], pconfig->b_data_tbl[i][j + 1]); - rkisp1_write(params->rkisp1, data, - RKISP1_CIF_ISP_LSC_B_TABLE_DATA); + rkisp1_write(params->rkisp1, + RKISP1_CIF_ISP_LSC_B_TABLE_DATA, data); } data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(pconfig->r_data_tbl[i][j], 0); - rkisp1_write(params->rkisp1, data, - RKISP1_CIF_ISP_LSC_R_TABLE_DATA); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_R_TABLE_DATA, + data); data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(pconfig->gr_data_tbl[i][j], 0); - rkisp1_write(params->rkisp1, data, - RKISP1_CIF_ISP_LSC_GR_TABLE_DATA); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_GR_TABLE_DATA, + data); data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(pconfig->gb_data_tbl[i][j], 0); - rkisp1_write(params->rkisp1, data, - RKISP1_CIF_ISP_LSC_GB_TABLE_DATA); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_GB_TABLE_DATA, + data); data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V10(pconfig->b_data_tbl[i][j], 0); - rkisp1_write(params->rkisp1, data, - RKISP1_CIF_ISP_LSC_B_TABLE_DATA); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_B_TABLE_DATA, + data); } isp_lsc_table_sel = (isp_lsc_status & RKISP1_CIF_ISP_LSC_ACTIVE_TABLE) ? RKISP1_CIF_ISP_LSC_TABLE_0 : RKISP1_CIF_ISP_LSC_TABLE_1; - rkisp1_write(params->rkisp1, isp_lsc_table_sel, - RKISP1_CIF_ISP_LSC_TABLE_SEL); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_TABLE_SEL, + isp_lsc_table_sel); } static void @@ -267,10 +263,10 @@ rkisp1_lsc_matrix_config_v12(struct rkisp1_params *params, sram_addr = (isp_lsc_status & RKISP1_CIF_ISP_LSC_ACTIVE_TABLE) ? RKISP1_CIF_ISP_LSC_TABLE_ADDRESS_0 : RKISP1_CIF_ISP_LSC_TABLE_ADDRESS_153; - rkisp1_write(params->rkisp1, sram_addr, RKISP1_CIF_ISP_LSC_R_TABLE_ADDR); - rkisp1_write(params->rkisp1, sram_addr, RKISP1_CIF_ISP_LSC_GR_TABLE_ADDR); - rkisp1_write(params->rkisp1, sram_addr, RKISP1_CIF_ISP_LSC_GB_TABLE_ADDR); - rkisp1_write(params->rkisp1, sram_addr, RKISP1_CIF_ISP_LSC_B_TABLE_ADDR); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_R_TABLE_ADDR, sram_addr); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_GR_TABLE_ADDR, sram_addr); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_GB_TABLE_ADDR, sram_addr); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_B_TABLE_ADDR, sram_addr); /* program data tables (table size is 9 * 17 = 153) */ for (i = 0; i < RKISP1_CIF_ISP_LSC_SAMPLES_MAX; i++) { @@ -282,49 +278,49 @@ rkisp1_lsc_matrix_config_v12(struct rkisp1_params *params, data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V12( pconfig->r_data_tbl[i][j], pconfig->r_data_tbl[i][j + 1]); - rkisp1_write(params->rkisp1, data, - RKISP1_CIF_ISP_LSC_R_TABLE_DATA); + rkisp1_write(params->rkisp1, + RKISP1_CIF_ISP_LSC_R_TABLE_DATA, data); data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V12( pconfig->gr_data_tbl[i][j], pconfig->gr_data_tbl[i][j + 1]); - rkisp1_write(params->rkisp1, data, - RKISP1_CIF_ISP_LSC_GR_TABLE_DATA); + rkisp1_write(params->rkisp1, + RKISP1_CIF_ISP_LSC_GR_TABLE_DATA, data); data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V12( pconfig->gb_data_tbl[i][j], pconfig->gb_data_tbl[i][j + 1]); - rkisp1_write(params->rkisp1, data, - RKISP1_CIF_ISP_LSC_GB_TABLE_DATA); + rkisp1_write(params->rkisp1, + RKISP1_CIF_ISP_LSC_GB_TABLE_DATA, data); data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V12( pconfig->b_data_tbl[i][j], pconfig->b_data_tbl[i][j + 1]); - rkisp1_write(params->rkisp1, data, - RKISP1_CIF_ISP_LSC_B_TABLE_DATA); + rkisp1_write(params->rkisp1, + RKISP1_CIF_ISP_LSC_B_TABLE_DATA, data); } data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(pconfig->r_data_tbl[i][j], 0); - rkisp1_write(params->rkisp1, data, - RKISP1_CIF_ISP_LSC_R_TABLE_DATA); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_R_TABLE_DATA, + data); data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(pconfig->gr_data_tbl[i][j], 0); - rkisp1_write(params->rkisp1, data, - RKISP1_CIF_ISP_LSC_GR_TABLE_DATA); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_GR_TABLE_DATA, + data); data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(pconfig->gb_data_tbl[i][j], 0); - rkisp1_write(params->rkisp1, data, - RKISP1_CIF_ISP_LSC_GB_TABLE_DATA); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_GB_TABLE_DATA, + data); data = RKISP1_CIF_ISP_LSC_TABLE_DATA_V12(pconfig->b_data_tbl[i][j], 0); - rkisp1_write(params->rkisp1, data, - RKISP1_CIF_ISP_LSC_B_TABLE_DATA); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_B_TABLE_DATA, + data); } isp_lsc_table_sel = (isp_lsc_status & RKISP1_CIF_ISP_LSC_ACTIVE_TABLE) ? RKISP1_CIF_ISP_LSC_TABLE_0 : RKISP1_CIF_ISP_LSC_TABLE_1; - rkisp1_write(params->rkisp1, isp_lsc_table_sel, - RKISP1_CIF_ISP_LSC_TABLE_SEL); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_LSC_TABLE_SEL, + isp_lsc_table_sel); } static void rkisp1_lsc_config(struct rkisp1_params *params, @@ -343,26 +339,26 @@ static void rkisp1_lsc_config(struct rkisp1_params *params, /* program x size tables */ data = RKISP1_CIF_ISP_LSC_SECT_SIZE(arg->x_size_tbl[i * 2], arg->x_size_tbl[i * 2 + 1]); - rkisp1_write(params->rkisp1, data, - RKISP1_CIF_ISP_LSC_XSIZE_01 + i * 4); + rkisp1_write(params->rkisp1, + RKISP1_CIF_ISP_LSC_XSIZE_01 + i * 4, data); /* program x grad tables */ data = RKISP1_CIF_ISP_LSC_SECT_SIZE(arg->x_grad_tbl[i * 2], arg->x_grad_tbl[i * 2 + 1]); - rkisp1_write(params->rkisp1, data, - RKISP1_CIF_ISP_LSC_XGRAD_01 + i * 4); + rkisp1_write(params->rkisp1, + RKISP1_CIF_ISP_LSC_XGRAD_01 + i * 4, data); /* program y size tables */ data = RKISP1_CIF_ISP_LSC_SECT_SIZE(arg->y_size_tbl[i * 2], arg->y_size_tbl[i * 2 + 1]); - rkisp1_write(params->rkisp1, data, - RKISP1_CIF_ISP_LSC_YSIZE_01 + i * 4); + rkisp1_write(params->rkisp1, + RKISP1_CIF_ISP_LSC_YSIZE_01 + i * 4, data); /* program y grad tables */ data = RKISP1_CIF_ISP_LSC_SECT_SIZE(arg->y_grad_tbl[i * 2], arg->y_grad_tbl[i * 2 + 1]); - rkisp1_write(params->rkisp1, data, - RKISP1_CIF_ISP_LSC_YGRAD_01 + i * 4); + rkisp1_write(params->rkisp1, + RKISP1_CIF_ISP_LSC_YGRAD_01 + i * 4, data); } /* restore the lsc ctrl status */ @@ -383,28 +379,32 @@ static void rkisp1_flt_config(struct rkisp1_params *params, { u32 filt_mode; - rkisp1_write(params->rkisp1, - arg->thresh_bl0, RKISP1_CIF_ISP_FILT_THRESH_BL0); - rkisp1_write(params->rkisp1, - arg->thresh_bl1, RKISP1_CIF_ISP_FILT_THRESH_BL1); - rkisp1_write(params->rkisp1, - arg->thresh_sh0, RKISP1_CIF_ISP_FILT_THRESH_SH0); - rkisp1_write(params->rkisp1, - arg->thresh_sh1, RKISP1_CIF_ISP_FILT_THRESH_SH1); - rkisp1_write(params->rkisp1, arg->fac_bl0, RKISP1_CIF_ISP_FILT_FAC_BL0); - rkisp1_write(params->rkisp1, arg->fac_bl1, RKISP1_CIF_ISP_FILT_FAC_BL1); - rkisp1_write(params->rkisp1, arg->fac_mid, RKISP1_CIF_ISP_FILT_FAC_MID); - rkisp1_write(params->rkisp1, arg->fac_sh0, RKISP1_CIF_ISP_FILT_FAC_SH0); - rkisp1_write(params->rkisp1, arg->fac_sh1, RKISP1_CIF_ISP_FILT_FAC_SH1); - rkisp1_write(params->rkisp1, - arg->lum_weight, RKISP1_CIF_ISP_FILT_LUM_WEIGHT); - - rkisp1_write(params->rkisp1, + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_FILT_THRESH_BL0, + arg->thresh_bl0); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_FILT_THRESH_BL1, + arg->thresh_bl1); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_FILT_THRESH_SH0, + arg->thresh_sh0); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_FILT_THRESH_SH1, + arg->thresh_sh1); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_FILT_FAC_BL0, + arg->fac_bl0); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_FILT_FAC_BL1, + arg->fac_bl1); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_FILT_FAC_MID, + arg->fac_mid); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_FILT_FAC_SH0, + arg->fac_sh0); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_FILT_FAC_SH1, + arg->fac_sh1); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_FILT_LUM_WEIGHT, + arg->lum_weight); + + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_FILT_MODE, (arg->mode ? RKISP1_CIF_ISP_FLT_MODE_DNR : 0) | RKISP1_CIF_ISP_FLT_CHROMA_V_MODE(arg->chr_v_mode) | RKISP1_CIF_ISP_FLT_CHROMA_H_MODE(arg->chr_h_mode) | - RKISP1_CIF_ISP_FLT_GREEN_STAGE1(arg->grn_stage1), - RKISP1_CIF_ISP_FILT_MODE); + RKISP1_CIF_ISP_FLT_GREEN_STAGE1(arg->grn_stage1)); /* avoid to override the old enable value */ filt_mode = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_FILT_MODE); @@ -414,7 +414,7 @@ static void rkisp1_flt_config(struct rkisp1_params *params, filt_mode |= RKISP1_CIF_ISP_FLT_CHROMA_V_MODE(arg->chr_v_mode) | RKISP1_CIF_ISP_FLT_CHROMA_H_MODE(arg->chr_h_mode) | RKISP1_CIF_ISP_FLT_GREEN_STAGE1(arg->grn_stage1); - rkisp1_write(params->rkisp1, filt_mode, RKISP1_CIF_ISP_FILT_MODE); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_FILT_MODE, filt_mode); } /* ISP demosaic interface function */ @@ -428,7 +428,7 @@ static int rkisp1_bdm_config(struct rkisp1_params *params, bdm_th &= RKISP1_CIF_ISP_DEMOSAIC_BYPASS; bdm_th |= arg->demosaic_th & ~RKISP1_CIF_ISP_DEMOSAIC_BYPASS; /* set demosaic threshold */ - rkisp1_write(params->rkisp1, bdm_th, RKISP1_CIF_ISP_DEMOSAIC); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DEMOSAIC, bdm_th); return 0; } @@ -438,18 +438,21 @@ static void rkisp1_sdg_config(struct rkisp1_params *params, { unsigned int i; - rkisp1_write(params->rkisp1, - arg->xa_pnts.gamma_dx0, RKISP1_CIF_ISP_GAMMA_DX_LO); - rkisp1_write(params->rkisp1, - arg->xa_pnts.gamma_dx1, RKISP1_CIF_ISP_GAMMA_DX_HI); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_GAMMA_DX_LO, + arg->xa_pnts.gamma_dx0); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_GAMMA_DX_HI, + arg->xa_pnts.gamma_dx1); for (i = 0; i < RKISP1_CIF_ISP_DEGAMMA_CURVE_SIZE; i++) { - rkisp1_write(params->rkisp1, arg->curve_r.gamma_y[i], - RKISP1_CIF_ISP_GAMMA_R_Y0 + i * 4); - rkisp1_write(params->rkisp1, arg->curve_g.gamma_y[i], - RKISP1_CIF_ISP_GAMMA_G_Y0 + i * 4); - rkisp1_write(params->rkisp1, arg->curve_b.gamma_y[i], - RKISP1_CIF_ISP_GAMMA_B_Y0 + i * 4); + rkisp1_write(params->rkisp1, + RKISP1_CIF_ISP_GAMMA_R_Y0 + i * 4, + arg->curve_r.gamma_y[i]); + rkisp1_write(params->rkisp1, + RKISP1_CIF_ISP_GAMMA_G_Y0 + i * 4, + arg->curve_g.gamma_y[i]); + rkisp1_write(params->rkisp1, + RKISP1_CIF_ISP_GAMMA_B_Y0 + i * 4, + arg->curve_b.gamma_y[i]); } } @@ -461,11 +464,13 @@ static void rkisp1_goc_config_v10(struct rkisp1_params *params, rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CTRL, RKISP1_CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA); - rkisp1_write(params->rkisp1, arg->mode, RKISP1_CIF_ISP_GAMMA_OUT_MODE_V10); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_GAMMA_OUT_MODE_V10, + arg->mode); for (i = 0; i < RKISP1_CIF_ISP_GAMMA_OUT_MAX_SAMPLES_V10; i++) - rkisp1_write(params->rkisp1, arg->gamma_y[i], - RKISP1_CIF_ISP_GAMMA_OUT_Y_0_V10 + i * 4); + rkisp1_write(params->rkisp1, + RKISP1_CIF_ISP_GAMMA_OUT_Y_0_V10 + i * 4, + arg->gamma_y[i]); } static void rkisp1_goc_config_v12(struct rkisp1_params *params, @@ -476,14 +481,15 @@ static void rkisp1_goc_config_v12(struct rkisp1_params *params, rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CTRL, RKISP1_CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA); - rkisp1_write(params->rkisp1, arg->mode, RKISP1_CIF_ISP_GAMMA_OUT_MODE_V12); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_GAMMA_OUT_MODE_V12, + arg->mode); for (i = 0; i < RKISP1_CIF_ISP_GAMMA_OUT_MAX_SAMPLES_V12 / 2; i++) { value = RKISP1_CIF_ISP_GAMMA_VALUE_V12( arg->gamma_y[2 * i + 1], arg->gamma_y[2 * i]); - rkisp1_write(params->rkisp1, value, - RKISP1_CIF_ISP_GAMMA_OUT_Y_0_V12 + i * 4); + rkisp1_write(params->rkisp1, + RKISP1_CIF_ISP_GAMMA_OUT_Y_0_V12 + i * 4, value); } } @@ -495,11 +501,13 @@ static void rkisp1_ctk_config(struct rkisp1_params *params, for (i = 0; i < 3; i++) for (j = 0; j < 3; j++) - rkisp1_write(params->rkisp1, arg->coeff[i][j], - RKISP1_CIF_ISP_CT_COEFF_0 + 4 * k++); + rkisp1_write(params->rkisp1, + RKISP1_CIF_ISP_CT_COEFF_0 + 4 * k++, + arg->coeff[i][j]); for (i = 0; i < 3; i++) - rkisp1_write(params->rkisp1, arg->ct_offset[i], - RKISP1_CIF_ISP_CT_OFFSET_R + i * 4); + rkisp1_write(params->rkisp1, + RKISP1_CIF_ISP_CT_OFFSET_R + i * 4, + arg->ct_offset[i]); } static void rkisp1_ctk_enable(struct rkisp1_params *params, bool en) @@ -508,19 +516,19 @@ static void rkisp1_ctk_enable(struct rkisp1_params *params, bool en) return; /* Write back the default values. */ - rkisp1_write(params->rkisp1, 0x80, RKISP1_CIF_ISP_CT_COEFF_0); - rkisp1_write(params->rkisp1, 0, RKISP1_CIF_ISP_CT_COEFF_1); - rkisp1_write(params->rkisp1, 0, RKISP1_CIF_ISP_CT_COEFF_2); - rkisp1_write(params->rkisp1, 0, RKISP1_CIF_ISP_CT_COEFF_3); - rkisp1_write(params->rkisp1, 0x80, RKISP1_CIF_ISP_CT_COEFF_4); - rkisp1_write(params->rkisp1, 0, RKISP1_CIF_ISP_CT_COEFF_5); - rkisp1_write(params->rkisp1, 0, RKISP1_CIF_ISP_CT_COEFF_6); - rkisp1_write(params->rkisp1, 0, RKISP1_CIF_ISP_CT_COEFF_7); - rkisp1_write(params->rkisp1, 0x80, RKISP1_CIF_ISP_CT_COEFF_8); - - rkisp1_write(params->rkisp1, 0, RKISP1_CIF_ISP_CT_OFFSET_R); - rkisp1_write(params->rkisp1, 0, RKISP1_CIF_ISP_CT_OFFSET_G); - rkisp1_write(params->rkisp1, 0, RKISP1_CIF_ISP_CT_OFFSET_B); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CT_COEFF_0, 0x80); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CT_COEFF_1, 0); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CT_COEFF_2, 0); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CT_COEFF_3, 0); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CT_COEFF_4, 0x80); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CT_COEFF_5, 0); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CT_COEFF_6, 0); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CT_COEFF_7, 0); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CT_COEFF_8, 0x80); + + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CT_OFFSET_R, 0); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CT_OFFSET_G, 0); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CT_OFFSET_B, 0); } /* ISP White Balance Mode */ @@ -531,15 +539,15 @@ static void rkisp1_awb_meas_config_v10(struct rkisp1_params *params, /* based on the mode,configure the awb module */ if (arg->awb_mode == RKISP1_CIF_ISP_AWB_MODE_YCBCR) { /* Reference Cb and Cr */ - rkisp1_write(params->rkisp1, + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_REF_V10, RKISP1_CIF_ISP_AWB_REF_CR_SET(arg->awb_ref_cr) | - arg->awb_ref_cb, RKISP1_CIF_ISP_AWB_REF_V10); + arg->awb_ref_cb); /* Yc Threshold */ - rkisp1_write(params->rkisp1, + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_THRESH_V10, RKISP1_CIF_ISP_AWB_MAX_Y_SET(arg->max_y) | RKISP1_CIF_ISP_AWB_MIN_Y_SET(arg->min_y) | RKISP1_CIF_ISP_AWB_MAX_CS_SET(arg->max_csum) | - arg->min_c, RKISP1_CIF_ISP_AWB_THRESH_V10); + arg->min_c); } reg_val = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_AWB_PROP_V10); @@ -547,21 +555,21 @@ static void rkisp1_awb_meas_config_v10(struct rkisp1_params *params, reg_val |= RKISP1_CIF_ISP_AWB_YMAX_CMP_EN; else reg_val &= ~RKISP1_CIF_ISP_AWB_YMAX_CMP_EN; - rkisp1_write(params->rkisp1, reg_val, RKISP1_CIF_ISP_AWB_PROP_V10); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_PROP_V10, reg_val); /* window offset */ - rkisp1_write(params->rkisp1, - arg->awb_wnd.v_offs, RKISP1_CIF_ISP_AWB_WND_V_OFFS_V10); - rkisp1_write(params->rkisp1, - arg->awb_wnd.h_offs, RKISP1_CIF_ISP_AWB_WND_H_OFFS_V10); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_WND_V_OFFS_V10, + arg->awb_wnd.v_offs); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_WND_H_OFFS_V10, + arg->awb_wnd.h_offs); /* AWB window size */ - rkisp1_write(params->rkisp1, - arg->awb_wnd.v_size, RKISP1_CIF_ISP_AWB_WND_V_SIZE_V10); - rkisp1_write(params->rkisp1, - arg->awb_wnd.h_size, RKISP1_CIF_ISP_AWB_WND_H_SIZE_V10); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_WND_V_SIZE_V10, + arg->awb_wnd.v_size); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_WND_H_SIZE_V10, + arg->awb_wnd.h_size); /* Number of frames */ - rkisp1_write(params->rkisp1, - arg->frames, RKISP1_CIF_ISP_AWB_FRAMES_V10); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_FRAMES_V10, + arg->frames); } static void rkisp1_awb_meas_config_v12(struct rkisp1_params *params, @@ -571,15 +579,15 @@ static void rkisp1_awb_meas_config_v12(struct rkisp1_params *params, /* based on the mode,configure the awb module */ if (arg->awb_mode == RKISP1_CIF_ISP_AWB_MODE_YCBCR) { /* Reference Cb and Cr */ - rkisp1_write(params->rkisp1, + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_REF_V12, RKISP1_CIF_ISP_AWB_REF_CR_SET(arg->awb_ref_cr) | - arg->awb_ref_cb, RKISP1_CIF_ISP_AWB_REF_V12); + arg->awb_ref_cb); /* Yc Threshold */ - rkisp1_write(params->rkisp1, + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_THRESH_V12, RKISP1_CIF_ISP_AWB_MAX_Y_SET(arg->max_y) | RKISP1_CIF_ISP_AWB_MIN_Y_SET(arg->min_y) | RKISP1_CIF_ISP_AWB_MAX_CS_SET(arg->max_csum) | - arg->min_c, RKISP1_CIF_ISP_AWB_THRESH_V12); + arg->min_c); } reg_val = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_AWB_PROP_V12); @@ -589,18 +597,14 @@ static void rkisp1_awb_meas_config_v12(struct rkisp1_params *params, reg_val &= ~RKISP1_CIF_ISP_AWB_YMAX_CMP_EN; reg_val &= ~RKISP1_CIF_ISP_AWB_SET_FRAMES_MASK_V12; reg_val |= RKISP1_CIF_ISP_AWB_SET_FRAMES_V12(arg->frames); - rkisp1_write(params->rkisp1, reg_val, RKISP1_CIF_ISP_AWB_PROP_V12); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_PROP_V12, reg_val); /* window offset */ - rkisp1_write(params->rkisp1, - arg->awb_wnd.v_offs << 16 | - arg->awb_wnd.h_offs, - RKISP1_CIF_ISP_AWB_OFFS_V12); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_OFFS_V12, + arg->awb_wnd.v_offs << 16 | arg->awb_wnd.h_offs); /* AWB window size */ - rkisp1_write(params->rkisp1, - arg->awb_wnd.v_size << 16 | - arg->awb_wnd.h_size, - RKISP1_CIF_ISP_AWB_SIZE_V12); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_SIZE_V12, + arg->awb_wnd.v_size << 16 | arg->awb_wnd.h_size); } static void @@ -619,14 +623,15 @@ rkisp1_awb_meas_enable_v10(struct rkisp1_params *params, else reg_val |= RKISP1_CIF_ISP_AWB_MODE_YCBCR_EN; - rkisp1_write(params->rkisp1, reg_val, RKISP1_CIF_ISP_AWB_PROP_V10); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_PROP_V10, + reg_val); /* Measurements require AWB block be active. */ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL, RKISP1_CIF_ISP_CTRL_ISP_AWB_ENA); } else { - rkisp1_write(params->rkisp1, - reg_val, RKISP1_CIF_ISP_AWB_PROP_V10); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_PROP_V10, + reg_val); rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CTRL, RKISP1_CIF_ISP_CTRL_ISP_AWB_ENA); } @@ -648,14 +653,15 @@ rkisp1_awb_meas_enable_v12(struct rkisp1_params *params, else reg_val |= RKISP1_CIF_ISP_AWB_MODE_YCBCR_EN; - rkisp1_write(params->rkisp1, reg_val, RKISP1_CIF_ISP_AWB_PROP_V12); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_PROP_V12, + reg_val); /* Measurements require AWB block be active. */ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL, RKISP1_CIF_ISP_CTRL_ISP_AWB_ENA); } else { - rkisp1_write(params->rkisp1, - reg_val, RKISP1_CIF_ISP_AWB_PROP_V12); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_PROP_V12, + reg_val); rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CTRL, RKISP1_CIF_ISP_CTRL_ISP_AWB_ENA); } @@ -665,26 +671,26 @@ static void rkisp1_awb_gain_config_v10(struct rkisp1_params *params, const struct rkisp1_cif_isp_awb_gain_config *arg) { - rkisp1_write(params->rkisp1, + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_GAIN_G_V10, RKISP1_CIF_ISP_AWB_GAIN_R_SET(arg->gain_green_r) | - arg->gain_green_b, RKISP1_CIF_ISP_AWB_GAIN_G_V10); + arg->gain_green_b); - rkisp1_write(params->rkisp1, + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_GAIN_RB_V10, RKISP1_CIF_ISP_AWB_GAIN_R_SET(arg->gain_red) | - arg->gain_blue, RKISP1_CIF_ISP_AWB_GAIN_RB_V10); + arg->gain_blue); } static void rkisp1_awb_gain_config_v12(struct rkisp1_params *params, const struct rkisp1_cif_isp_awb_gain_config *arg) { - rkisp1_write(params->rkisp1, + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_GAIN_G_V12, RKISP1_CIF_ISP_AWB_GAIN_R_SET(arg->gain_green_r) | - arg->gain_green_b, RKISP1_CIF_ISP_AWB_GAIN_G_V12); + arg->gain_green_b); - rkisp1_write(params->rkisp1, + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AWB_GAIN_RB_V12, RKISP1_CIF_ISP_AWB_GAIN_R_SET(arg->gain_red) | - arg->gain_blue, RKISP1_CIF_ISP_AWB_GAIN_RB_V12); + arg->gain_blue); } static void rkisp1_aec_config_v10(struct rkisp1_params *params, @@ -700,24 +706,22 @@ static void rkisp1_aec_config_v10(struct rkisp1_params *params, exp_ctrl |= RKISP1_CIF_ISP_EXP_CTRL_AUTOSTOP; if (arg->mode == RKISP1_CIF_ISP_EXP_MEASURING_MODE_1) exp_ctrl |= RKISP1_CIF_ISP_EXP_CTRL_MEASMODE_1; - rkisp1_write(params->rkisp1, exp_ctrl, RKISP1_CIF_ISP_EXP_CTRL); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_EXP_CTRL, exp_ctrl); - rkisp1_write(params->rkisp1, - arg->meas_window.h_offs, RKISP1_CIF_ISP_EXP_H_OFFSET_V10); - rkisp1_write(params->rkisp1, - arg->meas_window.v_offs, RKISP1_CIF_ISP_EXP_V_OFFSET_V10); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_EXP_H_OFFSET_V10, + arg->meas_window.h_offs); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_EXP_V_OFFSET_V10, + arg->meas_window.v_offs); block_hsize = arg->meas_window.h_size / RKISP1_CIF_ISP_EXP_COLUMN_NUM_V10 - 1; block_vsize = arg->meas_window.v_size / RKISP1_CIF_ISP_EXP_ROW_NUM_V10 - 1; - rkisp1_write(params->rkisp1, - RKISP1_CIF_ISP_EXP_H_SIZE_SET_V10(block_hsize), - RKISP1_CIF_ISP_EXP_H_SIZE_V10); - rkisp1_write(params->rkisp1, - RKISP1_CIF_ISP_EXP_V_SIZE_SET_V10(block_vsize), - RKISP1_CIF_ISP_EXP_V_SIZE_V10); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_EXP_H_SIZE_V10, + RKISP1_CIF_ISP_EXP_H_SIZE_SET_V10(block_hsize)); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_EXP_V_SIZE_V10, + RKISP1_CIF_ISP_EXP_V_SIZE_SET_V10(block_vsize)); } static void rkisp1_aec_config_v12(struct rkisp1_params *params, @@ -736,20 +740,18 @@ static void rkisp1_aec_config_v12(struct rkisp1_params *params, if (arg->mode == RKISP1_CIF_ISP_EXP_MEASURING_MODE_1) exp_ctrl |= RKISP1_CIF_ISP_EXP_CTRL_MEASMODE_1; exp_ctrl |= RKISP1_CIF_ISP_EXP_CTRL_WNDNUM_SET_V12(wnd_num_idx); - rkisp1_write(params->rkisp1, exp_ctrl, RKISP1_CIF_ISP_EXP_CTRL); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_EXP_CTRL, exp_ctrl); - rkisp1_write(params->rkisp1, + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_EXP_OFFS_V12, RKISP1_CIF_ISP_EXP_V_OFFSET_SET_V12(arg->meas_window.v_offs) | - RKISP1_CIF_ISP_EXP_H_OFFSET_SET_V12(arg->meas_window.h_offs), - RKISP1_CIF_ISP_EXP_OFFS_V12); + RKISP1_CIF_ISP_EXP_H_OFFSET_SET_V12(arg->meas_window.h_offs)); block_hsize = arg->meas_window.h_size / ae_wnd_num[wnd_num_idx] - 1; block_vsize = arg->meas_window.v_size / ae_wnd_num[wnd_num_idx] - 1; - rkisp1_write(params->rkisp1, + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_EXP_SIZE_V12, RKISP1_CIF_ISP_EXP_V_SIZE_SET_V12(block_vsize) | - RKISP1_CIF_ISP_EXP_H_SIZE_SET_V12(block_hsize), - RKISP1_CIF_ISP_EXP_SIZE_V12); + RKISP1_CIF_ISP_EXP_H_SIZE_SET_V12(block_hsize)); } static void rkisp1_cproc_config(struct rkisp1_params *params, @@ -762,11 +764,12 @@ static void rkisp1_cproc_config(struct rkisp1_params *params, u32 effect = cur_ie_config->effect; u32 quantization = params->quantization; - rkisp1_write(params->rkisp1, arg->contrast, RKISP1_CIF_C_PROC_CONTRAST); - rkisp1_write(params->rkisp1, arg->hue, RKISP1_CIF_C_PROC_HUE); - rkisp1_write(params->rkisp1, arg->sat, RKISP1_CIF_C_PROC_SATURATION); - rkisp1_write(params->rkisp1, arg->brightness, - RKISP1_CIF_C_PROC_BRIGHTNESS); + rkisp1_write(params->rkisp1, RKISP1_CIF_C_PROC_CONTRAST, + arg->contrast); + rkisp1_write(params->rkisp1, RKISP1_CIF_C_PROC_HUE, arg->hue); + rkisp1_write(params->rkisp1, RKISP1_CIF_C_PROC_SATURATION, arg->sat); + rkisp1_write(params->rkisp1, RKISP1_CIF_C_PROC_BRIGHTNESS, + arg->brightness); if (quantization != V4L2_QUANTIZATION_FULL_RANGE || effect != V4L2_COLORFX_NONE) { @@ -802,31 +805,29 @@ static void rkisp1_hst_config_v10(struct rkisp1_params *params, hist_prop = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_HIST_PROP_V10); hist_prop &= RKISP1_CIF_ISP_HIST_PROP_MODE_MASK_V10; hist_prop |= RKISP1_CIF_ISP_HIST_PREDIV_SET_V10(arg->histogram_predivider); - rkisp1_write(params->rkisp1, hist_prop, RKISP1_CIF_ISP_HIST_PROP_V10); - rkisp1_write(params->rkisp1, - arg->meas_window.h_offs, - RKISP1_CIF_ISP_HIST_H_OFFS_V10); - rkisp1_write(params->rkisp1, - arg->meas_window.v_offs, - RKISP1_CIF_ISP_HIST_V_OFFS_V10); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_HIST_PROP_V10, hist_prop); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_HIST_H_OFFS_V10, + arg->meas_window.h_offs); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_HIST_V_OFFS_V10, + arg->meas_window.v_offs); block_hsize = arg->meas_window.h_size / RKISP1_CIF_ISP_HIST_COLUMN_NUM_V10 - 1; block_vsize = arg->meas_window.v_size / RKISP1_CIF_ISP_HIST_ROW_NUM_V10 - 1; - rkisp1_write(params->rkisp1, block_hsize, RKISP1_CIF_ISP_HIST_H_SIZE_V10); - rkisp1_write(params->rkisp1, block_vsize, RKISP1_CIF_ISP_HIST_V_SIZE_V10); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_HIST_H_SIZE_V10, + block_hsize); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_HIST_V_SIZE_V10, + block_vsize); weight = arg->hist_weight; for (i = 0; i < ARRAY_SIZE(hist_weight_regs); ++i, weight += 4) - rkisp1_write(params->rkisp1, - RKISP1_CIF_ISP_HIST_WEIGHT_SET_V10(weight[0], - weight[1], - weight[2], - weight[3]), - hist_weight_regs[i]); + rkisp1_write(params->rkisp1, hist_weight_regs[i], + RKISP1_CIF_ISP_HIST_WEIGHT_SET_V10(weight[0], weight[1], + weight[2], weight[3])); - rkisp1_write(params->rkisp1, weight[0] & 0x1F, RKISP1_CIF_ISP_HIST_WEIGHT_44_V10); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_HIST_WEIGHT_44_V10, + weight[0] & 0x1F); } static void rkisp1_hst_config_v12(struct rkisp1_params *params, @@ -852,18 +853,16 @@ static void rkisp1_hst_config_v12(struct rkisp1_params *params, RKISP1_CIF_ISP_HIST_CTRL_AUTOSTOP_SET_V12(0) | RKISP1_CIF_ISP_HIST_CTRL_WNDNUM_SET_V12(1) | RKISP1_CIF_ISP_HIST_CTRL_STEPSIZE_SET_V12(arg->histogram_predivider); - rkisp1_write(params->rkisp1, hist_ctrl, RKISP1_CIF_ISP_HIST_CTRL_V12); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_HIST_CTRL_V12, hist_ctrl); - rkisp1_write(params->rkisp1, + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_HIST_OFFS_V12, RKISP1_CIF_ISP_HIST_OFFS_SET_V12(arg->meas_window.h_offs, - arg->meas_window.v_offs), - RKISP1_CIF_ISP_HIST_OFFS_V12); + arg->meas_window.v_offs)); block_hsize = arg->meas_window.h_size / hist_wnd_num[wnd_num_idx] - 1; block_vsize = arg->meas_window.v_size / hist_wnd_num[wnd_num_idx] - 1; - rkisp1_write(params->rkisp1, - RKISP1_CIF_ISP_HIST_SIZE_SET_V12(block_hsize, block_vsize), - RKISP1_CIF_ISP_HIST_SIZE_V12); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_HIST_SIZE_V12, + RKISP1_CIF_ISP_HIST_SIZE_SET_V12(block_hsize, block_vsize)); for (i = 0; i < hist_wnd_num[wnd_num_idx]; i++) { for (j = 0; j < hist_wnd_num[wnd_num_idx]; j++) { @@ -879,12 +878,12 @@ static void rkisp1_hst_config_v12(struct rkisp1_params *params, weight15x15[4 * i + 1], weight15x15[4 * i + 2], weight15x15[4 * i + 3]); - rkisp1_write(params->rkisp1, value, - RKISP1_CIF_ISP_HIST_WEIGHT_V12 + 4 * i); + rkisp1_write(params->rkisp1, + RKISP1_CIF_ISP_HIST_WEIGHT_V12 + 4 * i, value); } value = RKISP1_CIF_ISP_HIST_WEIGHT_SET_V12(weight15x15[4 * i + 0], 0, 0, 0); - rkisp1_write(params->rkisp1, value, - RKISP1_CIF_ISP_HIST_WEIGHT_V12 + 4 * i); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_HIST_WEIGHT_V12 + 4 * i, + value); } static void @@ -938,22 +937,20 @@ static void rkisp1_afm_config_v10(struct rkisp1_params *params, RKISP1_CIF_ISP_AFM_ENA); for (i = 0; i < num_of_win; i++) { - rkisp1_write(params->rkisp1, + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AFM_LT_A + i * 8, RKISP1_CIF_ISP_AFM_WINDOW_X(arg->afm_win[i].h_offs) | - RKISP1_CIF_ISP_AFM_WINDOW_Y(arg->afm_win[i].v_offs), - RKISP1_CIF_ISP_AFM_LT_A + i * 8); - rkisp1_write(params->rkisp1, + RKISP1_CIF_ISP_AFM_WINDOW_Y(arg->afm_win[i].v_offs)); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AFM_RB_A + i * 8, RKISP1_CIF_ISP_AFM_WINDOW_X(arg->afm_win[i].h_size + arg->afm_win[i].h_offs) | RKISP1_CIF_ISP_AFM_WINDOW_Y(arg->afm_win[i].v_size + - arg->afm_win[i].v_offs), - RKISP1_CIF_ISP_AFM_RB_A + i * 8); + arg->afm_win[i].v_offs)); } - rkisp1_write(params->rkisp1, arg->thres, RKISP1_CIF_ISP_AFM_THRES); - rkisp1_write(params->rkisp1, arg->var_shift, - RKISP1_CIF_ISP_AFM_VAR_SHIFT); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AFM_THRES, arg->thres); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AFM_VAR_SHIFT, + arg->var_shift); /* restore afm status */ - rkisp1_write(params->rkisp1, afm_ctrl, RKISP1_CIF_ISP_AFM_CTRL); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AFM_CTRL, afm_ctrl); } static void rkisp1_afm_config_v12(struct rkisp1_params *params, @@ -970,29 +967,26 @@ static void rkisp1_afm_config_v12(struct rkisp1_params *params, RKISP1_CIF_ISP_AFM_ENA); for (i = 0; i < num_of_win; i++) { - rkisp1_write(params->rkisp1, + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AFM_LT_A + i * 8, RKISP1_CIF_ISP_AFM_WINDOW_X(arg->afm_win[i].h_offs) | - RKISP1_CIF_ISP_AFM_WINDOW_Y(arg->afm_win[i].v_offs), - RKISP1_CIF_ISP_AFM_LT_A + i * 8); - rkisp1_write(params->rkisp1, + RKISP1_CIF_ISP_AFM_WINDOW_Y(arg->afm_win[i].v_offs)); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AFM_RB_A + i * 8, RKISP1_CIF_ISP_AFM_WINDOW_X(arg->afm_win[i].h_size + arg->afm_win[i].h_offs) | RKISP1_CIF_ISP_AFM_WINDOW_Y(arg->afm_win[i].v_size + - arg->afm_win[i].v_offs), - RKISP1_CIF_ISP_AFM_RB_A + i * 8); + arg->afm_win[i].v_offs)); } - rkisp1_write(params->rkisp1, arg->thres, RKISP1_CIF_ISP_AFM_THRES); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AFM_THRES, arg->thres); lum_var_shift = RKISP1_CIF_ISP_AFM_GET_LUM_SHIFT_a_V12(arg->var_shift); afm_var_shift = RKISP1_CIF_ISP_AFM_GET_AFM_SHIFT_a_V12(arg->var_shift); - rkisp1_write(params->rkisp1, + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AFM_VAR_SHIFT, RKISP1_CIF_ISP_AFM_SET_SHIFT_a_V12(lum_var_shift, afm_var_shift) | RKISP1_CIF_ISP_AFM_SET_SHIFT_b_V12(lum_var_shift, afm_var_shift) | - RKISP1_CIF_ISP_AFM_SET_SHIFT_c_V12(lum_var_shift, afm_var_shift), - RKISP1_CIF_ISP_AFM_VAR_SHIFT); + RKISP1_CIF_ISP_AFM_SET_SHIFT_c_V12(lum_var_shift, afm_var_shift)); /* restore afm status */ - rkisp1_write(params->rkisp1, afm_ctrl, RKISP1_CIF_ISP_AFM_CTRL); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_AFM_CTRL, afm_ctrl); } static void rkisp1_ie_config(struct rkisp1_params *params, @@ -1011,8 +1005,8 @@ static void rkisp1_ie_config(struct rkisp1_params *params, eff_ctrl |= RKISP1_CIF_IMG_EFF_CTRL_MODE_SEPIA; break; case V4L2_COLORFX_SET_CBCR: - rkisp1_write(params->rkisp1, arg->eff_tint, - RKISP1_CIF_IMG_EFF_TINT); + rkisp1_write(params->rkisp1, RKISP1_CIF_IMG_EFF_TINT, + arg->eff_tint); eff_ctrl |= RKISP1_CIF_IMG_EFF_CTRL_MODE_SEPIA; break; /* @@ -1021,26 +1015,26 @@ static void rkisp1_ie_config(struct rkisp1_params *params, */ case V4L2_COLORFX_AQUA: eff_ctrl |= RKISP1_CIF_IMG_EFF_CTRL_MODE_COLOR_SEL; - rkisp1_write(params->rkisp1, arg->color_sel, - RKISP1_CIF_IMG_EFF_COLOR_SEL); + rkisp1_write(params->rkisp1, RKISP1_CIF_IMG_EFF_COLOR_SEL, + arg->color_sel); break; case V4L2_COLORFX_EMBOSS: eff_ctrl |= RKISP1_CIF_IMG_EFF_CTRL_MODE_EMBOSS; - rkisp1_write(params->rkisp1, arg->eff_mat_1, - RKISP1_CIF_IMG_EFF_MAT_1); - rkisp1_write(params->rkisp1, arg->eff_mat_2, - RKISP1_CIF_IMG_EFF_MAT_2); - rkisp1_write(params->rkisp1, arg->eff_mat_3, - RKISP1_CIF_IMG_EFF_MAT_3); + rkisp1_write(params->rkisp1, RKISP1_CIF_IMG_EFF_MAT_1, + arg->eff_mat_1); + rkisp1_write(params->rkisp1, RKISP1_CIF_IMG_EFF_MAT_2, + arg->eff_mat_2); + rkisp1_write(params->rkisp1, RKISP1_CIF_IMG_EFF_MAT_3, + arg->eff_mat_3); break; case V4L2_COLORFX_SKETCH: eff_ctrl |= RKISP1_CIF_IMG_EFF_CTRL_MODE_SKETCH; - rkisp1_write(params->rkisp1, arg->eff_mat_3, - RKISP1_CIF_IMG_EFF_MAT_3); - rkisp1_write(params->rkisp1, arg->eff_mat_4, - RKISP1_CIF_IMG_EFF_MAT_4); - rkisp1_write(params->rkisp1, arg->eff_mat_5, - RKISP1_CIF_IMG_EFF_MAT_5); + rkisp1_write(params->rkisp1, RKISP1_CIF_IMG_EFF_MAT_3, + arg->eff_mat_3); + rkisp1_write(params->rkisp1, RKISP1_CIF_IMG_EFF_MAT_4, + arg->eff_mat_4); + rkisp1_write(params->rkisp1, RKISP1_CIF_IMG_EFF_MAT_5, + arg->eff_mat_5); break; case V4L2_COLORFX_BW: eff_ctrl |= RKISP1_CIF_IMG_EFF_CTRL_MODE_BLACKWHITE; @@ -1052,23 +1046,23 @@ static void rkisp1_ie_config(struct rkisp1_params *params, break; } - rkisp1_write(params->rkisp1, eff_ctrl, RKISP1_CIF_IMG_EFF_CTRL); + rkisp1_write(params->rkisp1, RKISP1_CIF_IMG_EFF_CTRL, eff_ctrl); } static void rkisp1_ie_enable(struct rkisp1_params *params, bool en) { if (en) { - rkisp1_param_set_bits(params, RKISP1_CIF_ICCL, - RKISP1_CIF_ICCL_IE_CLK); - rkisp1_write(params->rkisp1, RKISP1_CIF_IMG_EFF_CTRL_ENABLE, - RKISP1_CIF_IMG_EFF_CTRL); + rkisp1_param_set_bits(params, RKISP1_CIF_VI_ICCL, + RKISP1_CIF_VI_ICCL_IE_CLK); + rkisp1_write(params->rkisp1, RKISP1_CIF_IMG_EFF_CTRL, + RKISP1_CIF_IMG_EFF_CTRL_ENABLE); rkisp1_param_set_bits(params, RKISP1_CIF_IMG_EFF_CTRL, RKISP1_CIF_IMG_EFF_CTRL_CFG_UPD); } else { rkisp1_param_clear_bits(params, RKISP1_CIF_IMG_EFF_CTRL, RKISP1_CIF_IMG_EFF_CTRL_ENABLE); - rkisp1_param_clear_bits(params, RKISP1_CIF_ICCL, - RKISP1_CIF_ICCL_IE_CLK); + rkisp1_param_clear_bits(params, RKISP1_CIF_VI_ICCL, + RKISP1_CIF_VI_ICCL_IE_CLK); } } @@ -1088,16 +1082,18 @@ static void rkisp1_csm_config(struct rkisp1_params *params, bool full_range) if (full_range) { for (i = 0; i < ARRAY_SIZE(full_range_coeff); i++) - rkisp1_write(params->rkisp1, full_range_coeff[i], - RKISP1_CIF_ISP_CC_COEFF_0 + i * 4); + rkisp1_write(params->rkisp1, + RKISP1_CIF_ISP_CC_COEFF_0 + i * 4, + full_range_coeff[i]); rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL, RKISP1_CIF_ISP_CTRL_ISP_CSM_Y_FULL_ENA | RKISP1_CIF_ISP_CTRL_ISP_CSM_C_FULL_ENA); } else { for (i = 0; i < ARRAY_SIZE(limited_range_coeff); i++) - rkisp1_write(params->rkisp1, limited_range_coeff[i], - RKISP1_CIF_ISP_CC_COEFF_0 + i * 4); + rkisp1_write(params->rkisp1, + RKISP1_CIF_ISP_CC_COEFF_0 + i * 4, + limited_range_coeff[i]); rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CTRL, RKISP1_CIF_ISP_CTRL_ISP_CSM_Y_FULL_ENA | @@ -1152,52 +1148,53 @@ static void rkisp1_dpf_config(struct rkisp1_params *params, rkisp1_param_set_bits(params, RKISP1_CIF_ISP_DPF_MODE, isp_dpf_mode); - rkisp1_write(params->rkisp1, arg->gain.nf_b_gain, - RKISP1_CIF_ISP_DPF_NF_GAIN_B); - rkisp1_write(params->rkisp1, arg->gain.nf_r_gain, - RKISP1_CIF_ISP_DPF_NF_GAIN_R); - rkisp1_write(params->rkisp1, arg->gain.nf_gb_gain, - RKISP1_CIF_ISP_DPF_NF_GAIN_GB); - rkisp1_write(params->rkisp1, arg->gain.nf_gr_gain, - RKISP1_CIF_ISP_DPF_NF_GAIN_GR); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPF_NF_GAIN_B, + arg->gain.nf_b_gain); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPF_NF_GAIN_R, + arg->gain.nf_r_gain); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPF_NF_GAIN_GB, + arg->gain.nf_gb_gain); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPF_NF_GAIN_GR, + arg->gain.nf_gr_gain); for (i = 0; i < RKISP1_CIF_ISP_DPF_MAX_NLF_COEFFS; i++) { - rkisp1_write(params->rkisp1, arg->nll.coeff[i], - RKISP1_CIF_ISP_DPF_NULL_COEFF_0 + i * 4); + rkisp1_write(params->rkisp1, + RKISP1_CIF_ISP_DPF_NULL_COEFF_0 + i * 4, + arg->nll.coeff[i]); } spatial_coeff = arg->g_flt.spatial_coeff[0] | (arg->g_flt.spatial_coeff[1] << 8) | (arg->g_flt.spatial_coeff[2] << 16) | (arg->g_flt.spatial_coeff[3] << 24); - rkisp1_write(params->rkisp1, spatial_coeff, - RKISP1_CIF_ISP_DPF_S_WEIGHT_G_1_4); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPF_S_WEIGHT_G_1_4, + spatial_coeff); spatial_coeff = arg->g_flt.spatial_coeff[4] | (arg->g_flt.spatial_coeff[5] << 8); - rkisp1_write(params->rkisp1, spatial_coeff, - RKISP1_CIF_ISP_DPF_S_WEIGHT_G_5_6); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPF_S_WEIGHT_G_5_6, + spatial_coeff); spatial_coeff = arg->rb_flt.spatial_coeff[0] | (arg->rb_flt.spatial_coeff[1] << 8) | (arg->rb_flt.spatial_coeff[2] << 16) | (arg->rb_flt.spatial_coeff[3] << 24); - rkisp1_write(params->rkisp1, spatial_coeff, - RKISP1_CIF_ISP_DPF_S_WEIGHT_RB_1_4); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPF_S_WEIGHT_RB_1_4, + spatial_coeff); spatial_coeff = arg->rb_flt.spatial_coeff[4] | (arg->rb_flt.spatial_coeff[5] << 8); - rkisp1_write(params->rkisp1, spatial_coeff, - RKISP1_CIF_ISP_DPF_S_WEIGHT_RB_5_6); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPF_S_WEIGHT_RB_5_6, + spatial_coeff); } static void rkisp1_dpf_strength_config(struct rkisp1_params *params, const struct rkisp1_cif_isp_dpf_strength_config *arg) { - rkisp1_write(params->rkisp1, arg->b, RKISP1_CIF_ISP_DPF_STRENGTH_B); - rkisp1_write(params->rkisp1, arg->g, RKISP1_CIF_ISP_DPF_STRENGTH_G); - rkisp1_write(params->rkisp1, arg->r, RKISP1_CIF_ISP_DPF_STRENGTH_R); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPF_STRENGTH_B, arg->b); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPF_STRENGTH_G, arg->g); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPF_STRENGTH_R, arg->r); } static void @@ -1804,7 +1801,7 @@ static void rkisp1_init_params(struct rkisp1_params *params) params->vdev_fmt.fmt.meta.buffersize = sizeof(struct rkisp1_params_cfg); - if (params->rkisp1->media_dev.hw_revision == RKISP1_V12) + if (params->rkisp1->info->isp_ver == RKISP1_V12) params->ops = &rkisp1_v12_params_ops; else params->ops = &rkisp1_v10_params_ops; @@ -1844,16 +1841,20 @@ int rkisp1_params_register(struct rkisp1_device *rkisp1) node->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&vdev->entity, 1, &node->pad); if (ret) - return ret; + goto error; + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) { dev_err(rkisp1->dev, "failed to register %s, ret=%d\n", vdev->name, ret); - goto err_cleanup_media_entity; + goto error; } + return 0; -err_cleanup_media_entity: + +error: media_entity_cleanup(&vdev->entity); + mutex_destroy(&node->vlock); return ret; } @@ -1863,6 +1864,10 @@ void rkisp1_params_unregister(struct rkisp1_device *rkisp1) struct rkisp1_vdev_node *node = ¶ms->vnode; struct video_device *vdev = &node->vdev; + if (!video_is_registered(vdev)) + return; + vb2_video_unregister_device(vdev); media_entity_cleanup(&vdev->entity); + mutex_destroy(&node->vlock); } diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h index 82f8d33d98b3..dd3e6c38be67 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h @@ -11,7 +11,7 @@ /* ISP_CTRL */ #define RKISP1_CIF_ISP_CTRL_ISP_ENABLE BIT(0) #define RKISP1_CIF_ISP_CTRL_ISP_MODE_RAW_PICT (0 << 1) -#define RKISP1_CIF_ISP_CTRL_ISP_MODE_ITU656 BIT(1) +#define RKISP1_CIF_ISP_CTRL_ISP_MODE_ITU656 (1 << 1) #define RKISP1_CIF_ISP_CTRL_ISP_MODE_ITU601 (2 << 1) #define RKISP1_CIF_ISP_CTRL_ISP_MODE_BAYER_ITU601 (3 << 1) #define RKISP1_CIF_ISP_CTRL_ISP_MODE_DATA_MODE (4 << 1) @@ -33,37 +33,37 @@ #define RKISP1_CIF_ISP_ACQ_PROP_HSYNC_LOW BIT(1) #define RKISP1_CIF_ISP_ACQ_PROP_VSYNC_LOW BIT(2) #define RKISP1_CIF_ISP_ACQ_PROP_BAYER_PAT_RGGB (0 << 3) -#define RKISP1_CIF_ISP_ACQ_PROP_BAYER_PAT_GRBG BIT(3) +#define RKISP1_CIF_ISP_ACQ_PROP_BAYER_PAT_GRBG (1 << 3) #define RKISP1_CIF_ISP_ACQ_PROP_BAYER_PAT_GBRG (2 << 3) #define RKISP1_CIF_ISP_ACQ_PROP_BAYER_PAT_BGGR (3 << 3) #define RKISP1_CIF_ISP_ACQ_PROP_BAYER_PAT(pat) ((pat) << 3) #define RKISP1_CIF_ISP_ACQ_PROP_YCBYCR (0 << 7) -#define RKISP1_CIF_ISP_ACQ_PROP_YCRYCB BIT(7) +#define RKISP1_CIF_ISP_ACQ_PROP_YCRYCB (1 << 7) #define RKISP1_CIF_ISP_ACQ_PROP_CBYCRY (2 << 7) #define RKISP1_CIF_ISP_ACQ_PROP_CRYCBY (3 << 7) #define RKISP1_CIF_ISP_ACQ_PROP_FIELD_SEL_ALL (0 << 9) -#define RKISP1_CIF_ISP_ACQ_PROP_FIELD_SEL_EVEN BIT(9) +#define RKISP1_CIF_ISP_ACQ_PROP_FIELD_SEL_EVEN (1 << 9) #define RKISP1_CIF_ISP_ACQ_PROP_FIELD_SEL_ODD (2 << 9) #define RKISP1_CIF_ISP_ACQ_PROP_IN_SEL_12B (0 << 12) -#define RKISP1_CIF_ISP_ACQ_PROP_IN_SEL_10B_ZERO BIT(12) +#define RKISP1_CIF_ISP_ACQ_PROP_IN_SEL_10B_ZERO (1 << 12) #define RKISP1_CIF_ISP_ACQ_PROP_IN_SEL_10B_MSB (2 << 12) #define RKISP1_CIF_ISP_ACQ_PROP_IN_SEL_8B_ZERO (3 << 12) #define RKISP1_CIF_ISP_ACQ_PROP_IN_SEL_8B_MSB (4 << 12) /* VI_DPCL */ #define RKISP1_CIF_VI_DPCL_DMA_JPEG (0 << 0) -#define RKISP1_CIF_VI_DPCL_MP_MUX_MRSZ_MI BIT(0) +#define RKISP1_CIF_VI_DPCL_MP_MUX_MRSZ_MI (1 << 0) #define RKISP1_CIF_VI_DPCL_MP_MUX_MRSZ_JPEG (2 << 0) -#define RKISP1_CIF_VI_DPCL_CHAN_MODE_MP BIT(2) +#define RKISP1_CIF_VI_DPCL_CHAN_MODE_MP (1 << 2) #define RKISP1_CIF_VI_DPCL_CHAN_MODE_SP (2 << 2) #define RKISP1_CIF_VI_DPCL_CHAN_MODE_MPSP (3 << 2) #define RKISP1_CIF_VI_DPCL_DMA_SW_SPMUX (0 << 4) -#define RKISP1_CIF_VI_DPCL_DMA_SW_SI BIT(4) +#define RKISP1_CIF_VI_DPCL_DMA_SW_SI (1 << 4) #define RKISP1_CIF_VI_DPCL_DMA_SW_IE (2 << 4) #define RKISP1_CIF_VI_DPCL_DMA_SW_JPEG (3 << 4) #define RKISP1_CIF_VI_DPCL_DMA_SW_ISP (4 << 4) #define RKISP1_CIF_VI_DPCL_IF_SEL_PARALLEL (0 << 8) -#define RKISP1_CIF_VI_DPCL_IF_SEL_SMIA BIT(8) +#define RKISP1_CIF_VI_DPCL_IF_SEL_SMIA (1 << 8) #define RKISP1_CIF_VI_DPCL_IF_SEL_MIPI (2 << 8) #define RKISP1_CIF_VI_DPCL_DMA_IE_MUX_DMA BIT(10) #define RKISP1_CIF_VI_DPCL_DMA_SP_MUX_DMA BIT(11) @@ -112,26 +112,26 @@ #define RKISP1_CIF_MI_SP_AUTOUPDATE_ENABLE BIT(14) #define RKISP1_CIF_MI_LAST_PIXEL_SIG_ENABLE BIT(15) #define RKISP1_CIF_MI_CTRL_BURST_LEN_LUM_16 (0 << 16) -#define RKISP1_CIF_MI_CTRL_BURST_LEN_LUM_32 BIT(16) +#define RKISP1_CIF_MI_CTRL_BURST_LEN_LUM_32 (1 << 16) #define RKISP1_CIF_MI_CTRL_BURST_LEN_LUM_64 (2 << 16) #define RKISP1_CIF_MI_CTRL_BURST_LEN_CHROM_16 (0 << 18) -#define RKISP1_CIF_MI_CTRL_BURST_LEN_CHROM_32 BIT(18) +#define RKISP1_CIF_MI_CTRL_BURST_LEN_CHROM_32 (1 << 18) #define RKISP1_CIF_MI_CTRL_BURST_LEN_CHROM_64 (2 << 18) #define RKISP1_CIF_MI_CTRL_INIT_BASE_EN BIT(20) #define RKISP1_CIF_MI_CTRL_INIT_OFFSET_EN BIT(21) #define RKISP1_MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8 (0 << 22) -#define RKISP1_MI_CTRL_MP_WRITE_YUV_SPLA BIT(22) +#define RKISP1_MI_CTRL_MP_WRITE_YUV_SPLA (1 << 22) #define RKISP1_MI_CTRL_MP_WRITE_YUVINT (2 << 22) #define RKISP1_MI_CTRL_MP_WRITE_RAW12 (2 << 22) #define RKISP1_MI_CTRL_SP_WRITE_PLA (0 << 24) -#define RKISP1_MI_CTRL_SP_WRITE_SPLA BIT(24) +#define RKISP1_MI_CTRL_SP_WRITE_SPLA (1 << 24) #define RKISP1_MI_CTRL_SP_WRITE_INT (2 << 24) #define RKISP1_MI_CTRL_SP_INPUT_YUV400 (0 << 26) -#define RKISP1_MI_CTRL_SP_INPUT_YUV420 BIT(26) +#define RKISP1_MI_CTRL_SP_INPUT_YUV420 (1 << 26) #define RKISP1_MI_CTRL_SP_INPUT_YUV422 (2 << 26) #define RKISP1_MI_CTRL_SP_INPUT_YUV444 (3 << 26) #define RKISP1_MI_CTRL_SP_OUTPUT_YUV400 (0 << 28) -#define RKISP1_MI_CTRL_SP_OUTPUT_YUV420 BIT(28) +#define RKISP1_MI_CTRL_SP_OUTPUT_YUV420 (1 << 28) #define RKISP1_MI_CTRL_SP_OUTPUT_YUV422 (2 << 28) #define RKISP1_MI_CTRL_SP_OUTPUT_YUV444 (3 << 28) #define RKISP1_MI_CTRL_SP_OUTPUT_RGB565 (4 << 28) @@ -186,22 +186,22 @@ /* MI_DMA_CTRL */ #define RKISP1_CIF_MI_DMA_CTRL_BURST_LEN_LUM_16 (0 << 0) -#define RKISP1_CIF_MI_DMA_CTRL_BURST_LEN_LUM_32 BIT(0) +#define RKISP1_CIF_MI_DMA_CTRL_BURST_LEN_LUM_32 (1 << 0) #define RKISP1_CIF_MI_DMA_CTRL_BURST_LEN_LUM_64 (2 << 0) #define RKISP1_CIF_MI_DMA_CTRL_BURST_LEN_CHROM_16 (0 << 2) -#define RKISP1_CIF_MI_DMA_CTRL_BURST_LEN_CHROM_32 BIT(2) +#define RKISP1_CIF_MI_DMA_CTRL_BURST_LEN_CHROM_32 (1 << 2) #define RKISP1_CIF_MI_DMA_CTRL_BURST_LEN_CHROM_64 (2 << 2) #define RKISP1_CIF_MI_DMA_CTRL_READ_FMT_PLANAR (0 << 4) -#define RKISP1_CIF_MI_DMA_CTRL_READ_FMT_SPLANAR BIT(4) -#define RKISP1_CIF_MI_DMA_CTRL_FMT_YUV400 (0 << 6) -#define RKISP1_CIF_MI_DMA_CTRL_FMT_YUV420 BIT(6) +#define RKISP1_CIF_MI_DMA_CTRL_READ_FMT_SPLANAR (1 << 4) #define RKISP1_CIF_MI_DMA_CTRL_READ_FMT_PACKED (2 << 4) +#define RKISP1_CIF_MI_DMA_CTRL_FMT_YUV400 (0 << 6) +#define RKISP1_CIF_MI_DMA_CTRL_FMT_YUV420 (1 << 6) #define RKISP1_CIF_MI_DMA_CTRL_FMT_YUV422 (2 << 6) #define RKISP1_CIF_MI_DMA_CTRL_FMT_YUV444 (3 << 6) #define RKISP1_CIF_MI_DMA_CTRL_BYTE_SWAP BIT(8) #define RKISP1_CIF_MI_DMA_CTRL_CONTINUOUS_ENA BIT(9) #define RKISP1_CIF_MI_DMA_CTRL_RGB_BAYER_NO (0 << 12) -#define RKISP1_CIF_MI_DMA_CTRL_RGB_BAYER_8BIT BIT(12) +#define RKISP1_CIF_MI_DMA_CTRL_RGB_BAYER_8BIT (1 << 12) #define RKISP1_CIF_MI_DMA_CTRL_RGB_BAYER_16BIT (2 << 12) /* MI_DMA_START */ #define RKISP1_CIF_MI_DMA_START_ENABLE BIT(0) @@ -210,7 +210,7 @@ #define RKISP1_CIF_MI_XTD_FMT_CTRL_SP_CB_CR_SWAP BIT(1) #define RKISP1_CIF_MI_XTD_FMT_CTRL_DMA_CB_CR_SWAP BIT(2) -/* CCL */ +/* VI_CCL */ #define RKISP1_CIF_CCL_CIF_CLK_DIS BIT(2) /* VI_ISP_CLK_CTRL */ #define RKISP1_CIF_CLK_CTRL_ISP_RAW BIT(0) @@ -241,32 +241,32 @@ #define RKISP1_CIF_CLK_CTRL_RSZS BIT(25) #define RKISP1_CIF_CLK_CTRL_MIPI BIT(26) #define RKISP1_CIF_CLK_CTRL_MARVINMI BIT(27) -/* ICCL */ -#define RKISP1_CIF_ICCL_ISP_CLK BIT(0) -#define RKISP1_CIF_ICCL_CP_CLK BIT(1) -#define RKISP1_CIF_ICCL_RES_2 BIT(2) -#define RKISP1_CIF_ICCL_MRSZ_CLK BIT(3) -#define RKISP1_CIF_ICCL_SRSZ_CLK BIT(4) -#define RKISP1_CIF_ICCL_JPEG_CLK BIT(5) -#define RKISP1_CIF_ICCL_MI_CLK BIT(6) -#define RKISP1_CIF_ICCL_RES_7 BIT(7) -#define RKISP1_CIF_ICCL_IE_CLK BIT(8) -#define RKISP1_CIF_ICCL_SIMP_CLK BIT(9) -#define RKISP1_CIF_ICCL_SMIA_CLK BIT(10) -#define RKISP1_CIF_ICCL_MIPI_CLK BIT(11) -#define RKISP1_CIF_ICCL_DCROP_CLK BIT(12) -/* IRCL */ -#define RKISP1_CIF_IRCL_ISP_SW_RST BIT(0) -#define RKISP1_CIF_IRCL_CP_SW_RST BIT(1) -#define RKISP1_CIF_IRCL_YCS_SW_RST BIT(2) -#define RKISP1_CIF_IRCL_MRSZ_SW_RST BIT(3) -#define RKISP1_CIF_IRCL_SRSZ_SW_RST BIT(4) -#define RKISP1_CIF_IRCL_JPEG_SW_RST BIT(5) -#define RKISP1_CIF_IRCL_MI_SW_RST BIT(6) -#define RKISP1_CIF_IRCL_CIF_SW_RST BIT(7) -#define RKISP1_CIF_IRCL_IE_SW_RST BIT(8) -#define RKISP1_CIF_IRCL_SI_SW_RST BIT(9) -#define RKISP1_CIF_IRCL_MIPI_SW_RST BIT(11) +/* VI_ICCL */ +#define RKISP1_CIF_VI_ICCL_ISP_CLK BIT(0) +#define RKISP1_CIF_VI_ICCL_CP_CLK BIT(1) +#define RKISP1_CIF_VI_ICCL_RES_2 BIT(2) +#define RKISP1_CIF_VI_ICCL_MRSZ_CLK BIT(3) +#define RKISP1_CIF_VI_ICCL_SRSZ_CLK BIT(4) +#define RKISP1_CIF_VI_ICCL_JPEG_CLK BIT(5) +#define RKISP1_CIF_VI_ICCL_MI_CLK BIT(6) +#define RKISP1_CIF_VI_ICCL_RES_7 BIT(7) +#define RKISP1_CIF_VI_ICCL_IE_CLK BIT(8) +#define RKISP1_CIF_VI_ICCL_SIMP_CLK BIT(9) +#define RKISP1_CIF_VI_ICCL_SMIA_CLK BIT(10) +#define RKISP1_CIF_VI_ICCL_MIPI_CLK BIT(11) +#define RKISP1_CIF_VI_ICCL_DCROP_CLK BIT(12) +/* VI_IRCL */ +#define RKISP1_CIF_VI_IRCL_ISP_SW_RST BIT(0) +#define RKISP1_CIF_VI_IRCL_CP_SW_RST BIT(1) +#define RKISP1_CIF_VI_IRCL_YCS_SW_RST BIT(2) +#define RKISP1_CIF_VI_IRCL_MRSZ_SW_RST BIT(3) +#define RKISP1_CIF_VI_IRCL_SRSZ_SW_RST BIT(4) +#define RKISP1_CIF_VI_IRCL_JPEG_SW_RST BIT(5) +#define RKISP1_CIF_VI_IRCL_MI_SW_RST BIT(6) +#define RKISP1_CIF_VI_IRCL_CIF_SW_RST BIT(7) +#define RKISP1_CIF_VI_IRCL_IE_SW_RST BIT(8) +#define RKISP1_CIF_VI_IRCL_SI_SW_RST BIT(9) +#define RKISP1_CIF_VI_IRCL_MIPI_SW_RST BIT(11) /* C_PROC_CTR */ #define RKISP1_CIF_C_PROC_CTR_ENABLE BIT(0) @@ -282,10 +282,10 @@ #define RKISP1_CIF_C_PROC_TONE_RESERVED 0xF000 /* DUAL_CROP_CTRL */ #define RKISP1_CIF_DUAL_CROP_MP_MODE_BYPASS (0 << 0) -#define RKISP1_CIF_DUAL_CROP_MP_MODE_YUV BIT(0) +#define RKISP1_CIF_DUAL_CROP_MP_MODE_YUV (1 << 0) #define RKISP1_CIF_DUAL_CROP_MP_MODE_RAW (2 << 0) #define RKISP1_CIF_DUAL_CROP_SP_MODE_BYPASS (0 << 2) -#define RKISP1_CIF_DUAL_CROP_SP_MODE_YUV BIT(2) +#define RKISP1_CIF_DUAL_CROP_SP_MODE_YUV (1 << 2) #define RKISP1_CIF_DUAL_CROP_SP_MODE_RAW (2 << 2) #define RKISP1_CIF_DUAL_CROP_CFG_UPD_PERMANENT BIT(4) #define RKISP1_CIF_DUAL_CROP_CFG_UPD BIT(5) @@ -294,7 +294,7 @@ /* IMG_EFF_CTRL */ #define RKISP1_CIF_IMG_EFF_CTRL_ENABLE BIT(0) #define RKISP1_CIF_IMG_EFF_CTRL_MODE_BLACKWHITE (0 << 1) -#define RKISP1_CIF_IMG_EFF_CTRL_MODE_NEGATIVE BIT(1) +#define RKISP1_CIF_IMG_EFF_CTRL_MODE_NEGATIVE (1 << 1) #define RKISP1_CIF_IMG_EFF_CTRL_MODE_SEPIA (2 << 1) #define RKISP1_CIF_IMG_EFF_CTRL_MODE_COLOR_SEL (3 << 1) #define RKISP1_CIF_IMG_EFF_CTRL_MODE_EMBOSS (4 << 1) @@ -314,7 +314,7 @@ /* IMG_EFF_COLOR_SEL */ #define RKISP1_CIF_IMG_EFF_COLOR_RGB 0 -#define RKISP1_CIF_IMG_EFF_COLOR_B BIT(0) +#define RKISP1_CIF_IMG_EFF_COLOR_B (1 << 0) #define RKISP1_CIF_IMG_EFF_COLOR_G (2 << 0) #define RKISP1_CIF_IMG_EFF_COLOR_GB (3 << 0) #define RKISP1_CIF_IMG_EFF_COLOR_R (4 << 0) @@ -365,7 +365,7 @@ /* ISP HISTOGRAM CALCULATION : ISP_HIST_PROP */ #define RKISP1_CIF_ISP_HIST_PROP_MODE_DIS_V10 (0 << 0) -#define RKISP1_CIF_ISP_HIST_PROP_MODE_RGB_V10 BIT(0) +#define RKISP1_CIF_ISP_HIST_PROP_MODE_RGB_V10 (1 << 0) #define RKISP1_CIF_ISP_HIST_PROP_MODE_RED_V10 (2 << 0) #define RKISP1_CIF_ISP_HIST_PROP_MODE_GREEN_V10 (3 << 0) #define RKISP1_CIF_ISP_HIST_PROP_MODE_BLUE_V10 (4 << 0) @@ -443,6 +443,15 @@ #define RKISP1_CIF_ISP_DEMOSAIC_BYPASS BIT(10) #define RKISP1_CIF_ISP_DEMOSAIC_TH(x) ((x) & 0xFF) +/* ISP_FLAGS_SHD */ +#define RKISP1_CIF_ISP_FLAGS_SHD_ISP_ENABLE_SHD BIT(0) +#define RKISP1_CIF_ISP_FLAGS_SHD_ISP_ENABLE_INFORM_SHD BIT(1) +#define RKISP1_CIF_ISP_FLAGS_SHD_INFORM_FIELD BIT(2) +#define RKISP1_CIF_ISP_FLAGS_SHD_S_DATA_MASK GENMASK(27, 16) +#define RKISP1_CIF_ISP_FLAGS_SHD_S_DATA_SHIFT 16 +#define RKISP1_CIF_ISP_FLAGS_SHD_S_VSYNC BIT(30) +#define RKISP1_CIF_ISP_FLAGS_SHD_S_HSYNC BIT(31) + /* AWB */ /* ISP_AWB_PROP */ #define RKISP1_CIF_ISP_AWB_YMAX_CMP_EN BIT(2) @@ -628,7 +637,7 @@ #define RKISP1_CIF_ISP_BLS_ENA BIT(0) #define RKISP1_CIF_ISP_BLS_MODE_MEASURED BIT(1) #define RKISP1_CIF_ISP_BLS_MODE_FIXED 0 -#define RKISP1_CIF_ISP_BLS_WINDOW_1 BIT(2) +#define RKISP1_CIF_ISP_BLS_WINDOW_1 (1 << 2) #define RKISP1_CIF_ISP_BLS_WINDOW_2 (2 << 2) /* GAMMA-IN */ @@ -676,11 +685,11 @@ /* CIF Registers */ /* =================================================================== */ #define RKISP1_CIF_CTRL_BASE 0x00000000 -#define RKISP1_CIF_CCL (RKISP1_CIF_CTRL_BASE + 0x00000000) +#define RKISP1_CIF_VI_CCL (RKISP1_CIF_CTRL_BASE + 0x00000000) #define RKISP1_CIF_VI_ID (RKISP1_CIF_CTRL_BASE + 0x00000008) #define RKISP1_CIF_VI_ISP_CLK_CTRL_V12 (RKISP1_CIF_CTRL_BASE + 0x0000000C) -#define RKISP1_CIF_ICCL (RKISP1_CIF_CTRL_BASE + 0x00000010) -#define RKISP1_CIF_IRCL (RKISP1_CIF_CTRL_BASE + 0x00000014) +#define RKISP1_CIF_VI_ICCL (RKISP1_CIF_CTRL_BASE + 0x00000010) +#define RKISP1_CIF_VI_IRCL (RKISP1_CIF_CTRL_BASE + 0x00000014) #define RKISP1_CIF_VI_DPCL (RKISP1_CIF_CTRL_BASE + 0x00000018) #define RKISP1_CIF_IMG_EFF_BASE 0x00000200 @@ -894,52 +903,29 @@ #define RKISP1_CIF_DUAL_CROP_S_V_SIZE_SHD (RKISP1_CIF_DUAL_CROP_BASE + 0x00000040) #define RKISP1_CIF_MRSZ_BASE 0x00000C00 -#define RKISP1_CIF_MRSZ_CTRL (RKISP1_CIF_MRSZ_BASE + 0x00000000) -#define RKISP1_CIF_MRSZ_SCALE_HY (RKISP1_CIF_MRSZ_BASE + 0x00000004) -#define RKISP1_CIF_MRSZ_SCALE_HCB (RKISP1_CIF_MRSZ_BASE + 0x00000008) -#define RKISP1_CIF_MRSZ_SCALE_HCR (RKISP1_CIF_MRSZ_BASE + 0x0000000C) -#define RKISP1_CIF_MRSZ_SCALE_VY (RKISP1_CIF_MRSZ_BASE + 0x00000010) -#define RKISP1_CIF_MRSZ_SCALE_VC (RKISP1_CIF_MRSZ_BASE + 0x00000014) -#define RKISP1_CIF_MRSZ_PHASE_HY (RKISP1_CIF_MRSZ_BASE + 0x00000018) -#define RKISP1_CIF_MRSZ_PHASE_HC (RKISP1_CIF_MRSZ_BASE + 0x0000001C) -#define RKISP1_CIF_MRSZ_PHASE_VY (RKISP1_CIF_MRSZ_BASE + 0x00000020) -#define RKISP1_CIF_MRSZ_PHASE_VC (RKISP1_CIF_MRSZ_BASE + 0x00000024) -#define RKISP1_CIF_MRSZ_SCALE_LUT_ADDR (RKISP1_CIF_MRSZ_BASE + 0x00000028) -#define RKISP1_CIF_MRSZ_SCALE_LUT (RKISP1_CIF_MRSZ_BASE + 0x0000002C) -#define RKISP1_CIF_MRSZ_CTRL_SHD (RKISP1_CIF_MRSZ_BASE + 0x00000030) -#define RKISP1_CIF_MRSZ_SCALE_HY_SHD (RKISP1_CIF_MRSZ_BASE + 0x00000034) -#define RKISP1_CIF_MRSZ_SCALE_HCB_SHD (RKISP1_CIF_MRSZ_BASE + 0x00000038) -#define RKISP1_CIF_MRSZ_SCALE_HCR_SHD (RKISP1_CIF_MRSZ_BASE + 0x0000003C) -#define RKISP1_CIF_MRSZ_SCALE_VY_SHD (RKISP1_CIF_MRSZ_BASE + 0x00000040) -#define RKISP1_CIF_MRSZ_SCALE_VC_SHD (RKISP1_CIF_MRSZ_BASE + 0x00000044) -#define RKISP1_CIF_MRSZ_PHASE_HY_SHD (RKISP1_CIF_MRSZ_BASE + 0x00000048) -#define RKISP1_CIF_MRSZ_PHASE_HC_SHD (RKISP1_CIF_MRSZ_BASE + 0x0000004C) -#define RKISP1_CIF_MRSZ_PHASE_VY_SHD (RKISP1_CIF_MRSZ_BASE + 0x00000050) -#define RKISP1_CIF_MRSZ_PHASE_VC_SHD (RKISP1_CIF_MRSZ_BASE + 0x00000054) - #define RKISP1_CIF_SRSZ_BASE 0x00001000 -#define RKISP1_CIF_SRSZ_CTRL (RKISP1_CIF_SRSZ_BASE + 0x00000000) -#define RKISP1_CIF_SRSZ_SCALE_HY (RKISP1_CIF_SRSZ_BASE + 0x00000004) -#define RKISP1_CIF_SRSZ_SCALE_HCB (RKISP1_CIF_SRSZ_BASE + 0x00000008) -#define RKISP1_CIF_SRSZ_SCALE_HCR (RKISP1_CIF_SRSZ_BASE + 0x0000000C) -#define RKISP1_CIF_SRSZ_SCALE_VY (RKISP1_CIF_SRSZ_BASE + 0x00000010) -#define RKISP1_CIF_SRSZ_SCALE_VC (RKISP1_CIF_SRSZ_BASE + 0x00000014) -#define RKISP1_CIF_SRSZ_PHASE_HY (RKISP1_CIF_SRSZ_BASE + 0x00000018) -#define RKISP1_CIF_SRSZ_PHASE_HC (RKISP1_CIF_SRSZ_BASE + 0x0000001C) -#define RKISP1_CIF_SRSZ_PHASE_VY (RKISP1_CIF_SRSZ_BASE + 0x00000020) -#define RKISP1_CIF_SRSZ_PHASE_VC (RKISP1_CIF_SRSZ_BASE + 0x00000024) -#define RKISP1_CIF_SRSZ_SCALE_LUT_ADDR (RKISP1_CIF_SRSZ_BASE + 0x00000028) -#define RKISP1_CIF_SRSZ_SCALE_LUT (RKISP1_CIF_SRSZ_BASE + 0x0000002C) -#define RKISP1_CIF_SRSZ_CTRL_SHD (RKISP1_CIF_SRSZ_BASE + 0x00000030) -#define RKISP1_CIF_SRSZ_SCALE_HY_SHD (RKISP1_CIF_SRSZ_BASE + 0x00000034) -#define RKISP1_CIF_SRSZ_SCALE_HCB_SHD (RKISP1_CIF_SRSZ_BASE + 0x00000038) -#define RKISP1_CIF_SRSZ_SCALE_HCR_SHD (RKISP1_CIF_SRSZ_BASE + 0x0000003C) -#define RKISP1_CIF_SRSZ_SCALE_VY_SHD (RKISP1_CIF_SRSZ_BASE + 0x00000040) -#define RKISP1_CIF_SRSZ_SCALE_VC_SHD (RKISP1_CIF_SRSZ_BASE + 0x00000044) -#define RKISP1_CIF_SRSZ_PHASE_HY_SHD (RKISP1_CIF_SRSZ_BASE + 0x00000048) -#define RKISP1_CIF_SRSZ_PHASE_HC_SHD (RKISP1_CIF_SRSZ_BASE + 0x0000004C) -#define RKISP1_CIF_SRSZ_PHASE_VY_SHD (RKISP1_CIF_SRSZ_BASE + 0x00000050) -#define RKISP1_CIF_SRSZ_PHASE_VC_SHD (RKISP1_CIF_SRSZ_BASE + 0x00000054) +#define RKISP1_CIF_RSZ_CTRL 0x0000 +#define RKISP1_CIF_RSZ_SCALE_HY 0x0004 +#define RKISP1_CIF_RSZ_SCALE_HCB 0x0008 +#define RKISP1_CIF_RSZ_SCALE_HCR 0x000C +#define RKISP1_CIF_RSZ_SCALE_VY 0x0010 +#define RKISP1_CIF_RSZ_SCALE_VC 0x0014 +#define RKISP1_CIF_RSZ_PHASE_HY 0x0018 +#define RKISP1_CIF_RSZ_PHASE_HC 0x001C +#define RKISP1_CIF_RSZ_PHASE_VY 0x0020 +#define RKISP1_CIF_RSZ_PHASE_VC 0x0024 +#define RKISP1_CIF_RSZ_SCALE_LUT_ADDR 0x0028 +#define RKISP1_CIF_RSZ_SCALE_LUT 0x002C +#define RKISP1_CIF_RSZ_CTRL_SHD 0x0030 +#define RKISP1_CIF_RSZ_SCALE_HY_SHD 0x0034 +#define RKISP1_CIF_RSZ_SCALE_HCB_SHD 0x0038 +#define RKISP1_CIF_RSZ_SCALE_HCR_SHD 0x003C +#define RKISP1_CIF_RSZ_SCALE_VY_SHD 0x0040 +#define RKISP1_CIF_RSZ_SCALE_VC_SHD 0x0044 +#define RKISP1_CIF_RSZ_PHASE_HY_SHD 0x0048 +#define RKISP1_CIF_RSZ_PHASE_HC_SHD 0x004C +#define RKISP1_CIF_RSZ_PHASE_VY_SHD 0x0050 +#define RKISP1_CIF_RSZ_PHASE_VC_SHD 0x0054 #define RKISP1_CIF_MI_BASE 0x00001400 #define RKISP1_CIF_MI_CTRL (RKISP1_CIF_MI_BASE + 0x00000000) diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c index 2070f4b06705..f4caa8f684aa 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c @@ -61,30 +61,6 @@ struct rkisp1_rsz_config { /* registers */ struct { u32 ctrl; - u32 ctrl_shd; - u32 scale_hy; - u32 scale_hcr; - u32 scale_hcb; - u32 scale_vy; - u32 scale_vc; - u32 scale_lut; - u32 scale_lut_addr; - u32 scale_hy_shd; - u32 scale_hcr_shd; - u32 scale_hcb_shd; - u32 scale_vy_shd; - u32 scale_vc_shd; - u32 phase_hy; - u32 phase_hc; - u32 phase_vy; - u32 phase_vc; - u32 phase_hy_shd; - u32 phase_hc_shd; - u32 phase_vy_shd; - u32 phase_vc_shd; - } rsz; - struct { - u32 ctrl; u32 yuvmode_mask; u32 rawmode_mask; u32 h_offset; @@ -101,30 +77,6 @@ static const struct rkisp1_rsz_config rkisp1_rsz_config_mp = { .min_rsz_width = RKISP1_RSZ_SRC_MIN_WIDTH, .min_rsz_height = RKISP1_RSZ_SRC_MIN_HEIGHT, /* registers */ - .rsz = { - .ctrl = RKISP1_CIF_MRSZ_CTRL, - .scale_hy = RKISP1_CIF_MRSZ_SCALE_HY, - .scale_hcr = RKISP1_CIF_MRSZ_SCALE_HCR, - .scale_hcb = RKISP1_CIF_MRSZ_SCALE_HCB, - .scale_vy = RKISP1_CIF_MRSZ_SCALE_VY, - .scale_vc = RKISP1_CIF_MRSZ_SCALE_VC, - .scale_lut = RKISP1_CIF_MRSZ_SCALE_LUT, - .scale_lut_addr = RKISP1_CIF_MRSZ_SCALE_LUT_ADDR, - .scale_hy_shd = RKISP1_CIF_MRSZ_SCALE_HY_SHD, - .scale_hcr_shd = RKISP1_CIF_MRSZ_SCALE_HCR_SHD, - .scale_hcb_shd = RKISP1_CIF_MRSZ_SCALE_HCB_SHD, - .scale_vy_shd = RKISP1_CIF_MRSZ_SCALE_VY_SHD, - .scale_vc_shd = RKISP1_CIF_MRSZ_SCALE_VC_SHD, - .phase_hy = RKISP1_CIF_MRSZ_PHASE_HY, - .phase_hc = RKISP1_CIF_MRSZ_PHASE_HC, - .phase_vy = RKISP1_CIF_MRSZ_PHASE_VY, - .phase_vc = RKISP1_CIF_MRSZ_PHASE_VC, - .ctrl_shd = RKISP1_CIF_MRSZ_CTRL_SHD, - .phase_hy_shd = RKISP1_CIF_MRSZ_PHASE_HY_SHD, - .phase_hc_shd = RKISP1_CIF_MRSZ_PHASE_HC_SHD, - .phase_vy_shd = RKISP1_CIF_MRSZ_PHASE_VY_SHD, - .phase_vc_shd = RKISP1_CIF_MRSZ_PHASE_VC_SHD, - }, .dual_crop = { .ctrl = RKISP1_CIF_DUAL_CROP_CTRL, .yuvmode_mask = RKISP1_CIF_DUAL_CROP_MP_MODE_YUV, @@ -143,30 +95,6 @@ static const struct rkisp1_rsz_config rkisp1_rsz_config_sp = { .min_rsz_width = RKISP1_RSZ_SRC_MIN_WIDTH, .min_rsz_height = RKISP1_RSZ_SRC_MIN_HEIGHT, /* registers */ - .rsz = { - .ctrl = RKISP1_CIF_SRSZ_CTRL, - .scale_hy = RKISP1_CIF_SRSZ_SCALE_HY, - .scale_hcr = RKISP1_CIF_SRSZ_SCALE_HCR, - .scale_hcb = RKISP1_CIF_SRSZ_SCALE_HCB, - .scale_vy = RKISP1_CIF_SRSZ_SCALE_VY, - .scale_vc = RKISP1_CIF_SRSZ_SCALE_VC, - .scale_lut = RKISP1_CIF_SRSZ_SCALE_LUT, - .scale_lut_addr = RKISP1_CIF_SRSZ_SCALE_LUT_ADDR, - .scale_hy_shd = RKISP1_CIF_SRSZ_SCALE_HY_SHD, - .scale_hcr_shd = RKISP1_CIF_SRSZ_SCALE_HCR_SHD, - .scale_hcb_shd = RKISP1_CIF_SRSZ_SCALE_HCB_SHD, - .scale_vy_shd = RKISP1_CIF_SRSZ_SCALE_VY_SHD, - .scale_vc_shd = RKISP1_CIF_SRSZ_SCALE_VC_SHD, - .phase_hy = RKISP1_CIF_SRSZ_PHASE_HY, - .phase_hc = RKISP1_CIF_SRSZ_PHASE_HC, - .phase_vy = RKISP1_CIF_SRSZ_PHASE_VY, - .phase_vc = RKISP1_CIF_SRSZ_PHASE_VC, - .ctrl_shd = RKISP1_CIF_SRSZ_CTRL_SHD, - .phase_hy_shd = RKISP1_CIF_SRSZ_PHASE_HY_SHD, - .phase_hc_shd = RKISP1_CIF_SRSZ_PHASE_HC_SHD, - .phase_vy_shd = RKISP1_CIF_SRSZ_PHASE_VY_SHD, - .phase_vc_shd = RKISP1_CIF_SRSZ_PHASE_VC_SHD, - }, .dual_crop = { .ctrl = RKISP1_CIF_DUAL_CROP_CTRL, .yuvmode_mask = RKISP1_CIF_DUAL_CROP_SP_MODE_YUV, @@ -178,6 +106,17 @@ static const struct rkisp1_rsz_config rkisp1_rsz_config_sp = { }, }; +static inline u32 rkisp1_rsz_read(struct rkisp1_resizer *rsz, u32 offset) +{ + return rkisp1_read(rsz->rkisp1, rsz->regs_base + offset); +} + +static inline void rkisp1_rsz_write(struct rkisp1_resizer *rsz, u32 offset, + u32 value) +{ + rkisp1_write(rsz->rkisp1, rsz->regs_base + offset, value); +} + static struct v4l2_mbus_framefmt * rkisp1_rsz_get_pad_fmt(struct rkisp1_resizer *rsz, struct v4l2_subdev_state *sd_state, @@ -222,7 +161,7 @@ static void rkisp1_dcrop_disable(struct rkisp1_resizer *rsz, dc_ctrl |= RKISP1_CIF_DUAL_CROP_GEN_CFG_UPD; else dc_ctrl |= RKISP1_CIF_DUAL_CROP_CFG_UPD; - rkisp1_write(rsz->rkisp1, dc_ctrl, rsz->config->dual_crop.ctrl); + rkisp1_write(rsz->rkisp1, rsz->config->dual_crop.ctrl, dc_ctrl); } /* configure dual-crop unit */ @@ -247,13 +186,13 @@ static void rkisp1_dcrop_config(struct rkisp1_resizer *rsz) } dc_ctrl = rkisp1_read(rkisp1, rsz->config->dual_crop.ctrl); - rkisp1_write(rkisp1, sink_crop->left, rsz->config->dual_crop.h_offset); - rkisp1_write(rkisp1, sink_crop->top, rsz->config->dual_crop.v_offset); - rkisp1_write(rkisp1, sink_crop->width, rsz->config->dual_crop.h_size); - rkisp1_write(rkisp1, sink_crop->height, rsz->config->dual_crop.v_size); + rkisp1_write(rkisp1, rsz->config->dual_crop.h_offset, sink_crop->left); + rkisp1_write(rkisp1, rsz->config->dual_crop.v_offset, sink_crop->top); + rkisp1_write(rkisp1, rsz->config->dual_crop.h_size, sink_crop->width); + rkisp1_write(rkisp1, rsz->config->dual_crop.v_size, sink_crop->height); dc_ctrl |= rsz->config->dual_crop.yuvmode_mask; dc_ctrl |= RKISP1_CIF_DUAL_CROP_CFG_UPD; - rkisp1_write(rkisp1, dc_ctrl, rsz->config->dual_crop.ctrl); + rkisp1_write(rkisp1, rsz->config->dual_crop.ctrl, dc_ctrl); dev_dbg(rkisp1->dev, "stream %d crop: %dx%d -> %dx%d\n", rsz->id, sink_fmt->width, sink_fmt->height, @@ -264,52 +203,17 @@ static void rkisp1_dcrop_config(struct rkisp1_resizer *rsz) * Resizer hw configs */ -static void rkisp1_rsz_dump_regs(struct rkisp1_resizer *rsz) -{ - dev_dbg(rsz->rkisp1->dev, - "RSZ_CTRL 0x%08x/0x%08x\n" - "RSZ_SCALE_HY %d/%d\n" - "RSZ_SCALE_HCB %d/%d\n" - "RSZ_SCALE_HCR %d/%d\n" - "RSZ_SCALE_VY %d/%d\n" - "RSZ_SCALE_VC %d/%d\n" - "RSZ_PHASE_HY %d/%d\n" - "RSZ_PHASE_HC %d/%d\n" - "RSZ_PHASE_VY %d/%d\n" - "RSZ_PHASE_VC %d/%d\n", - rkisp1_read(rsz->rkisp1, rsz->config->rsz.ctrl), - rkisp1_read(rsz->rkisp1, rsz->config->rsz.ctrl_shd), - rkisp1_read(rsz->rkisp1, rsz->config->rsz.scale_hy), - rkisp1_read(rsz->rkisp1, rsz->config->rsz.scale_hy_shd), - rkisp1_read(rsz->rkisp1, rsz->config->rsz.scale_hcb), - rkisp1_read(rsz->rkisp1, rsz->config->rsz.scale_hcb_shd), - rkisp1_read(rsz->rkisp1, rsz->config->rsz.scale_hcr), - rkisp1_read(rsz->rkisp1, rsz->config->rsz.scale_hcr_shd), - rkisp1_read(rsz->rkisp1, rsz->config->rsz.scale_vy), - rkisp1_read(rsz->rkisp1, rsz->config->rsz.scale_vy_shd), - rkisp1_read(rsz->rkisp1, rsz->config->rsz.scale_vc), - rkisp1_read(rsz->rkisp1, rsz->config->rsz.scale_vc_shd), - rkisp1_read(rsz->rkisp1, rsz->config->rsz.phase_hy), - rkisp1_read(rsz->rkisp1, rsz->config->rsz.phase_hy_shd), - rkisp1_read(rsz->rkisp1, rsz->config->rsz.phase_hc), - rkisp1_read(rsz->rkisp1, rsz->config->rsz.phase_hc_shd), - rkisp1_read(rsz->rkisp1, rsz->config->rsz.phase_vy), - rkisp1_read(rsz->rkisp1, rsz->config->rsz.phase_vy_shd), - rkisp1_read(rsz->rkisp1, rsz->config->rsz.phase_vc), - rkisp1_read(rsz->rkisp1, rsz->config->rsz.phase_vc_shd)); -} - static void rkisp1_rsz_update_shadow(struct rkisp1_resizer *rsz, enum rkisp1_shadow_regs_when when) { - u32 ctrl_cfg = rkisp1_read(rsz->rkisp1, rsz->config->rsz.ctrl); + u32 ctrl_cfg = rkisp1_rsz_read(rsz, RKISP1_CIF_RSZ_CTRL); if (when == RKISP1_SHADOW_REGS_ASYNC) ctrl_cfg |= RKISP1_CIF_RSZ_CTRL_CFG_UPD_AUTO; else ctrl_cfg |= RKISP1_CIF_RSZ_CTRL_CFG_UPD; - rkisp1_write(rsz->rkisp1, ctrl_cfg, rsz->config->rsz.ctrl); + rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_CTRL, ctrl_cfg); } static u32 rkisp1_rsz_calc_ratio(u32 len_sink, u32 len_src) @@ -325,7 +229,7 @@ static u32 rkisp1_rsz_calc_ratio(u32 len_sink, u32 len_src) static void rkisp1_rsz_disable(struct rkisp1_resizer *rsz, enum rkisp1_shadow_regs_when when) { - rkisp1_write(rsz->rkisp1, 0, rsz->config->rsz.ctrl); + rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_CTRL, 0); if (when == RKISP1_SHADOW_REGS_SYNC) rkisp1_rsz_update_shadow(rsz, when); @@ -338,20 +242,19 @@ static void rkisp1_rsz_config_regs(struct rkisp1_resizer *rsz, struct v4l2_rect *src_c, enum rkisp1_shadow_regs_when when) { - struct rkisp1_device *rkisp1 = rsz->rkisp1; u32 ratio, rsz_ctrl = 0; unsigned int i; /* No phase offset */ - rkisp1_write(rkisp1, 0, rsz->config->rsz.phase_hy); - rkisp1_write(rkisp1, 0, rsz->config->rsz.phase_hc); - rkisp1_write(rkisp1, 0, rsz->config->rsz.phase_vy); - rkisp1_write(rkisp1, 0, rsz->config->rsz.phase_vc); + rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_PHASE_HY, 0); + rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_PHASE_HC, 0); + rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_PHASE_VY, 0); + rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_PHASE_VC, 0); /* Linear interpolation */ for (i = 0; i < 64; i++) { - rkisp1_write(rkisp1, i, rsz->config->rsz.scale_lut_addr); - rkisp1_write(rkisp1, i, rsz->config->rsz.scale_lut); + rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_SCALE_LUT_ADDR, i); + rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_SCALE_LUT, i); } if (sink_y->width != src_y->width) { @@ -359,7 +262,7 @@ static void rkisp1_rsz_config_regs(struct rkisp1_resizer *rsz, if (sink_y->width < src_y->width) rsz_ctrl |= RKISP1_CIF_RSZ_CTRL_SCALE_HY_UP; ratio = rkisp1_rsz_calc_ratio(sink_y->width, src_y->width); - rkisp1_write(rkisp1, ratio, rsz->config->rsz.scale_hy); + rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_SCALE_HY, ratio); } if (sink_c->width != src_c->width) { @@ -367,8 +270,8 @@ static void rkisp1_rsz_config_regs(struct rkisp1_resizer *rsz, if (sink_c->width < src_c->width) rsz_ctrl |= RKISP1_CIF_RSZ_CTRL_SCALE_HC_UP; ratio = rkisp1_rsz_calc_ratio(sink_c->width, src_c->width); - rkisp1_write(rkisp1, ratio, rsz->config->rsz.scale_hcb); - rkisp1_write(rkisp1, ratio, rsz->config->rsz.scale_hcr); + rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_SCALE_HCB, ratio); + rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_SCALE_HCR, ratio); } if (sink_y->height != src_y->height) { @@ -376,7 +279,7 @@ static void rkisp1_rsz_config_regs(struct rkisp1_resizer *rsz, if (sink_y->height < src_y->height) rsz_ctrl |= RKISP1_CIF_RSZ_CTRL_SCALE_VY_UP; ratio = rkisp1_rsz_calc_ratio(sink_y->height, src_y->height); - rkisp1_write(rkisp1, ratio, rsz->config->rsz.scale_vy); + rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_SCALE_VY, ratio); } if (sink_c->height != src_c->height) { @@ -384,10 +287,10 @@ static void rkisp1_rsz_config_regs(struct rkisp1_resizer *rsz, if (sink_c->height < src_c->height) rsz_ctrl |= RKISP1_CIF_RSZ_CTRL_SCALE_VC_UP; ratio = rkisp1_rsz_calc_ratio(sink_c->height, src_c->height); - rkisp1_write(rkisp1, ratio, rsz->config->rsz.scale_vc); + rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_SCALE_VC, ratio); } - rkisp1_write(rkisp1, rsz_ctrl, rsz->config->rsz.ctrl); + rkisp1_rsz_write(rsz, RKISP1_CIF_RSZ_CTRL, rsz_ctrl); rkisp1_rsz_update_shadow(rsz, when); } @@ -448,8 +351,6 @@ static void rkisp1_rsz_config(struct rkisp1_resizer *rsz, /* set values in the hw */ rkisp1_rsz_config_regs(rsz, &sink_y, &sink_c, &src_y, &src_c, when); - - rkisp1_rsz_dump_regs(rsz); } /* ---------------------------------------------------------------------------- @@ -532,14 +433,14 @@ static void rkisp1_rsz_set_src_fmt(struct rkisp1_resizer *rsz, struct v4l2_mbus_framefmt *format, unsigned int which) { - const struct rkisp1_isp_mbus_info *sink_mbus_info; + const struct rkisp1_mbus_info *sink_mbus_info; struct v4l2_mbus_framefmt *src_fmt, *sink_fmt; sink_fmt = rkisp1_rsz_get_pad_fmt(rsz, sd_state, RKISP1_RSZ_PAD_SINK, which); src_fmt = rkisp1_rsz_get_pad_fmt(rsz, sd_state, RKISP1_RSZ_PAD_SRC, which); - sink_mbus_info = rkisp1_isp_mbus_info_get(sink_fmt->code); + sink_mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code); /* for YUV formats, userspace can change the mbus code on the src pad if it is supported */ if (sink_mbus_info->pixel_enc == V4L2_PIXEL_ENC_YUV && @@ -561,7 +462,7 @@ static void rkisp1_rsz_set_sink_crop(struct rkisp1_resizer *rsz, struct v4l2_rect *r, unsigned int which) { - const struct rkisp1_isp_mbus_info *mbus_info; + const struct rkisp1_mbus_info *mbus_info; struct v4l2_mbus_framefmt *sink_fmt; struct v4l2_rect *sink_crop; @@ -572,7 +473,7 @@ static void rkisp1_rsz_set_sink_crop(struct rkisp1_resizer *rsz, which); /* Not crop for MP bayer raw data */ - mbus_info = rkisp1_isp_mbus_info_get(sink_fmt->code); + mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code); if (rsz->id == RKISP1_MAINPATH && mbus_info->pixel_enc == V4L2_PIXEL_ENC_BAYER) { @@ -599,7 +500,7 @@ static void rkisp1_rsz_set_sink_fmt(struct rkisp1_resizer *rsz, struct v4l2_mbus_framefmt *format, unsigned int which) { - const struct rkisp1_isp_mbus_info *mbus_info; + const struct rkisp1_mbus_info *mbus_info; struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; struct v4l2_rect *sink_crop; @@ -615,10 +516,10 @@ static void rkisp1_rsz_set_sink_fmt(struct rkisp1_resizer *rsz, else sink_fmt->code = format->code; - mbus_info = rkisp1_isp_mbus_info_get(sink_fmt->code); + mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code); if (!mbus_info || !(mbus_info->direction & RKISP1_ISP_SD_SRC)) { sink_fmt->code = RKISP1_DEF_FMT; - mbus_info = rkisp1_isp_mbus_info_get(sink_fmt->code); + mbus_info = rkisp1_mbus_info_get_by_code(sink_fmt->code); } if (which == V4L2_SUBDEV_FORMAT_ACTIVE) rsz->pixel_enc = mbus_info->pixel_enc; @@ -782,8 +683,12 @@ static const struct v4l2_subdev_ops rkisp1_rsz_ops = { static void rkisp1_rsz_unregister(struct rkisp1_resizer *rsz) { + if (!rsz->rkisp1) + return; + v4l2_device_unregister_subdev(&rsz->sd); media_entity_cleanup(&rsz->sd.entity); + mutex_destroy(&rsz->ops_lock); } static int rkisp1_rsz_register(struct rkisp1_resizer *rsz) @@ -799,10 +704,13 @@ static int rkisp1_rsz_register(struct rkisp1_resizer *rsz) struct v4l2_subdev *sd = &rsz->sd; int ret; - if (rsz->id == RKISP1_SELFPATH) + if (rsz->id == RKISP1_SELFPATH) { + rsz->regs_base = RKISP1_CIF_SRSZ_BASE; rsz->config = &rkisp1_rsz_config_sp; - else + } else { + rsz->regs_base = RKISP1_CIF_MRSZ_BASE; rsz->config = &rkisp1_rsz_config_mp; + } v4l2_subdev_init(sd, &rkisp1_rsz_ops); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; @@ -821,47 +729,43 @@ static int rkisp1_rsz_register(struct rkisp1_resizer *rsz) mutex_init(&rsz->ops_lock); ret = media_entity_pads_init(&sd->entity, RKISP1_RSZ_PAD_MAX, pads); if (ret) - return ret; + goto error; ret = v4l2_device_register_subdev(&rsz->rkisp1->v4l2_dev, sd); if (ret) { dev_err(sd->dev, "Failed to register resizer subdev\n"); - goto err_cleanup_media_entity; + goto error; } rkisp1_rsz_init_config(sd, &state); return 0; -err_cleanup_media_entity: +error: media_entity_cleanup(&sd->entity); - + mutex_destroy(&rsz->ops_lock); return ret; } int rkisp1_resizer_devs_register(struct rkisp1_device *rkisp1) { - struct rkisp1_resizer *rsz; - unsigned int i, j; + unsigned int i; int ret; for (i = 0; i < ARRAY_SIZE(rkisp1->resizer_devs); i++) { - rsz = &rkisp1->resizer_devs[i]; + struct rkisp1_resizer *rsz = &rkisp1->resizer_devs[i]; + rsz->rkisp1 = rkisp1; rsz->id = i; + ret = rkisp1_rsz_register(rsz); - if (ret) - goto err_unreg_resizer_devs; + if (ret) { + rsz->rkisp1 = NULL; + rkisp1_resizer_devs_unregister(rkisp1); + return ret; + } } return 0; - -err_unreg_resizer_devs: - for (j = 0; j < i; j++) { - rsz = &rkisp1->resizer_devs[j]; - rkisp1_rsz_unregister(rsz); - } - - return ret; } void rkisp1_resizer_devs_unregister(struct rkisp1_device *rkisp1) diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-stats.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-stats.c index be5777c65bfb..2795eef91bdd 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-stats.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-stats.c @@ -305,7 +305,7 @@ static void rkisp1_stats_get_bls_meas(struct rkisp1_stats *stats, struct rkisp1_stat_buffer *pbuf) { struct rkisp1_device *rkisp1 = stats->rkisp1; - const struct rkisp1_isp_mbus_info *in_fmt = rkisp1->isp.sink_fmt; + const struct rkisp1_mbus_info *in_fmt = rkisp1->isp.sink_fmt; struct rkisp1_cif_isp_bls_meas_val *bls_val; bls_val = &pbuf->params.ae.bls_val; @@ -408,7 +408,7 @@ void rkisp1_stats_isr(struct rkisp1_stats *stats, u32 isp_ris) spin_lock(&stats->lock); - rkisp1_write(rkisp1, RKISP1_STATS_MEAS_MASK, RKISP1_CIF_ISP_ICR); + rkisp1_write(rkisp1, RKISP1_CIF_ISP_ICR, RKISP1_STATS_MEAS_MASK); isp_mis_tmp = rkisp1_read(rkisp1, RKISP1_CIF_ISP_MIS); if (isp_mis_tmp & RKISP1_STATS_MEAS_MASK) @@ -427,7 +427,7 @@ static void rkisp1_init_stats(struct rkisp1_stats *stats) stats->vdev_fmt.fmt.meta.buffersize = sizeof(struct rkisp1_stat_buffer); - if (stats->rkisp1->media_dev.hw_revision == RKISP1_V12) + if (stats->rkisp1->info->isp_ver == RKISP1_V12) stats->ops = &rkisp1_v12_stats_ops; else stats->ops = &rkisp1_v10_stats_ops; @@ -463,21 +463,21 @@ int rkisp1_stats_register(struct rkisp1_device *rkisp1) node->pad.flags = MEDIA_PAD_FL_SINK; ret = media_entity_pads_init(&vdev->entity, 1, &node->pad); if (ret) - goto err_mutex_destroy; + goto error; ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) { dev_err(&vdev->dev, "failed to register %s, ret=%d\n", vdev->name, ret); - goto err_cleanup_media_entity; + goto error; } return 0; -err_cleanup_media_entity: +error: media_entity_cleanup(&vdev->entity); -err_mutex_destroy: mutex_destroy(&node->vlock); + stats->rkisp1 = NULL; return ret; } @@ -487,6 +487,9 @@ void rkisp1_stats_unregister(struct rkisp1_device *rkisp1) struct rkisp1_vdev_node *node = &stats->vnode; struct video_device *vdev = &node->vdev; + if (!stats->rkisp1) + return; + vb2_video_unregister_device(vdev); media_entity_cleanup(&vdev->entity); mutex_destroy(&node->vlock); diff --git a/drivers/media/platform/samsung/exynos-gsc/gsc-core.c b/drivers/media/platform/samsung/exynos-gsc/gsc-core.c index e3559b047092..b147c645ae0b 100644 --- a/drivers/media/platform/samsung/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/samsung/exynos-gsc/gsc-core.c @@ -339,8 +339,7 @@ static int get_plane_info(struct gsc_frame *frm, u32 addr, u32 *index, u32 *ret_ void gsc_set_prefbuf(struct gsc_dev *gsc, struct gsc_frame *frm) { - u32 f_chk_addr, f_chk_len, s_chk_addr, s_chk_len; - f_chk_addr = f_chk_len = s_chk_addr = s_chk_len = 0; + u32 f_chk_addr, f_chk_len, s_chk_addr = 0, s_chk_len = 0; f_chk_addr = frm->addr.y; f_chk_len = frm->payload[0]; diff --git a/drivers/media/platform/samsung/exynos-gsc/gsc-core.h b/drivers/media/platform/samsung/exynos-gsc/gsc-core.h index e894e85e84a4..1ea5fa1bf3c8 100644 --- a/drivers/media/platform/samsung/exynos-gsc/gsc-core.h +++ b/drivers/media/platform/samsung/exynos-gsc/gsc-core.h @@ -222,7 +222,7 @@ struct gsc_m2m_device { * @org_scaler_input_w: max pixel width when the scaler is enabled * @org_scaler_input_h: max pixel height when the scaler is enabled * @real_rot_dis_w: max pixel src cropped height with the rotator is off - * @real_rot_dis_h: max pixel src croppped width with the rotator is off + * @real_rot_dis_h: max pixel src cropped width with the rotator is off * @real_rot_en_w: max pixel src cropped width with the rotator is on * @real_rot_en_h: max pixel src cropped height with the rotator is on * @target_rot_dis_w: max pixel dst scaled width with the rotator is off diff --git a/drivers/media/platform/samsung/exynos4-is/common.c b/drivers/media/platform/samsung/exynos4-is/common.c index 26ee2388edfd..e41333535eac 100644 --- a/drivers/media/platform/samsung/exynos4-is/common.c +++ b/drivers/media/platform/samsung/exynos4-is/common.c @@ -21,7 +21,7 @@ struct v4l2_subdev *fimc_find_remote_sensor(struct media_entity *entity) while (pad->flags & MEDIA_PAD_FL_SINK) { /* source pad */ - pad = media_entity_remote_pad(pad); + pad = media_pad_remote_pad_first(pad); if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-capture.c b/drivers/media/platform/samsung/exynos4-is/fimc-capture.c index 7ff4024003f4..03638c8f772d 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-capture.c @@ -737,7 +737,7 @@ static struct media_entity *fimc_pipeline_get_head(struct media_entity *me) struct media_pad *pad = &me->pads[0]; while (!(pad->flags & MEDIA_PAD_FL_SOURCE)) { - pad = media_entity_remote_pad(pad); + pad = media_pad_remote_pad_first(pad); if (!pad) break; me = pad->entity; @@ -810,7 +810,7 @@ static int fimc_pipeline_try_format(struct fimc_ctx *ctx, return ret; } - pad = media_entity_remote_pad(&me->pads[sfmt.pad]); + pad = media_pad_remote_pad_first(&me->pads[sfmt.pad]); if (!pad) return -EINVAL; me = pad->entity; @@ -1115,7 +1115,7 @@ static int fimc_pipeline_validate(struct fimc_dev *fimc) if (p->flags & MEDIA_PAD_FL_SINK) { sink_pad = p; - src_pad = media_entity_remote_pad(sink_pad); + src_pad = media_pad_remote_pad_first(sink_pad); if (src_pad) break; } diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.h b/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.h index da36b48b8f9f..9dcbb9853ac0 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.h +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.h @@ -116,7 +116,7 @@ enum fimc_is_error { ERROR_COMMON_PARAMETER = 2, /* Invalid parameter */ /* setfile is not loaded before adjusting */ ERROR_COMMON_SETFILE_LOAD = 3, - /* setfile is not Adjusted before runnng. */ + /* setfile is not Adjusted before running. */ ERROR_COMMON_SETFILE_ADJUST = 4, /* Index of setfile is not valid (0~MAX_SETFILE_NUM-1) */ ERROR_COMMON_SETFILE_INDEX = 5, diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-isp-video.c b/drivers/media/platform/samsung/exynos4-is/fimc-isp-video.c index 83688a7982f7..8f12240b0eb7 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-isp-video.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-isp-video.c @@ -465,7 +465,7 @@ static int isp_video_pipeline_validate(struct fimc_isp *isp) return -EPIPE; /* Retrieve format at the source pad */ - pad = media_entity_remote_pad(pad); + pad = media_pad_remote_pad_first(pad); if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c index 1a396b7cd9a9..41b0a4a5929a 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c @@ -789,7 +789,7 @@ static int fimc_pipeline_validate(struct fimc_lite *fimc) return -EPIPE; } /* Retrieve format at the source pad */ - pad = media_entity_remote_pad(pad); + pad = media_pad_remote_pad_first(pad); if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; diff --git a/drivers/media/platform/samsung/exynos4-is/media-dev.c b/drivers/media/platform/samsung/exynos4-is/media-dev.c index 544b54e428c9..52b43ea04030 100644 --- a/drivers/media/platform/samsung/exynos4-is/media-dev.c +++ b/drivers/media/platform/samsung/exynos4-is/media-dev.c @@ -81,7 +81,7 @@ static void fimc_pipeline_prepare(struct fimc_pipeline *p, struct media_pad *spad = &me->pads[i]; if (!(spad->flags & MEDIA_PAD_FL_SINK)) continue; - pad = media_entity_remote_pad(spad); + pad = media_pad_remote_pad_first(spad); if (pad) break; } diff --git a/drivers/media/platform/samsung/exynos4-is/mipi-csis.c b/drivers/media/platform/samsung/exynos4-is/mipi-csis.c index 27a214936cb0..6a0d35f33e8c 100644 --- a/drivers/media/platform/samsung/exynos4-is/mipi-csis.c +++ b/drivers/media/platform/samsung/exynos4-is/mipi-csis.c @@ -124,7 +124,7 @@ static char *csi_clock_name[] = { #define DEFAULT_SCLK_CSIS_FREQ 166000000UL static const char * const csis_supply_name[] = { - "vddcore", /* CSIS Core (1.0V, 1.1V or 1.2V) suppply */ + "vddcore", /* CSIS Core (1.0V, 1.1V or 1.2V) supply */ "vddio", /* CSIS I/O and PLL (1.8V) supply */ }; #define CSIS_NUM_SUPPLIES ARRAY_SIZE(csis_supply_name) diff --git a/drivers/media/platform/samsung/s3c-camif/camif-capture.c b/drivers/media/platform/samsung/s3c-camif/camif-capture.c index 140854ab4dd8..c2d8f1e425d8 100644 --- a/drivers/media/platform/samsung/s3c-camif/camif-capture.c +++ b/drivers/media/platform/samsung/s3c-camif/camif-capture.c @@ -811,7 +811,7 @@ static int camif_pipeline_validate(struct camif_dev *camif) int ret; /* Retrieve format at the sensor subdev source pad */ - pad = media_entity_remote_pad(&camif->pads[0]); + pad = media_pad_remote_pad_first(&camif->pads[0]); if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) return -EPIPE; diff --git a/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c b/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c index 456287186ad8..55814041b8d8 100644 --- a/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c @@ -1709,7 +1709,7 @@ static int exynos3250_jpeg_try_downscale(struct s5p_jpeg_ctx *ctx, w_ratio = ctx->out_q.w / r->width; h_ratio = ctx->out_q.h / r->height; - scale_factor = w_ratio > h_ratio ? w_ratio : h_ratio; + scale_factor = max(w_ratio, h_ratio); scale_factor = clamp_val(scale_factor, 1, 8); /* Align scale ratio to the nearest power of 2 */ diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_pm.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_pm.c index 72a901e99450..187849841a28 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_pm.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_pm.c @@ -88,7 +88,6 @@ int s5p_mfc_power_on(void) if (ret < 0) { mfc_err("clock prepare failed for clock: %s\n", pm->clk_names[i]); - i++; goto err; } } @@ -98,7 +97,7 @@ int s5p_mfc_power_on(void) return 0; err: - while (--i > 0) + while (--i >= 0) clk_disable_unprepare(pm->clocks[i]); pm_runtime_put(pm->device); return ret; diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c index 7bb1384e4bad..cefe6b7bfdc4 100644 --- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c +++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c @@ -107,7 +107,7 @@ static void channel_swdemux_tsklet(struct tasklet_struct *t) size, DMA_FROM_DEVICE); - buf = (u8 *) channel->back_buffer_aligned; + buf = channel->back_buffer_aligned; dev_dbg(fei->dev, "chan=%d channel=%p num_packets = %d, buf = %p, pos = 0x%x\n\trp=0x%lx, wp=0x%lx\n", @@ -176,7 +176,7 @@ static int c8sectpfe_start_feed(struct dvb_demux_feed *dvbdmxfeed) channel = fei->channel_data[stdemux->tsin_index]; - bitmap = (unsigned long *) channel->pid_buffer_aligned; + bitmap = channel->pid_buffer_aligned; /* 8192 is a special PID */ if (dvbdmxfeed->pid == 8192) { @@ -272,7 +272,7 @@ static int c8sectpfe_stop_feed(struct dvb_demux_feed *dvbdmxfeed) channel = fei->channel_data[stdemux->tsin_index]; - bitmap = (unsigned long *) channel->pid_buffer_aligned; + bitmap = channel->pid_buffer_aligned; if (dvbdmxfeed->pid == 8192) { tmp = readl(fei->io + C8SECTPFE_IB_PID_SET(channel->tsin_id)); @@ -333,8 +333,7 @@ static int c8sectpfe_stop_feed(struct dvb_demux_feed *dvbdmxfeed) __func__, __LINE__, stdemux, channel->tsin_id); /* turn off all PIDS in the bitmap */ - memset((void *)channel->pid_buffer_aligned - , 0x00, PID_TABLE_SIZE); + memset(channel->pid_buffer_aligned, 0, PID_TABLE_SIZE); /* manage cache so data is visible to HW */ dma_sync_single_for_device(fei->dev, @@ -458,23 +457,19 @@ static int configure_memdma_and_inputblock(struct c8sectpfei *fei, init_completion(&tsin->idle_completion); - tsin->back_buffer_start = kzalloc(FEI_BUFFER_SIZE + - FEI_ALIGNMENT, GFP_KERNEL); - + tsin->back_buffer_start = kzalloc(FEI_BUFFER_SIZE + FEI_ALIGNMENT, GFP_KERNEL); if (!tsin->back_buffer_start) { ret = -ENOMEM; goto err_unmap; } /* Ensure backbuffer is 32byte aligned */ - tsin->back_buffer_aligned = tsin->back_buffer_start - + FEI_ALIGNMENT; + tsin->back_buffer_aligned = tsin->back_buffer_start + FEI_ALIGNMENT; - tsin->back_buffer_aligned = (void *) - (((uintptr_t) tsin->back_buffer_aligned) & ~0x1F); + tsin->back_buffer_aligned = PTR_ALIGN(tsin->back_buffer_aligned, FEI_ALIGNMENT); tsin->back_buffer_busaddr = dma_map_single(fei->dev, - (void *)tsin->back_buffer_aligned, + tsin->back_buffer_aligned, FEI_BUFFER_SIZE, DMA_BIDIRECTIONAL); @@ -489,8 +484,7 @@ static int configure_memdma_and_inputblock(struct c8sectpfei *fei, * per pid. By powers of deduction we conclude stih407 family * is configured (at SoC design stage) for bit per pid. */ - tsin->pid_buffer_start = kzalloc(2048, GFP_KERNEL); - + tsin->pid_buffer_start = kzalloc(PID_TABLE_SIZE + PID_TABLE_SIZE, GFP_KERNEL); if (!tsin->pid_buffer_start) { ret = -ENOMEM; goto err_unmap; @@ -503,11 +497,9 @@ static int configure_memdma_and_inputblock(struct c8sectpfei *fei, * the register. */ - tsin->pid_buffer_aligned = tsin->pid_buffer_start + - PID_TABLE_SIZE; + tsin->pid_buffer_aligned = tsin->pid_buffer_start + PID_TABLE_SIZE; - tsin->pid_buffer_aligned = (void *) - (((uintptr_t) tsin->pid_buffer_aligned) & ~0x3ff); + tsin->pid_buffer_aligned = PTR_ALIGN(tsin->pid_buffer_aligned, PID_TABLE_SIZE); tsin->pid_buffer_busaddr = dma_map_single(fei->dev, tsin->pid_buffer_aligned, @@ -915,8 +907,7 @@ static int c8sectpfe_remove(struct platform_device *pdev) if (readl(fei->io + SYS_OTHER_CLKEN)) writel(0, fei->io + SYS_OTHER_CLKEN); - if (fei->c8sectpfeclk) - clk_disable_unprepare(fei->c8sectpfeclk); + clk_disable_unprepare(fei->c8sectpfeclk); return 0; } diff --git a/drivers/media/platform/st/sti/delta/delta-v4l2.c b/drivers/media/platform/st/sti/delta/delta-v4l2.c index 420ad4d8df5d..03eaee6d15da 100644 --- a/drivers/media/platform/st/sti/delta/delta-v4l2.c +++ b/drivers/media/platform/st/sti/delta/delta-v4l2.c @@ -1669,14 +1669,12 @@ static int delta_open(struct file *file) set_default_params(ctx); /* enable ST231 clocks */ - if (delta->clk_st231) - if (clk_prepare_enable(delta->clk_st231)) - dev_warn(delta->dev, "failed to enable st231 clk\n"); + if (clk_prepare_enable(delta->clk_st231)) + dev_warn(delta->dev, "failed to enable st231 clk\n"); /* enable FLASH_PROMIP clock */ - if (delta->clk_flash_promip) - if (clk_prepare_enable(delta->clk_flash_promip)) - dev_warn(delta->dev, "failed to enable delta promip clk\n"); + if (clk_prepare_enable(delta->clk_flash_promip)) + dev_warn(delta->dev, "failed to enable delta promip clk\n"); mutex_unlock(&delta->lock); @@ -1717,12 +1715,10 @@ static int delta_release(struct file *file) v4l2_fh_exit(&ctx->fh); /* disable ST231 clocks */ - if (delta->clk_st231) - clk_disable_unprepare(delta->clk_st231); + clk_disable_unprepare(delta->clk_st231); /* disable FLASH_PROMIP clock */ - if (delta->clk_flash_promip) - clk_disable_unprepare(delta->clk_flash_promip); + clk_disable_unprepare(delta->clk_flash_promip); dev_dbg(delta->dev, "%s decoder instance released\n", ctx->name); @@ -1926,8 +1922,7 @@ static int delta_runtime_suspend(struct device *dev) { struct delta_dev *delta = dev_get_drvdata(dev); - if (delta->clk_delta) - clk_disable_unprepare(delta->clk_delta); + clk_disable_unprepare(delta->clk_delta); return 0; } @@ -1936,9 +1931,8 @@ static int delta_runtime_resume(struct device *dev) { struct delta_dev *delta = dev_get_drvdata(dev); - if (delta->clk_delta) - if (clk_prepare_enable(delta->clk_delta)) - dev_warn(dev, "failed to prepare/enable delta clk\n"); + if (clk_prepare_enable(delta->clk_delta)) + dev_warn(dev, "failed to prepare/enable delta clk\n"); return 0; } diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c index 09a743cd7004..2ca95ab2b0fe 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmi.c +++ b/drivers/media/platform/st/stm32/stm32-dcmi.c @@ -611,7 +611,7 @@ static struct media_entity *dcmi_find_source(struct stm32_dcmi *dcmi) if (!(pad->flags & MEDIA_PAD_FL_SINK)) break; - pad = media_entity_remote_pad(pad); + pad = media_pad_remote_pad_first(pad); if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; @@ -622,7 +622,6 @@ static struct media_entity *dcmi_find_source(struct stm32_dcmi *dcmi) } static int dcmi_pipeline_s_fmt(struct stm32_dcmi *dcmi, - struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct media_entity *entity = &dcmi->source->entity; @@ -664,7 +663,7 @@ static int dcmi_pipeline_s_fmt(struct stm32_dcmi *dcmi, format->format.width, format->format.height); fmt.pad = pad->index; - ret = v4l2_subdev_call(subdev, pad, set_fmt, sd_state, &fmt); + ret = v4l2_subdev_call(subdev, pad, set_fmt, NULL, &fmt); if (ret < 0) { dev_err(dcmi->dev, "%s: Failed to set format 0x%x %ux%u on \"%s\":%d pad (%d)\n", __func__, format->format.code, @@ -682,7 +681,7 @@ static int dcmi_pipeline_s_fmt(struct stm32_dcmi *dcmi, } /* Walk to next entity */ - sink_pad = media_entity_remote_pad(src_pad); + sink_pad = media_pad_remote_pad_first(src_pad); if (!sink_pad || !is_media_entity_v4l2_subdev(sink_pad->entity)) break; @@ -706,7 +705,7 @@ static int dcmi_pipeline_s_stream(struct stm32_dcmi *dcmi, int state) if (!(pad->flags & MEDIA_PAD_FL_SINK)) break; - pad = media_entity_remote_pad(pad); + pad = media_pad_remote_pad_first(pad); if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; @@ -999,10 +998,6 @@ static int dcmi_try_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f, const struct dcmi_format *sd_fmt; struct dcmi_framesize sd_fsize; struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_subdev_pad_config pad_cfg; - struct v4l2_subdev_state pad_state = { - .pads = &pad_cfg - }; struct v4l2_subdev_format format = { .which = V4L2_SUBDEV_FORMAT_TRY, }; @@ -1037,8 +1032,7 @@ static int dcmi_try_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f, } v4l2_fill_mbus_format(&format.format, pix, sd_fmt->mbus_code); - ret = v4l2_subdev_call(dcmi->source, pad, set_fmt, - &pad_state, &format); + ret = v4l2_subdev_call_state_try(dcmi->source, pad, set_fmt, &format); if (ret < 0) return ret; @@ -1115,7 +1109,7 @@ static int dcmi_set_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f) mf->width = sd_framesize.width; mf->height = sd_framesize.height; - ret = dcmi_pipeline_s_fmt(dcmi, NULL, &format); + ret = dcmi_pipeline_s_fmt(dcmi, &format); if (ret < 0) return ret; @@ -1187,10 +1181,6 @@ static int dcmi_set_sensor_format(struct stm32_dcmi *dcmi, struct v4l2_subdev_format format = { .which = V4L2_SUBDEV_FORMAT_TRY, }; - struct v4l2_subdev_pad_config pad_cfg; - struct v4l2_subdev_state pad_state = { - .pads = &pad_cfg - }; int ret; sd_fmt = find_format_by_fourcc(dcmi, pix->pixelformat); @@ -1203,8 +1193,7 @@ static int dcmi_set_sensor_format(struct stm32_dcmi *dcmi, } v4l2_fill_mbus_format(&format.format, pix, sd_fmt->mbus_code); - ret = v4l2_subdev_call(dcmi->source, pad, set_fmt, - &pad_state, &format); + ret = v4l2_subdev_call_state_try(dcmi->source, pad, set_fmt, &format); if (ret < 0) return ret; @@ -1592,26 +1581,32 @@ static int dcmi_set_default_fmt(struct stm32_dcmi *dcmi) return 0; } -/* - * FIXME: For the time being we only support subdevices - * which expose RGB & YUV "parallel form" mbus code (_2X8). - * Nevertheless, this allows to support serial source subdevices - * and serial to parallel bridges which conform to this. - */ static const struct dcmi_format dcmi_formats[] = { { .fourcc = V4L2_PIX_FMT_RGB565, .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE, .bpp = 2, }, { + .fourcc = V4L2_PIX_FMT_RGB565, + .mbus_code = MEDIA_BUS_FMT_RGB565_1X16, + .bpp = 2, + }, { .fourcc = V4L2_PIX_FMT_YUYV, .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, .bpp = 2, }, { + .fourcc = V4L2_PIX_FMT_YUYV, + .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16, + .bpp = 2, + }, { .fourcc = V4L2_PIX_FMT_UYVY, .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, .bpp = 2, }, { + .fourcc = V4L2_PIX_FMT_UYVY, + .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16, + .bpp = 2, + }, { .fourcc = V4L2_PIX_FMT_JPEG, .mbus_code = MEDIA_BUS_FMT_JPEG_1X8, .bpp = 1, @@ -1631,6 +1626,54 @@ static const struct dcmi_format dcmi_formats[] = { .fourcc = V4L2_PIX_FMT_SRGGB8, .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, .bpp = 1, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, + .bpp = 2, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG10, + .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, + .bpp = 2, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG10, + .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, + .bpp = 2, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB10, + .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, + .bpp = 2, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR12, + .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, + .bpp = 2, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG12, + .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12, + .bpp = 2, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG12, + .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, + .bpp = 2, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB12, + .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12, + .bpp = 2, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR14, + .mbus_code = MEDIA_BUS_FMT_SBGGR14_1X14, + .bpp = 2, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG14, + .mbus_code = MEDIA_BUS_FMT_SGBRG14_1X14, + .bpp = 2, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG14, + .mbus_code = MEDIA_BUS_FMT_SGRBG14_1X14, + .bpp = 2, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB14, + .mbus_code = MEDIA_BUS_FMT_SRGGB14_1X14, + .bpp = 2, }, }; diff --git a/drivers/media/platform/sunxi/Kconfig b/drivers/media/platform/sunxi/Kconfig index 46b7b9bf989c..2dd15083a1d9 100644 --- a/drivers/media/platform/sunxi/Kconfig +++ b/drivers/media/platform/sunxi/Kconfig @@ -4,5 +4,7 @@ comment "Sunxi media platform drivers" source "drivers/media/platform/sunxi/sun4i-csi/Kconfig" source "drivers/media/platform/sunxi/sun6i-csi/Kconfig" +source "drivers/media/platform/sunxi/sun6i-mipi-csi2/Kconfig" +source "drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Kconfig" source "drivers/media/platform/sunxi/sun8i-di/Kconfig" source "drivers/media/platform/sunxi/sun8i-rotate/Kconfig" diff --git a/drivers/media/platform/sunxi/Makefile b/drivers/media/platform/sunxi/Makefile index fc537c9f5ca9..9aa01cb01883 100644 --- a/drivers/media/platform/sunxi/Makefile +++ b/drivers/media/platform/sunxi/Makefile @@ -2,5 +2,7 @@ obj-y += sun4i-csi/ obj-y += sun6i-csi/ +obj-y += sun6i-mipi-csi2/ +obj-y += sun8i-a83t-mipi-csi2/ obj-y += sun8i-di/ obj-y += sun8i-rotate/ diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c index 682c26536034..1d46e113d01d 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c @@ -77,7 +77,7 @@ sun6i_video_remote_subdev(struct sun6i_video *video, u32 *pad) { struct media_pad *remote; - remote = media_entity_remote_pad(&video->pad); + remote = media_pad_remote_pad_first(&video->pad); if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) return NULL; @@ -560,7 +560,7 @@ static int sun6i_video_link_validate(struct media_link *link) video->mbus_code = 0; - if (!media_entity_remote_pad(link->sink->entity->pads)) { + if (!media_pad_remote_pad_first(link->sink->entity->pads)) { dev_info(video->csi->dev, "video node %s pad not connected\n", vdev->name); return -ENOLINK; diff --git a/drivers/media/platform/sunxi/sun6i-mipi-csi2/Kconfig b/drivers/media/platform/sunxi/sun6i-mipi-csi2/Kconfig new file mode 100644 index 000000000000..eb982466abd3 --- /dev/null +++ b/drivers/media/platform/sunxi/sun6i-mipi-csi2/Kconfig @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_SUN6I_MIPI_CSI2 + tristate "Allwinner A31 MIPI CSI-2 Controller Driver" + depends on V4L_PLATFORM_DRIVERS && VIDEO_DEV + depends on ARCH_SUNXI || COMPILE_TEST + depends on PM && COMMON_CLK + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + select PHY_SUN6I_MIPI_DPHY + select GENERIC_PHY_MIPI_DPHY + select REGMAP_MMIO + help + Support for the Allwinner A31 MIPI CSI-2 controller, also found on + other platforms such as the V3/V3s. diff --git a/drivers/media/platform/sunxi/sun6i-mipi-csi2/Makefile b/drivers/media/platform/sunxi/sun6i-mipi-csi2/Makefile new file mode 100644 index 000000000000..14e4e03818b5 --- /dev/null +++ b/drivers/media/platform/sunxi/sun6i-mipi-csi2/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only +sun6i-mipi-csi2-y += sun6i_mipi_csi2.o + +obj-$(CONFIG_VIDEO_SUN6I_MIPI_CSI2) += sun6i-mipi-csi2.o diff --git a/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c b/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c new file mode 100644 index 000000000000..a4e3f9a6b2ff --- /dev/null +++ b/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c @@ -0,0 +1,750 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2020-2022 Bootlin + * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com> + */ + +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <media/mipi-csi2.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> + +#include "sun6i_mipi_csi2.h" +#include "sun6i_mipi_csi2_reg.h" + +/* Format */ + +static const struct sun6i_mipi_csi2_format sun6i_mipi_csi2_formats[] = { + { + .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, + .data_type = MIPI_CSI2_DT_RAW8, + .bpp = 8, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, + .data_type = MIPI_CSI2_DT_RAW8, + .bpp = 8, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, + .data_type = MIPI_CSI2_DT_RAW8, + .bpp = 8, + }, + { + .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, + .data_type = MIPI_CSI2_DT_RAW8, + .bpp = 8, + }, + { + .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, + .data_type = MIPI_CSI2_DT_RAW10, + .bpp = 10, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, + .data_type = MIPI_CSI2_DT_RAW10, + .bpp = 10, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, + .data_type = MIPI_CSI2_DT_RAW10, + .bpp = 10, + }, + { + .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, + .data_type = MIPI_CSI2_DT_RAW10, + .bpp = 10, + }, +}; + +static const struct sun6i_mipi_csi2_format * +sun6i_mipi_csi2_format_find(u32 mbus_code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(sun6i_mipi_csi2_formats); i++) + if (sun6i_mipi_csi2_formats[i].mbus_code == mbus_code) + return &sun6i_mipi_csi2_formats[i]; + + return NULL; +} + +/* Controller */ + +static void sun6i_mipi_csi2_enable(struct sun6i_mipi_csi2_device *csi2_dev) +{ + struct regmap *regmap = csi2_dev->regmap; + + regmap_update_bits(regmap, SUN6I_MIPI_CSI2_CTL_REG, + SUN6I_MIPI_CSI2_CTL_EN, SUN6I_MIPI_CSI2_CTL_EN); +} + +static void sun6i_mipi_csi2_disable(struct sun6i_mipi_csi2_device *csi2_dev) +{ + struct regmap *regmap = csi2_dev->regmap; + + regmap_update_bits(regmap, SUN6I_MIPI_CSI2_CTL_REG, + SUN6I_MIPI_CSI2_CTL_EN, 0); +} + +static void sun6i_mipi_csi2_configure(struct sun6i_mipi_csi2_device *csi2_dev) +{ + struct regmap *regmap = csi2_dev->regmap; + unsigned int lanes_count = + csi2_dev->bridge.endpoint.bus.mipi_csi2.num_data_lanes; + struct v4l2_mbus_framefmt *mbus_format = &csi2_dev->bridge.mbus_format; + const struct sun6i_mipi_csi2_format *format; + struct device *dev = csi2_dev->dev; + u32 version = 0; + + format = sun6i_mipi_csi2_format_find(mbus_format->code); + if (WARN_ON(!format)) + return; + + /* + * The enable flow in the Allwinner BSP is a bit different: the enable + * and reset bits are set together before starting the CSI controller. + * + * In mainline we enable the CSI controller first (due to subdev logic). + * One reliable way to make this work is to deassert reset, configure + * registers and enable the controller when everything's ready. + * + * However, setting the version enable bit and removing it afterwards + * appears necessary for capture to work reliably, while replacing it + * with a delay doesn't do the trick. + */ + regmap_write(regmap, SUN6I_MIPI_CSI2_CTL_REG, + SUN6I_MIPI_CSI2_CTL_RESET_N | + SUN6I_MIPI_CSI2_CTL_VERSION_EN | + SUN6I_MIPI_CSI2_CTL_UNPK_EN); + + regmap_read(regmap, SUN6I_MIPI_CSI2_VERSION_REG, &version); + + regmap_update_bits(regmap, SUN6I_MIPI_CSI2_CTL_REG, + SUN6I_MIPI_CSI2_CTL_VERSION_EN, 0); + + dev_dbg(dev, "A31 MIPI CSI-2 version: %04x\n", version); + + regmap_write(regmap, SUN6I_MIPI_CSI2_CFG_REG, + SUN6I_MIPI_CSI2_CFG_CHANNEL_MODE(1) | + SUN6I_MIPI_CSI2_CFG_LANE_COUNT(lanes_count)); + + /* + * Only a single virtual channel (index 0) is currently supported. + * While the registers do mention multiple physical channels being + * available (which can be configured to match a specific virtual + * channel or data type), it's unclear whether channels > 0 are actually + * connected and available and the reference source code only makes use + * of channel 0. + * + * Using extra channels would also require matching channels to be + * available on the CSI (and ISP) side, which is also unsure although + * some CSI implementations are said to support multiple channels for + * BT656 time-sharing. + * + * We still configure virtual channel numbers to ensure that virtual + * channel 0 only goes to channel 0. + */ + + regmap_write(regmap, SUN6I_MIPI_CSI2_VCDT_RX_REG, + SUN6I_MIPI_CSI2_VCDT_RX_CH_VC(3, 3) | + SUN6I_MIPI_CSI2_VCDT_RX_CH_VC(2, 2) | + SUN6I_MIPI_CSI2_VCDT_RX_CH_VC(1, 1) | + SUN6I_MIPI_CSI2_VCDT_RX_CH_VC(0, 0) | + SUN6I_MIPI_CSI2_VCDT_RX_CH_DT(0, format->data_type)); + + regmap_write(regmap, SUN6I_MIPI_CSI2_CH_INT_PD_REG, + SUN6I_MIPI_CSI2_CH_INT_PD_CLEAR); +} + +/* V4L2 Subdev */ + +static int sun6i_mipi_csi2_s_stream(struct v4l2_subdev *subdev, int on) +{ + struct sun6i_mipi_csi2_device *csi2_dev = v4l2_get_subdevdata(subdev); + struct v4l2_subdev *source_subdev = csi2_dev->bridge.source_subdev; + union phy_configure_opts dphy_opts = { 0 }; + struct phy_configure_opts_mipi_dphy *dphy_cfg = &dphy_opts.mipi_dphy; + struct v4l2_mbus_framefmt *mbus_format = &csi2_dev->bridge.mbus_format; + const struct sun6i_mipi_csi2_format *format; + struct phy *dphy = csi2_dev->dphy; + struct device *dev = csi2_dev->dev; + struct v4l2_ctrl *ctrl; + unsigned int lanes_count = + csi2_dev->bridge.endpoint.bus.mipi_csi2.num_data_lanes; + unsigned long pixel_rate; + int ret; + + if (!source_subdev) + return -ENODEV; + + if (!on) { + ret = v4l2_subdev_call(source_subdev, video, s_stream, 0); + goto disable; + } + + /* Runtime PM */ + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + /* Sensor Pixel Rate */ + + ctrl = v4l2_ctrl_find(source_subdev->ctrl_handler, V4L2_CID_PIXEL_RATE); + if (!ctrl) { + dev_err(dev, "missing sensor pixel rate\n"); + ret = -ENODEV; + goto error_pm; + } + + pixel_rate = (unsigned long)v4l2_ctrl_g_ctrl_int64(ctrl); + if (!pixel_rate) { + dev_err(dev, "missing (zero) sensor pixel rate\n"); + ret = -ENODEV; + goto error_pm; + } + + /* D-PHY */ + + if (!lanes_count) { + dev_err(dev, "missing (zero) MIPI CSI-2 lanes count\n"); + ret = -ENODEV; + goto error_pm; + } + + format = sun6i_mipi_csi2_format_find(mbus_format->code); + if (WARN_ON(!format)) { + ret = -ENODEV; + goto error_pm; + } + + phy_mipi_dphy_get_default_config(pixel_rate, format->bpp, lanes_count, + dphy_cfg); + + /* + * Note that our hardware is using DDR, which is not taken in account by + * phy_mipi_dphy_get_default_config when calculating hs_clk_rate from + * the pixel rate, lanes count and bpp. + * + * The resulting clock rate is basically the symbol rate over the whole + * link. The actual clock rate is calculated with division by two since + * DDR samples both on rising and falling edges. + */ + + dev_dbg(dev, "A31 MIPI CSI-2 config:\n"); + dev_dbg(dev, "%ld pixels/s, %u bits/pixel, %u lanes, %lu Hz clock\n", + pixel_rate, format->bpp, lanes_count, + dphy_cfg->hs_clk_rate / 2); + + ret = phy_reset(dphy); + if (ret) { + dev_err(dev, "failed to reset MIPI D-PHY\n"); + goto error_pm; + } + + ret = phy_configure(dphy, &dphy_opts); + if (ret) { + dev_err(dev, "failed to configure MIPI D-PHY\n"); + goto error_pm; + } + + /* Controller */ + + sun6i_mipi_csi2_configure(csi2_dev); + sun6i_mipi_csi2_enable(csi2_dev); + + /* D-PHY */ + + ret = phy_power_on(dphy); + if (ret) { + dev_err(dev, "failed to power on MIPI D-PHY\n"); + goto error_pm; + } + + /* Source */ + + ret = v4l2_subdev_call(source_subdev, video, s_stream, 1); + if (ret && ret != -ENOIOCTLCMD) + goto disable; + + return 0; + +disable: + if (!on) + ret = 0; + phy_power_off(dphy); + sun6i_mipi_csi2_disable(csi2_dev); + +error_pm: + pm_runtime_put(dev); + + return ret; +} + +static const struct v4l2_subdev_video_ops sun6i_mipi_csi2_video_ops = { + .s_stream = sun6i_mipi_csi2_s_stream, +}; + +static void +sun6i_mipi_csi2_mbus_format_prepare(struct v4l2_mbus_framefmt *mbus_format) +{ + if (!sun6i_mipi_csi2_format_find(mbus_format->code)) + mbus_format->code = sun6i_mipi_csi2_formats[0].mbus_code; + + mbus_format->field = V4L2_FIELD_NONE; + mbus_format->colorspace = V4L2_COLORSPACE_RAW; + mbus_format->quantization = V4L2_QUANTIZATION_DEFAULT; + mbus_format->xfer_func = V4L2_XFER_FUNC_DEFAULT; +} + +static int sun6i_mipi_csi2_init_cfg(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state) +{ + struct sun6i_mipi_csi2_device *csi2_dev = v4l2_get_subdevdata(subdev); + unsigned int pad = SUN6I_MIPI_CSI2_PAD_SINK; + struct v4l2_mbus_framefmt *mbus_format = + v4l2_subdev_get_try_format(subdev, state, pad); + struct mutex *lock = &csi2_dev->bridge.lock; + + mutex_lock(lock); + + mbus_format->code = sun6i_mipi_csi2_formats[0].mbus_code; + mbus_format->width = 640; + mbus_format->height = 480; + + sun6i_mipi_csi2_mbus_format_prepare(mbus_format); + + mutex_unlock(lock); + + return 0; +} + +static int +sun6i_mipi_csi2_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code_enum) +{ + if (code_enum->index >= ARRAY_SIZE(sun6i_mipi_csi2_formats)) + return -EINVAL; + + code_enum->code = sun6i_mipi_csi2_formats[code_enum->index].mbus_code; + + return 0; +} + +static int sun6i_mipi_csi2_get_fmt(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct sun6i_mipi_csi2_device *csi2_dev = v4l2_get_subdevdata(subdev); + struct v4l2_mbus_framefmt *mbus_format = &format->format; + struct mutex *lock = &csi2_dev->bridge.lock; + + mutex_lock(lock); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + *mbus_format = *v4l2_subdev_get_try_format(subdev, state, + format->pad); + else + *mbus_format = csi2_dev->bridge.mbus_format; + + mutex_unlock(lock); + + return 0; +} + +static int sun6i_mipi_csi2_set_fmt(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct sun6i_mipi_csi2_device *csi2_dev = v4l2_get_subdevdata(subdev); + struct v4l2_mbus_framefmt *mbus_format = &format->format; + struct mutex *lock = &csi2_dev->bridge.lock; + + mutex_lock(lock); + + sun6i_mipi_csi2_mbus_format_prepare(mbus_format); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + *v4l2_subdev_get_try_format(subdev, state, format->pad) = + *mbus_format; + else + csi2_dev->bridge.mbus_format = *mbus_format; + + mutex_unlock(lock); + + return 0; +} + +static const struct v4l2_subdev_pad_ops sun6i_mipi_csi2_pad_ops = { + .init_cfg = sun6i_mipi_csi2_init_cfg, + .enum_mbus_code = sun6i_mipi_csi2_enum_mbus_code, + .get_fmt = sun6i_mipi_csi2_get_fmt, + .set_fmt = sun6i_mipi_csi2_set_fmt, +}; + +static const struct v4l2_subdev_ops sun6i_mipi_csi2_subdev_ops = { + .video = &sun6i_mipi_csi2_video_ops, + .pad = &sun6i_mipi_csi2_pad_ops, +}; + +/* Media Entity */ + +static const struct media_entity_operations sun6i_mipi_csi2_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +/* V4L2 Async */ + +static int +sun6i_mipi_csi2_notifier_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *remote_subdev, + struct v4l2_async_subdev *async_subdev) +{ + struct v4l2_subdev *subdev = notifier->sd; + struct sun6i_mipi_csi2_device *csi2_dev = + container_of(notifier, struct sun6i_mipi_csi2_device, + bridge.notifier); + struct media_entity *sink_entity = &subdev->entity; + struct media_entity *source_entity = &remote_subdev->entity; + struct device *dev = csi2_dev->dev; + int sink_pad_index = 0; + int source_pad_index; + int ret; + + ret = media_entity_get_fwnode_pad(source_entity, remote_subdev->fwnode, + MEDIA_PAD_FL_SOURCE); + if (ret < 0) { + dev_err(dev, "missing source pad in external entity %s\n", + source_entity->name); + return -EINVAL; + } + + source_pad_index = ret; + + dev_dbg(dev, "creating %s:%u -> %s:%u link\n", source_entity->name, + source_pad_index, sink_entity->name, sink_pad_index); + + ret = media_create_pad_link(source_entity, source_pad_index, + sink_entity, sink_pad_index, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + dev_err(dev, "failed to create %s:%u -> %s:%u link\n", + source_entity->name, source_pad_index, + sink_entity->name, sink_pad_index); + return ret; + } + + csi2_dev->bridge.source_subdev = remote_subdev; + + return 0; +} + +static const struct v4l2_async_notifier_operations +sun6i_mipi_csi2_notifier_ops = { + .bound = sun6i_mipi_csi2_notifier_bound, +}; + +/* Bridge */ + +static int +sun6i_mipi_csi2_bridge_source_setup(struct sun6i_mipi_csi2_device *csi2_dev) +{ + struct v4l2_async_notifier *notifier = &csi2_dev->bridge.notifier; + struct v4l2_fwnode_endpoint *endpoint = &csi2_dev->bridge.endpoint; + struct v4l2_async_subdev *subdev_async; + struct fwnode_handle *handle; + struct device *dev = csi2_dev->dev; + int ret; + + handle = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!handle) + return -ENODEV; + + endpoint->bus_type = V4L2_MBUS_CSI2_DPHY; + + ret = v4l2_fwnode_endpoint_parse(handle, endpoint); + if (ret) + goto complete; + + subdev_async = + v4l2_async_nf_add_fwnode_remote(notifier, handle, + struct v4l2_async_subdev); + if (IS_ERR(subdev_async)) + ret = PTR_ERR(subdev_async); + +complete: + fwnode_handle_put(handle); + + return ret; +} + +static int sun6i_mipi_csi2_bridge_setup(struct sun6i_mipi_csi2_device *csi2_dev) +{ + struct sun6i_mipi_csi2_bridge *bridge = &csi2_dev->bridge; + struct v4l2_subdev *subdev = &bridge->subdev; + struct v4l2_async_notifier *notifier = &bridge->notifier; + struct media_pad *pads = bridge->pads; + struct device *dev = csi2_dev->dev; + int ret; + + mutex_init(&bridge->lock); + + /* V4L2 Subdev */ + + v4l2_subdev_init(subdev, &sun6i_mipi_csi2_subdev_ops); + strscpy(subdev->name, SUN6I_MIPI_CSI2_NAME, sizeof(subdev->name)); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + subdev->owner = THIS_MODULE; + subdev->dev = dev; + + v4l2_set_subdevdata(subdev, csi2_dev); + + /* Media Entity */ + + subdev->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + subdev->entity.ops = &sun6i_mipi_csi2_entity_ops; + + /* Media Pads */ + + pads[SUN6I_MIPI_CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[SUN6I_MIPI_CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&subdev->entity, SUN6I_MIPI_CSI2_PAD_COUNT, + pads); + if (ret) + return ret; + + /* V4L2 Async */ + + v4l2_async_nf_init(notifier); + notifier->ops = &sun6i_mipi_csi2_notifier_ops; + + ret = sun6i_mipi_csi2_bridge_source_setup(csi2_dev); + if (ret) + goto error_v4l2_notifier_cleanup; + + ret = v4l2_async_subdev_nf_register(subdev, notifier); + if (ret < 0) + goto error_v4l2_notifier_cleanup; + + /* V4L2 Subdev */ + + ret = v4l2_async_register_subdev(subdev); + if (ret < 0) + goto error_v4l2_notifier_unregister; + + return 0; + +error_v4l2_notifier_unregister: + v4l2_async_nf_unregister(notifier); + +error_v4l2_notifier_cleanup: + v4l2_async_nf_cleanup(notifier); + + media_entity_cleanup(&subdev->entity); + + return ret; +} + +static void +sun6i_mipi_csi2_bridge_cleanup(struct sun6i_mipi_csi2_device *csi2_dev) +{ + struct v4l2_subdev *subdev = &csi2_dev->bridge.subdev; + struct v4l2_async_notifier *notifier = &csi2_dev->bridge.notifier; + + v4l2_async_unregister_subdev(subdev); + v4l2_async_nf_unregister(notifier); + v4l2_async_nf_cleanup(notifier); + media_entity_cleanup(&subdev->entity); +} + +/* Platform */ + +static int sun6i_mipi_csi2_suspend(struct device *dev) +{ + struct sun6i_mipi_csi2_device *csi2_dev = dev_get_drvdata(dev); + + clk_disable_unprepare(csi2_dev->clock_mod); + reset_control_assert(csi2_dev->reset); + + return 0; +} + +static int sun6i_mipi_csi2_resume(struct device *dev) +{ + struct sun6i_mipi_csi2_device *csi2_dev = dev_get_drvdata(dev); + int ret; + + ret = reset_control_deassert(csi2_dev->reset); + if (ret) { + dev_err(dev, "failed to deassert reset\n"); + return ret; + } + + ret = clk_prepare_enable(csi2_dev->clock_mod); + if (ret) { + dev_err(dev, "failed to enable module clock\n"); + goto error_reset; + } + + return 0; + +error_reset: + reset_control_assert(csi2_dev->reset); + + return ret; +} + +static const struct dev_pm_ops sun6i_mipi_csi2_pm_ops = { + .runtime_suspend = sun6i_mipi_csi2_suspend, + .runtime_resume = sun6i_mipi_csi2_resume, +}; + +static const struct regmap_config sun6i_mipi_csi2_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x400, +}; + +static int +sun6i_mipi_csi2_resources_setup(struct sun6i_mipi_csi2_device *csi2_dev, + struct platform_device *platform_dev) +{ + struct device *dev = csi2_dev->dev; + void __iomem *io_base; + int ret; + + /* Registers */ + + io_base = devm_platform_ioremap_resource(platform_dev, 0); + if (IS_ERR(io_base)) + return PTR_ERR(io_base); + + csi2_dev->regmap = + devm_regmap_init_mmio_clk(dev, "bus", io_base, + &sun6i_mipi_csi2_regmap_config); + if (IS_ERR(csi2_dev->regmap)) { + dev_err(dev, "failed to init register map\n"); + return PTR_ERR(csi2_dev->regmap); + } + + /* Clock */ + + csi2_dev->clock_mod = devm_clk_get(dev, "mod"); + if (IS_ERR(csi2_dev->clock_mod)) { + dev_err(dev, "failed to acquire mod clock\n"); + return PTR_ERR(csi2_dev->clock_mod); + } + + ret = clk_set_rate_exclusive(csi2_dev->clock_mod, 297000000); + if (ret) { + dev_err(dev, "failed to set mod clock rate\n"); + return ret; + } + + /* Reset */ + + csi2_dev->reset = devm_reset_control_get_shared(dev, NULL); + if (IS_ERR(csi2_dev->reset)) { + dev_err(dev, "failed to get reset controller\n"); + return PTR_ERR(csi2_dev->reset); + } + + /* D-PHY */ + + csi2_dev->dphy = devm_phy_get(dev, "dphy"); + if (IS_ERR(csi2_dev->dphy)) { + dev_err(dev, "failed to get MIPI D-PHY\n"); + return PTR_ERR(csi2_dev->dphy); + } + + ret = phy_init(csi2_dev->dphy); + if (ret) { + dev_err(dev, "failed to initialize MIPI D-PHY\n"); + return ret; + } + + /* Runtime PM */ + + pm_runtime_enable(dev); + + return 0; +} + +static void +sun6i_mipi_csi2_resources_cleanup(struct sun6i_mipi_csi2_device *csi2_dev) +{ + pm_runtime_disable(csi2_dev->dev); + phy_exit(csi2_dev->dphy); + clk_rate_exclusive_put(csi2_dev->clock_mod); +} + +static int sun6i_mipi_csi2_probe(struct platform_device *platform_dev) +{ + struct sun6i_mipi_csi2_device *csi2_dev; + struct device *dev = &platform_dev->dev; + int ret; + + csi2_dev = devm_kzalloc(dev, sizeof(*csi2_dev), GFP_KERNEL); + if (!csi2_dev) + return -ENOMEM; + + csi2_dev->dev = dev; + platform_set_drvdata(platform_dev, csi2_dev); + + ret = sun6i_mipi_csi2_resources_setup(csi2_dev, platform_dev); + if (ret) + return ret; + + ret = sun6i_mipi_csi2_bridge_setup(csi2_dev); + if (ret) + return ret; + + return 0; +} + +static int sun6i_mipi_csi2_remove(struct platform_device *platform_dev) +{ + struct sun6i_mipi_csi2_device *csi2_dev = + platform_get_drvdata(platform_dev); + + sun6i_mipi_csi2_bridge_cleanup(csi2_dev); + sun6i_mipi_csi2_resources_cleanup(csi2_dev); + + return 0; +} + +static const struct of_device_id sun6i_mipi_csi2_of_match[] = { + { .compatible = "allwinner,sun6i-a31-mipi-csi2" }, + {}, +}; +MODULE_DEVICE_TABLE(of, sun6i_mipi_csi2_of_match); + +static struct platform_driver sun6i_mipi_csi2_platform_driver = { + .probe = sun6i_mipi_csi2_probe, + .remove = sun6i_mipi_csi2_remove, + .driver = { + .name = SUN6I_MIPI_CSI2_NAME, + .of_match_table = of_match_ptr(sun6i_mipi_csi2_of_match), + .pm = &sun6i_mipi_csi2_pm_ops, + }, +}; +module_platform_driver(sun6i_mipi_csi2_platform_driver); + +MODULE_DESCRIPTION("Allwinner A31 MIPI CSI-2 Controller Driver"); +MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.h b/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.h new file mode 100644 index 000000000000..24b15e34b5e8 --- /dev/null +++ b/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2020-2022 Bootlin + * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com> + */ + +#ifndef _SUN6I_MIPI_CSI2_H_ +#define _SUN6I_MIPI_CSI2_H_ + +#include <linux/phy/phy.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> + +#define SUN6I_MIPI_CSI2_NAME "sun6i-mipi-csi2" + +enum sun6i_mipi_csi2_pad { + SUN6I_MIPI_CSI2_PAD_SINK = 0, + SUN6I_MIPI_CSI2_PAD_SOURCE = 1, + SUN6I_MIPI_CSI2_PAD_COUNT = 2, +}; + +struct sun6i_mipi_csi2_format { + u32 mbus_code; + u8 data_type; + u32 bpp; +}; + +struct sun6i_mipi_csi2_bridge { + struct v4l2_subdev subdev; + struct media_pad pads[SUN6I_MIPI_CSI2_PAD_COUNT]; + struct v4l2_fwnode_endpoint endpoint; + struct v4l2_async_notifier notifier; + struct v4l2_mbus_framefmt mbus_format; + struct mutex lock; /* Mbus format lock. */ + + struct v4l2_subdev *source_subdev; +}; + +struct sun6i_mipi_csi2_device { + struct device *dev; + + struct regmap *regmap; + struct clk *clock_mod; + struct reset_control *reset; + struct phy *dphy; + + struct sun6i_mipi_csi2_bridge bridge; +}; + +#endif diff --git a/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2_reg.h b/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2_reg.h new file mode 100644 index 000000000000..d9c92cf2b038 --- /dev/null +++ b/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2_reg.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2020-2022 Bootlin + * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com> + */ + +#ifndef _SUN6I_MIPI_CSI2_REG_H_ +#define _SUN6I_MIPI_CSI2_REG_H_ + +#define SUN6I_MIPI_CSI2_CTL_REG 0x0 +#define SUN6I_MIPI_CSI2_CTL_RESET_N BIT(31) +#define SUN6I_MIPI_CSI2_CTL_VERSION_EN BIT(30) +#define SUN6I_MIPI_CSI2_CTL_UNPK_EN BIT(1) +#define SUN6I_MIPI_CSI2_CTL_EN BIT(0) + +#define SUN6I_MIPI_CSI2_CFG_REG 0x4 +#define SUN6I_MIPI_CSI2_CFG_CHANNEL_MODE(v) ((((v) - 1) << 8) & \ + GENMASK(9, 8)) +#define SUN6I_MIPI_CSI2_CFG_LANE_COUNT(v) (((v) - 1) & GENMASK(1, 0)) + +#define SUN6I_MIPI_CSI2_VCDT_RX_REG 0x8 +#define SUN6I_MIPI_CSI2_VCDT_RX_CH_VC(ch, vc) (((vc) & GENMASK(1, 0)) << \ + ((ch) * 8 + 6)) +#define SUN6I_MIPI_CSI2_VCDT_RX_CH_DT(ch, t) (((t) & GENMASK(5, 0)) << \ + ((ch) * 8)) +#define SUN6I_MIPI_CSI2_RX_PKT_NUM_REG 0xc + +#define SUN6I_MIPI_CSI2_VERSION_REG 0x3c + +#define SUN6I_MIPI_CSI2_CH_CFG_REG 0x40 +#define SUN6I_MIPI_CSI2_CH_INT_EN_REG 0x50 +#define SUN6I_MIPI_CSI2_CH_INT_EN_EOT_ERR BIT(29) +#define SUN6I_MIPI_CSI2_CH_INT_EN_CHKSUM_ERR BIT(28) +#define SUN6I_MIPI_CSI2_CH_INT_EN_ECC_WRN BIT(27) +#define SUN6I_MIPI_CSI2_CH_INT_EN_ECC_ERR BIT(26) +#define SUN6I_MIPI_CSI2_CH_INT_EN_LINE_SYNC_ERR BIT(25) +#define SUN6I_MIPI_CSI2_CH_INT_EN_FRAME_SYNC_ERR BIT(24) +#define SUN6I_MIPI_CSI2_CH_INT_EN_EMB_DATA BIT(18) +#define SUN6I_MIPI_CSI2_CH_INT_EN_PF BIT(17) +#define SUN6I_MIPI_CSI2_CH_INT_EN_PH_UPDATE BIT(16) +#define SUN6I_MIPI_CSI2_CH_INT_EN_LINE_START_SYNC BIT(11) +#define SUN6I_MIPI_CSI2_CH_INT_EN_LINE_END_SYNC BIT(10) +#define SUN6I_MIPI_CSI2_CH_INT_EN_FRAME_START_SYNC BIT(9) +#define SUN6I_MIPI_CSI2_CH_INT_EN_FRAME_END_SYNC BIT(8) +#define SUN6I_MIPI_CSI2_CH_INT_EN_FIFO_OVER BIT(0) + +#define SUN6I_MIPI_CSI2_CH_INT_PD_REG 0x58 +#define SUN6I_MIPI_CSI2_CH_INT_PD_CLEAR 0xff +#define SUN6I_MIPI_CSI2_CH_INT_PD_EOT_ERR BIT(29) +#define SUN6I_MIPI_CSI2_CH_INT_PD_CHKSUM_ERR BIT(28) +#define SUN6I_MIPI_CSI2_CH_INT_PD_ECC_WRN BIT(27) +#define SUN6I_MIPI_CSI2_CH_INT_PD_ECC_ERR BIT(26) +#define SUN6I_MIPI_CSI2_CH_INT_PD_LINE_SYNC_ERR BIT(25) +#define SUN6I_MIPI_CSI2_CH_INT_PD_FRAME_SYNC_ERR BIT(24) +#define SUN6I_MIPI_CSI2_CH_INT_PD_EMB_DATA BIT(18) +#define SUN6I_MIPI_CSI2_CH_INT_PD_PF BIT(17) +#define SUN6I_MIPI_CSI2_CH_INT_PD_PH_UPDATE BIT(16) +#define SUN6I_MIPI_CSI2_CH_INT_PD_LINE_START_SYNC BIT(11) +#define SUN6I_MIPI_CSI2_CH_INT_PD_LINE_END_SYNC BIT(10) +#define SUN6I_MIPI_CSI2_CH_INT_PD_FRAME_START_SYNC BIT(9) +#define SUN6I_MIPI_CSI2_CH_INT_PD_FRAME_END_SYNC BIT(8) +#define SUN6I_MIPI_CSI2_CH_INT_PD_FIFO_OVER BIT(0) + +#define SUN6I_MIPI_CSI2_CH_DT_TRIGGER_REG 0x60 +#define SUN6I_MIPI_CSI2_CH_CUR_PH_REG 0x70 +#define SUN6I_MIPI_CSI2_CH_ECC_REG 0x74 +#define SUN6I_MIPI_CSI2_CH_CKS_REG 0x78 +#define SUN6I_MIPI_CSI2_CH_FRAME_NUM_REG 0x7c +#define SUN6I_MIPI_CSI2_CH_LINE_NUM_REG 0x80 + +#define SUN6I_MIPI_CSI2_CH_OFFSET 0x100 + +#define SUN6I_MIPI_CSI2_CH_REG(reg, ch) \ + (SUN6I_MIPI_CSI2_CH_OFFSET * (ch) + (reg)) + +#endif diff --git a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Kconfig b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Kconfig new file mode 100644 index 000000000000..789d58ee12ea --- /dev/null +++ b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Kconfig @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_SUN8I_A83T_MIPI_CSI2 + tristate "Allwinner A83T MIPI CSI-2 Controller and D-PHY Driver" + depends on V4L_PLATFORM_DRIVERS && VIDEO_DEV + depends on ARCH_SUNXI || COMPILE_TEST + depends on PM && COMMON_CLK + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + select REGMAP_MMIO + select GENERIC_PHY_MIPI_DPHY + help + Support for the Allwinner A83T MIPI CSI-2 controller and D-PHY. diff --git a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Makefile b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Makefile new file mode 100644 index 000000000000..1427d15a879a --- /dev/null +++ b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only +sun8i-a83t-mipi-csi2-y += sun8i_a83t_mipi_csi2.o sun8i_a83t_dphy.o + +obj-$(CONFIG_VIDEO_SUN8I_A83T_MIPI_CSI2) += sun8i-a83t-mipi-csi2.o diff --git a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_dphy.c b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_dphy.c new file mode 100644 index 000000000000..24bbcc85013d --- /dev/null +++ b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_dphy.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2020-2022 Bootlin + * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com> + */ + +#include <linux/phy/phy.h> +#include <linux/regmap.h> + +#include "sun8i_a83t_dphy.h" +#include "sun8i_a83t_mipi_csi2.h" + +static int sun8i_a83t_dphy_configure(struct phy *dphy, + union phy_configure_opts *opts) +{ + return phy_mipi_dphy_config_validate(&opts->mipi_dphy); +} + +static int sun8i_a83t_dphy_power_on(struct phy *dphy) +{ + struct sun8i_a83t_mipi_csi2_device *csi2_dev = phy_get_drvdata(dphy); + struct regmap *regmap = csi2_dev->regmap; + + regmap_write(regmap, SUN8I_A83T_DPHY_CTRL_REG, + SUN8I_A83T_DPHY_CTRL_RESET_N | + SUN8I_A83T_DPHY_CTRL_SHUTDOWN_N); + + regmap_write(regmap, SUN8I_A83T_DPHY_ANA0_REG, + SUN8I_A83T_DPHY_ANA0_REXT_EN | + SUN8I_A83T_DPHY_ANA0_RINT(2) | + SUN8I_A83T_DPHY_ANA0_SNK(2)); + + return 0; +}; + +static int sun8i_a83t_dphy_power_off(struct phy *dphy) +{ + struct sun8i_a83t_mipi_csi2_device *csi2_dev = phy_get_drvdata(dphy); + struct regmap *regmap = csi2_dev->regmap; + + regmap_write(regmap, SUN8I_A83T_DPHY_CTRL_REG, 0); + + return 0; +}; + +static const struct phy_ops sun8i_a83t_dphy_ops = { + .configure = sun8i_a83t_dphy_configure, + .power_on = sun8i_a83t_dphy_power_on, + .power_off = sun8i_a83t_dphy_power_off, +}; + +int sun8i_a83t_dphy_register(struct sun8i_a83t_mipi_csi2_device *csi2_dev) +{ + struct device *dev = csi2_dev->dev; + struct phy_provider *phy_provider; + + csi2_dev->dphy = devm_phy_create(dev, NULL, &sun8i_a83t_dphy_ops); + if (IS_ERR(csi2_dev->dphy)) { + dev_err(dev, "failed to create D-PHY\n"); + return PTR_ERR(csi2_dev->dphy); + } + + phy_set_drvdata(csi2_dev->dphy, csi2_dev); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(phy_provider)) { + dev_err(dev, "failed to register D-PHY provider\n"); + return PTR_ERR(phy_provider); + } + + return 0; +} diff --git a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_dphy.h b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_dphy.h new file mode 100644 index 000000000000..9ab709060770 --- /dev/null +++ b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_dphy.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2020 Kévin L'hôpital <kevin.lhopital@bootlin.com> + * Copyright 2020-2022 Bootlin + * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com> + */ + +#ifndef _SUN8I_A83T_DPHY_H_ +#define _SUN8I_A83T_DPHY_H_ + +#include "sun8i_a83t_mipi_csi2.h" + +#define SUN8I_A83T_DPHY_CTRL_REG 0x10 +#define SUN8I_A83T_DPHY_CTRL_INIT_VALUE 0xb8df698e +#define SUN8I_A83T_DPHY_CTRL_RESET_N BIT(31) +#define SUN8I_A83T_DPHY_CTRL_SHUTDOWN_N BIT(15) +#define SUN8I_A83T_DPHY_CTRL_DEBUG BIT(8) +#define SUN8I_A83T_DPHY_STATUS_REG 0x14 +#define SUN8I_A83T_DPHY_STATUS_CLK_STOP BIT(10) +#define SUN8I_A83T_DPHY_STATUS_CLK_ULPS BIT(9) +#define SUN8I_A83T_DPHY_STATUS_HSCLK BIT(8) +#define SUN8I_A83T_DPHY_STATUS_D3_STOP BIT(7) +#define SUN8I_A83T_DPHY_STATUS_D2_STOP BIT(6) +#define SUN8I_A83T_DPHY_STATUS_D1_STOP BIT(5) +#define SUN8I_A83T_DPHY_STATUS_D0_STOP BIT(4) +#define SUN8I_A83T_DPHY_STATUS_D3_ULPS BIT(3) +#define SUN8I_A83T_DPHY_STATUS_D2_ULPS BIT(2) +#define SUN8I_A83T_DPHY_STATUS_D1_ULPS BIT(1) +#define SUN8I_A83T_DPHY_STATUS_D0_ULPS BIT(0) + +#define SUN8I_A83T_DPHY_ANA0_REG 0x30 +#define SUN8I_A83T_DPHY_ANA0_REXT_EN BIT(31) +#define SUN8I_A83T_DPHY_ANA0_REXT BIT(30) +#define SUN8I_A83T_DPHY_ANA0_RINT(v) (((v) << 28) & GENMASK(29, 28)) +#define SUN8I_A83T_DPHY_ANA0_SNK(v) (((v) << 20) & GENMASK(22, 20)) + +int sun8i_a83t_dphy_register(struct sun8i_a83t_mipi_csi2_device *csi2_dev); + +#endif diff --git a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c new file mode 100644 index 000000000000..d052ee77ef0a --- /dev/null +++ b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c @@ -0,0 +1,816 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2020 Kévin L'hôpital <kevin.lhopital@bootlin.com> + * Copyright 2020-2022 Bootlin + * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com> + */ + +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <media/mipi-csi2.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> + +#include "sun8i_a83t_dphy.h" +#include "sun8i_a83t_mipi_csi2.h" +#include "sun8i_a83t_mipi_csi2_reg.h" + +/* Format */ + +static const struct sun8i_a83t_mipi_csi2_format +sun8i_a83t_mipi_csi2_formats[] = { + { + .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, + .data_type = MIPI_CSI2_DT_RAW8, + .bpp = 8, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, + .data_type = MIPI_CSI2_DT_RAW8, + .bpp = 8, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, + .data_type = MIPI_CSI2_DT_RAW8, + .bpp = 8, + }, + { + .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, + .data_type = MIPI_CSI2_DT_RAW8, + .bpp = 8, + }, + { + .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, + .data_type = MIPI_CSI2_DT_RAW10, + .bpp = 10, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, + .data_type = MIPI_CSI2_DT_RAW10, + .bpp = 10, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, + .data_type = MIPI_CSI2_DT_RAW10, + .bpp = 10, + }, + { + .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, + .data_type = MIPI_CSI2_DT_RAW10, + .bpp = 10, + }, +}; + +static const struct sun8i_a83t_mipi_csi2_format * +sun8i_a83t_mipi_csi2_format_find(u32 mbus_code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(sun8i_a83t_mipi_csi2_formats); i++) + if (sun8i_a83t_mipi_csi2_formats[i].mbus_code == mbus_code) + return &sun8i_a83t_mipi_csi2_formats[i]; + + return NULL; +} + +/* Controller */ + +static void +sun8i_a83t_mipi_csi2_init(struct sun8i_a83t_mipi_csi2_device *csi2_dev) +{ + struct regmap *regmap = csi2_dev->regmap; + + /* + * The Allwinner BSP sets various magic values on a bunch of registers. + * This is apparently a necessary initialization process that will cause + * the capture to fail with unsolicited interrupts hitting if skipped. + * + * Most of the registers are set to proper values later, except for the + * two reserved registers. They are said to hold a "hardware lock" + * value, without more information available. + */ + + regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_CTRL_REG, 0); + regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_CTRL_REG, + SUN8I_A83T_MIPI_CSI2_CTRL_INIT_VALUE); + + regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_RX_PKT_NUM_REG, 0); + regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_RX_PKT_NUM_REG, + SUN8I_A83T_MIPI_CSI2_RX_PKT_NUM_INIT_VALUE); + + regmap_write(regmap, SUN8I_A83T_DPHY_CTRL_REG, 0); + regmap_write(regmap, SUN8I_A83T_DPHY_CTRL_REG, + SUN8I_A83T_DPHY_CTRL_INIT_VALUE); + + regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_RSVD1_REG, 0); + regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_RSVD1_REG, + SUN8I_A83T_MIPI_CSI2_RSVD1_HW_LOCK_VALUE); + + regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_RSVD2_REG, 0); + regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_RSVD2_REG, + SUN8I_A83T_MIPI_CSI2_RSVD2_HW_LOCK_VALUE); + + regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_CFG_REG, 0); + regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_CFG_REG, + SUN8I_A83T_MIPI_CSI2_CFG_INIT_VALUE); +} + +static void +sun8i_a83t_mipi_csi2_enable(struct sun8i_a83t_mipi_csi2_device *csi2_dev) +{ + struct regmap *regmap = csi2_dev->regmap; + + regmap_update_bits(regmap, SUN8I_A83T_MIPI_CSI2_CFG_REG, + SUN8I_A83T_MIPI_CSI2_CFG_SYNC_EN, + SUN8I_A83T_MIPI_CSI2_CFG_SYNC_EN); +} + +static void +sun8i_a83t_mipi_csi2_disable(struct sun8i_a83t_mipi_csi2_device *csi2_dev) +{ + struct regmap *regmap = csi2_dev->regmap; + + regmap_update_bits(regmap, SUN8I_A83T_MIPI_CSI2_CFG_REG, + SUN8I_A83T_MIPI_CSI2_CFG_SYNC_EN, 0); + + regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_CTRL_REG, 0); +} + +static void +sun8i_a83t_mipi_csi2_configure(struct sun8i_a83t_mipi_csi2_device *csi2_dev) +{ + struct regmap *regmap = csi2_dev->regmap; + unsigned int lanes_count = + csi2_dev->bridge.endpoint.bus.mipi_csi2.num_data_lanes; + struct v4l2_mbus_framefmt *mbus_format = &csi2_dev->bridge.mbus_format; + const struct sun8i_a83t_mipi_csi2_format *format; + struct device *dev = csi2_dev->dev; + u32 version = 0; + + format = sun8i_a83t_mipi_csi2_format_find(mbus_format->code); + if (WARN_ON(!format)) + return; + + regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_CTRL_REG, + SUN8I_A83T_MIPI_CSI2_CTRL_RESET_N); + + regmap_read(regmap, SUN8I_A83T_MIPI_CSI2_VERSION_REG, &version); + + dev_dbg(dev, "A83T MIPI CSI-2 version: %04x\n", version); + + regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_CFG_REG, + SUN8I_A83T_MIPI_CSI2_CFG_UNPKT_EN | + SUN8I_A83T_MIPI_CSI2_CFG_SYNC_DLY_CYCLE(8) | + SUN8I_A83T_MIPI_CSI2_CFG_N_CHANNEL(1) | + SUN8I_A83T_MIPI_CSI2_CFG_N_LANE(lanes_count)); + + /* + * Only a single virtual channel (index 0) is currently supported. + * While the registers do mention multiple physical channels being + * available (which can be configured to match a specific virtual + * channel or data type), it's unclear whether channels > 0 are actually + * connected and available and the reference source code only makes use + * of channel 0. + * + * Using extra channels would also require matching channels to be + * available on the CSI (and ISP) side, which is also unsure although + * some CSI implementations are said to support multiple channels for + * BT656 time-sharing. + * + * We still configure virtual channel numbers to ensure that virtual + * channel 0 only goes to channel 0. + */ + + regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_VCDT0_REG, + SUN8I_A83T_MIPI_CSI2_VCDT0_CH_VC(3, 3) | + SUN8I_A83T_MIPI_CSI2_VCDT0_CH_VC(2, 2) | + SUN8I_A83T_MIPI_CSI2_VCDT0_CH_VC(1, 1) | + SUN8I_A83T_MIPI_CSI2_VCDT0_CH_VC(0, 0) | + SUN8I_A83T_MIPI_CSI2_VCDT0_CH_DT(0, format->data_type)); +} + +/* V4L2 Subdev */ + +static int sun8i_a83t_mipi_csi2_s_stream(struct v4l2_subdev *subdev, int on) +{ + struct sun8i_a83t_mipi_csi2_device *csi2_dev = + v4l2_get_subdevdata(subdev); + struct v4l2_subdev *source_subdev = csi2_dev->bridge.source_subdev; + union phy_configure_opts dphy_opts = { 0 }; + struct phy_configure_opts_mipi_dphy *dphy_cfg = &dphy_opts.mipi_dphy; + struct v4l2_mbus_framefmt *mbus_format = &csi2_dev->bridge.mbus_format; + const struct sun8i_a83t_mipi_csi2_format *format; + struct phy *dphy = csi2_dev->dphy; + struct device *dev = csi2_dev->dev; + struct v4l2_ctrl *ctrl; + unsigned int lanes_count = + csi2_dev->bridge.endpoint.bus.mipi_csi2.num_data_lanes; + unsigned long pixel_rate; + int ret; + + if (!source_subdev) + return -ENODEV; + + if (!on) { + ret = v4l2_subdev_call(source_subdev, video, s_stream, 0); + goto disable; + } + + /* Runtime PM */ + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + /* Sensor pixel rate */ + + ctrl = v4l2_ctrl_find(source_subdev->ctrl_handler, V4L2_CID_PIXEL_RATE); + if (!ctrl) { + dev_err(dev, "missing sensor pixel rate\n"); + ret = -ENODEV; + goto error_pm; + } + + pixel_rate = (unsigned long)v4l2_ctrl_g_ctrl_int64(ctrl); + if (!pixel_rate) { + dev_err(dev, "missing (zero) sensor pixel rate\n"); + ret = -ENODEV; + goto error_pm; + } + + /* D-PHY */ + + if (!lanes_count) { + dev_err(dev, "missing (zero) MIPI CSI-2 lanes count\n"); + ret = -ENODEV; + goto error_pm; + } + + format = sun8i_a83t_mipi_csi2_format_find(mbus_format->code); + if (WARN_ON(!format)) { + ret = -ENODEV; + goto error_pm; + } + + phy_mipi_dphy_get_default_config(pixel_rate, format->bpp, lanes_count, + dphy_cfg); + + /* + * Note that our hardware is using DDR, which is not taken in account by + * phy_mipi_dphy_get_default_config when calculating hs_clk_rate from + * the pixel rate, lanes count and bpp. + * + * The resulting clock rate is basically the symbol rate over the whole + * link. The actual clock rate is calculated with division by two since + * DDR samples both on rising and falling edges. + */ + + dev_dbg(dev, "A83T MIPI CSI-2 config:\n"); + dev_dbg(dev, "%ld pixels/s, %u bits/pixel, %u lanes, %lu Hz clock\n", + pixel_rate, format->bpp, lanes_count, + dphy_cfg->hs_clk_rate / 2); + + ret = phy_reset(dphy); + if (ret) { + dev_err(dev, "failed to reset MIPI D-PHY\n"); + goto error_pm; + } + + ret = phy_configure(dphy, &dphy_opts); + if (ret) { + dev_err(dev, "failed to configure MIPI D-PHY\n"); + goto error_pm; + } + + /* Controller */ + + sun8i_a83t_mipi_csi2_configure(csi2_dev); + sun8i_a83t_mipi_csi2_enable(csi2_dev); + + /* D-PHY */ + + ret = phy_power_on(dphy); + if (ret) { + dev_err(dev, "failed to power on MIPI D-PHY\n"); + goto error_pm; + } + + /* Source */ + + ret = v4l2_subdev_call(source_subdev, video, s_stream, 1); + if (ret && ret != -ENOIOCTLCMD) + goto disable; + + return 0; + +disable: + if (!on) + ret = 0; + phy_power_off(dphy); + sun8i_a83t_mipi_csi2_disable(csi2_dev); + +error_pm: + pm_runtime_put(dev); + + return ret; +} + +static const struct v4l2_subdev_video_ops +sun8i_a83t_mipi_csi2_video_ops = { + .s_stream = sun8i_a83t_mipi_csi2_s_stream, +}; + +static void +sun8i_a83t_mipi_csi2_mbus_format_prepare(struct v4l2_mbus_framefmt *mbus_format) +{ + if (!sun8i_a83t_mipi_csi2_format_find(mbus_format->code)) + mbus_format->code = sun8i_a83t_mipi_csi2_formats[0].mbus_code; + + mbus_format->field = V4L2_FIELD_NONE; + mbus_format->colorspace = V4L2_COLORSPACE_RAW; + mbus_format->quantization = V4L2_QUANTIZATION_DEFAULT; + mbus_format->xfer_func = V4L2_XFER_FUNC_DEFAULT; +} + +static int sun8i_a83t_mipi_csi2_init_cfg(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state) +{ + struct sun8i_a83t_mipi_csi2_device *csi2_dev = + v4l2_get_subdevdata(subdev); + unsigned int pad = SUN8I_A83T_MIPI_CSI2_PAD_SINK; + struct v4l2_mbus_framefmt *mbus_format = + v4l2_subdev_get_try_format(subdev, state, pad); + struct mutex *lock = &csi2_dev->bridge.lock; + + mutex_lock(lock); + + mbus_format->code = sun8i_a83t_mipi_csi2_formats[0].mbus_code; + mbus_format->width = 640; + mbus_format->height = 480; + + sun8i_a83t_mipi_csi2_mbus_format_prepare(mbus_format); + + mutex_unlock(lock); + + return 0; +} + +static int +sun8i_a83t_mipi_csi2_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code_enum) +{ + if (code_enum->index >= ARRAY_SIZE(sun8i_a83t_mipi_csi2_formats)) + return -EINVAL; + + code_enum->code = + sun8i_a83t_mipi_csi2_formats[code_enum->index].mbus_code; + + return 0; +} + +static int sun8i_a83t_mipi_csi2_get_fmt(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct sun8i_a83t_mipi_csi2_device *csi2_dev = + v4l2_get_subdevdata(subdev); + struct v4l2_mbus_framefmt *mbus_format = &format->format; + struct mutex *lock = &csi2_dev->bridge.lock; + + mutex_lock(lock); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + *mbus_format = *v4l2_subdev_get_try_format(subdev, state, + format->pad); + else + *mbus_format = csi2_dev->bridge.mbus_format; + + mutex_unlock(lock); + + return 0; +} + +static int sun8i_a83t_mipi_csi2_set_fmt(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct sun8i_a83t_mipi_csi2_device *csi2_dev = + v4l2_get_subdevdata(subdev); + struct v4l2_mbus_framefmt *mbus_format = &format->format; + struct mutex *lock = &csi2_dev->bridge.lock; + + mutex_lock(lock); + + sun8i_a83t_mipi_csi2_mbus_format_prepare(mbus_format); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + *v4l2_subdev_get_try_format(subdev, state, format->pad) = + *mbus_format; + else + csi2_dev->bridge.mbus_format = *mbus_format; + + mutex_unlock(lock); + + return 0; +} + +static const struct v4l2_subdev_pad_ops sun8i_a83t_mipi_csi2_pad_ops = { + .init_cfg = sun8i_a83t_mipi_csi2_init_cfg, + .enum_mbus_code = sun8i_a83t_mipi_csi2_enum_mbus_code, + .get_fmt = sun8i_a83t_mipi_csi2_get_fmt, + .set_fmt = sun8i_a83t_mipi_csi2_set_fmt, +}; + +static const struct v4l2_subdev_ops sun8i_a83t_mipi_csi2_subdev_ops = { + .video = &sun8i_a83t_mipi_csi2_video_ops, + .pad = &sun8i_a83t_mipi_csi2_pad_ops, +}; + +/* Media Entity */ + +static const struct media_entity_operations sun8i_a83t_mipi_csi2_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +/* V4L2 Async */ + +static int +sun8i_a83t_mipi_csi2_notifier_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *remote_subdev, + struct v4l2_async_subdev *async_subdev) +{ + struct v4l2_subdev *subdev = notifier->sd; + struct sun8i_a83t_mipi_csi2_device *csi2_dev = + container_of(notifier, struct sun8i_a83t_mipi_csi2_device, + bridge.notifier); + struct media_entity *sink_entity = &subdev->entity; + struct media_entity *source_entity = &remote_subdev->entity; + struct device *dev = csi2_dev->dev; + int sink_pad_index = 0; + int source_pad_index; + int ret; + + ret = media_entity_get_fwnode_pad(source_entity, remote_subdev->fwnode, + MEDIA_PAD_FL_SOURCE); + if (ret < 0) { + dev_err(dev, "missing source pad in external entity %s\n", + source_entity->name); + return -EINVAL; + } + + source_pad_index = ret; + + dev_dbg(dev, "creating %s:%u -> %s:%u link\n", source_entity->name, + source_pad_index, sink_entity->name, sink_pad_index); + + ret = media_create_pad_link(source_entity, source_pad_index, + sink_entity, sink_pad_index, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + dev_err(dev, "failed to create %s:%u -> %s:%u link\n", + source_entity->name, source_pad_index, + sink_entity->name, sink_pad_index); + return ret; + } + + csi2_dev->bridge.source_subdev = remote_subdev; + + return 0; +} + +static const struct v4l2_async_notifier_operations +sun8i_a83t_mipi_csi2_notifier_ops = { + .bound = sun8i_a83t_mipi_csi2_notifier_bound, +}; + +/* Bridge */ + +static int +sun8i_a83t_mipi_csi2_bridge_source_setup(struct sun8i_a83t_mipi_csi2_device *csi2_dev) +{ + struct v4l2_async_notifier *notifier = &csi2_dev->bridge.notifier; + struct v4l2_fwnode_endpoint *endpoint = &csi2_dev->bridge.endpoint; + struct v4l2_async_subdev *subdev_async; + struct fwnode_handle *handle; + struct device *dev = csi2_dev->dev; + int ret; + + handle = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!handle) + return -ENODEV; + + endpoint->bus_type = V4L2_MBUS_CSI2_DPHY; + + ret = v4l2_fwnode_endpoint_parse(handle, endpoint); + if (ret) + goto complete; + + subdev_async = + v4l2_async_nf_add_fwnode_remote(notifier, handle, + struct v4l2_async_subdev); + if (IS_ERR(subdev_async)) + ret = PTR_ERR(subdev_async); + +complete: + fwnode_handle_put(handle); + + return ret; +} + +static int +sun8i_a83t_mipi_csi2_bridge_setup(struct sun8i_a83t_mipi_csi2_device *csi2_dev) +{ + struct sun8i_a83t_mipi_csi2_bridge *bridge = &csi2_dev->bridge; + struct v4l2_subdev *subdev = &bridge->subdev; + struct v4l2_async_notifier *notifier = &bridge->notifier; + struct media_pad *pads = bridge->pads; + struct device *dev = csi2_dev->dev; + int ret; + + mutex_init(&bridge->lock); + + /* V4L2 Subdev */ + + v4l2_subdev_init(subdev, &sun8i_a83t_mipi_csi2_subdev_ops); + strscpy(subdev->name, SUN8I_A83T_MIPI_CSI2_NAME, sizeof(subdev->name)); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + subdev->owner = THIS_MODULE; + subdev->dev = dev; + + v4l2_set_subdevdata(subdev, csi2_dev); + + /* Media Entity */ + + subdev->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + subdev->entity.ops = &sun8i_a83t_mipi_csi2_entity_ops; + + /* Media Pads */ + + pads[SUN8I_A83T_MIPI_CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[SUN8I_A83T_MIPI_CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&subdev->entity, + SUN8I_A83T_MIPI_CSI2_PAD_COUNT, pads); + if (ret) + return ret; + + /* V4L2 Async */ + + v4l2_async_nf_init(notifier); + notifier->ops = &sun8i_a83t_mipi_csi2_notifier_ops; + + ret = sun8i_a83t_mipi_csi2_bridge_source_setup(csi2_dev); + if (ret) + goto error_v4l2_notifier_cleanup; + + ret = v4l2_async_subdev_nf_register(subdev, notifier); + if (ret < 0) + goto error_v4l2_notifier_cleanup; + + /* V4L2 Subdev */ + + ret = v4l2_async_register_subdev(subdev); + if (ret < 0) + goto error_v4l2_notifier_unregister; + + return 0; + +error_v4l2_notifier_unregister: + v4l2_async_nf_unregister(notifier); + +error_v4l2_notifier_cleanup: + v4l2_async_nf_cleanup(notifier); + + media_entity_cleanup(&subdev->entity); + + return ret; +} + +static void +sun8i_a83t_mipi_csi2_bridge_cleanup(struct sun8i_a83t_mipi_csi2_device *csi2_dev) +{ + struct v4l2_subdev *subdev = &csi2_dev->bridge.subdev; + struct v4l2_async_notifier *notifier = &csi2_dev->bridge.notifier; + + v4l2_async_unregister_subdev(subdev); + v4l2_async_nf_unregister(notifier); + v4l2_async_nf_cleanup(notifier); + media_entity_cleanup(&subdev->entity); +} + +/* Platform */ + +static int sun8i_a83t_mipi_csi2_suspend(struct device *dev) +{ + struct sun8i_a83t_mipi_csi2_device *csi2_dev = dev_get_drvdata(dev); + + clk_disable_unprepare(csi2_dev->clock_misc); + clk_disable_unprepare(csi2_dev->clock_mipi); + clk_disable_unprepare(csi2_dev->clock_mod); + reset_control_assert(csi2_dev->reset); + + return 0; +} + +static int sun8i_a83t_mipi_csi2_resume(struct device *dev) +{ + struct sun8i_a83t_mipi_csi2_device *csi2_dev = dev_get_drvdata(dev); + int ret; + + ret = reset_control_deassert(csi2_dev->reset); + if (ret) { + dev_err(dev, "failed to deassert reset\n"); + return ret; + } + + ret = clk_prepare_enable(csi2_dev->clock_mod); + if (ret) { + dev_err(dev, "failed to enable module clock\n"); + goto error_reset; + } + + ret = clk_prepare_enable(csi2_dev->clock_mipi); + if (ret) { + dev_err(dev, "failed to enable MIPI clock\n"); + goto error_clock_mod; + } + + ret = clk_prepare_enable(csi2_dev->clock_misc); + if (ret) { + dev_err(dev, "failed to enable CSI misc clock\n"); + goto error_clock_mipi; + } + + sun8i_a83t_mipi_csi2_init(csi2_dev); + + return 0; + +error_clock_mipi: + clk_disable_unprepare(csi2_dev->clock_mipi); + +error_clock_mod: + clk_disable_unprepare(csi2_dev->clock_mod); + +error_reset: + reset_control_assert(csi2_dev->reset); + + return ret; +} + +static const struct dev_pm_ops sun8i_a83t_mipi_csi2_pm_ops = { + .runtime_suspend = sun8i_a83t_mipi_csi2_suspend, + .runtime_resume = sun8i_a83t_mipi_csi2_resume, +}; + +static const struct regmap_config sun8i_a83t_mipi_csi2_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x120, +}; + +static int +sun8i_a83t_mipi_csi2_resources_setup(struct sun8i_a83t_mipi_csi2_device *csi2_dev, + struct platform_device *platform_dev) +{ + struct device *dev = csi2_dev->dev; + void __iomem *io_base; + int ret; + + /* Registers */ + + io_base = devm_platform_ioremap_resource(platform_dev, 0); + if (IS_ERR(io_base)) + return PTR_ERR(io_base); + + csi2_dev->regmap = + devm_regmap_init_mmio_clk(dev, "bus", io_base, + &sun8i_a83t_mipi_csi2_regmap_config); + if (IS_ERR(csi2_dev->regmap)) { + dev_err(dev, "failed to init register map\n"); + return PTR_ERR(csi2_dev->regmap); + } + + /* Clocks */ + + csi2_dev->clock_mod = devm_clk_get(dev, "mod"); + if (IS_ERR(csi2_dev->clock_mod)) { + dev_err(dev, "failed to acquire mod clock\n"); + return PTR_ERR(csi2_dev->clock_mod); + } + + ret = clk_set_rate_exclusive(csi2_dev->clock_mod, 297000000); + if (ret) { + dev_err(dev, "failed to set mod clock rate\n"); + return ret; + } + + csi2_dev->clock_mipi = devm_clk_get(dev, "mipi"); + if (IS_ERR(csi2_dev->clock_mipi)) { + dev_err(dev, "failed to acquire mipi clock\n"); + return PTR_ERR(csi2_dev->clock_mipi); + } + + csi2_dev->clock_misc = devm_clk_get(dev, "misc"); + if (IS_ERR(csi2_dev->clock_misc)) { + dev_err(dev, "failed to acquire misc clock\n"); + return PTR_ERR(csi2_dev->clock_misc); + } + + /* Reset */ + + csi2_dev->reset = devm_reset_control_get_shared(dev, NULL); + if (IS_ERR(csi2_dev->reset)) { + dev_err(dev, "failed to get reset controller\n"); + return PTR_ERR(csi2_dev->reset); + } + + /* D-PHY */ + + ret = sun8i_a83t_dphy_register(csi2_dev); + if (ret) { + dev_err(dev, "failed to initialize MIPI D-PHY\n"); + return ret; + } + + /* Runtime PM */ + + pm_runtime_enable(dev); + + return 0; +} + +static void +sun8i_a83t_mipi_csi2_resources_cleanup(struct sun8i_a83t_mipi_csi2_device *csi2_dev) +{ + pm_runtime_disable(csi2_dev->dev); + phy_exit(csi2_dev->dphy); + clk_rate_exclusive_put(csi2_dev->clock_mod); +} + +static int sun8i_a83t_mipi_csi2_probe(struct platform_device *platform_dev) +{ + struct sun8i_a83t_mipi_csi2_device *csi2_dev; + struct device *dev = &platform_dev->dev; + int ret; + + csi2_dev = devm_kzalloc(dev, sizeof(*csi2_dev), GFP_KERNEL); + if (!csi2_dev) + return -ENOMEM; + + csi2_dev->dev = dev; + platform_set_drvdata(platform_dev, csi2_dev); + + ret = sun8i_a83t_mipi_csi2_resources_setup(csi2_dev, platform_dev); + if (ret) + return ret; + + ret = sun8i_a83t_mipi_csi2_bridge_setup(csi2_dev); + if (ret) + return ret; + + return 0; +} + +static int sun8i_a83t_mipi_csi2_remove(struct platform_device *platform_dev) +{ + struct sun8i_a83t_mipi_csi2_device *csi2_dev = + platform_get_drvdata(platform_dev); + + sun8i_a83t_mipi_csi2_bridge_cleanup(csi2_dev); + sun8i_a83t_mipi_csi2_resources_cleanup(csi2_dev); + + return 0; +} + +static const struct of_device_id sun8i_a83t_mipi_csi2_of_match[] = { + { .compatible = "allwinner,sun8i-a83t-mipi-csi2" }, + {}, +}; +MODULE_DEVICE_TABLE(of, sun8i_a83t_mipi_csi2_of_match); + +static struct platform_driver sun8i_a83t_mipi_csi2_platform_driver = { + .probe = sun8i_a83t_mipi_csi2_probe, + .remove = sun8i_a83t_mipi_csi2_remove, + .driver = { + .name = SUN8I_A83T_MIPI_CSI2_NAME, + .of_match_table = of_match_ptr(sun8i_a83t_mipi_csi2_of_match), + .pm = &sun8i_a83t_mipi_csi2_pm_ops, + }, +}; +module_platform_driver(sun8i_a83t_mipi_csi2_platform_driver); + +MODULE_DESCRIPTION("Allwinner A83T MIPI CSI-2 and D-PHY Controller Driver"); +MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.h b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.h new file mode 100644 index 000000000000..f1e64c53434c --- /dev/null +++ b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2020 Kévin L'hôpital <kevin.lhopital@bootlin.com> + * Copyright 2020-2022 Bootlin + * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com> + */ + +#ifndef _SUN8I_A83T_MIPI_CSI2_H_ +#define _SUN8I_A83T_MIPI_CSI2_H_ + +#include <linux/phy/phy.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> + +#define SUN8I_A83T_MIPI_CSI2_NAME "sun8i-a83t-mipi-csi2" + +enum sun8i_a83t_mipi_csi2_pad { + SUN8I_A83T_MIPI_CSI2_PAD_SINK = 0, + SUN8I_A83T_MIPI_CSI2_PAD_SOURCE = 1, + SUN8I_A83T_MIPI_CSI2_PAD_COUNT = 2, +}; + +struct sun8i_a83t_mipi_csi2_format { + u32 mbus_code; + u8 data_type; + u32 bpp; +}; + +struct sun8i_a83t_mipi_csi2_bridge { + struct v4l2_subdev subdev; + struct media_pad pads[SUN8I_A83T_MIPI_CSI2_PAD_COUNT]; + struct v4l2_fwnode_endpoint endpoint; + struct v4l2_async_notifier notifier; + struct v4l2_mbus_framefmt mbus_format; + struct mutex lock; /* Mbus format lock. */ + + struct v4l2_subdev *source_subdev; +}; + +struct sun8i_a83t_mipi_csi2_device { + struct device *dev; + + struct regmap *regmap; + struct clk *clock_mod; + struct clk *clock_mipi; + struct clk *clock_misc; + struct reset_control *reset; + struct phy *dphy; + + struct sun8i_a83t_mipi_csi2_bridge bridge; +}; + +#endif diff --git a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2_reg.h b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2_reg.h new file mode 100644 index 000000000000..2cfc9eb490e6 --- /dev/null +++ b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2_reg.h @@ -0,0 +1,151 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2020 Kévin L'hôpital <kevin.lhopital@bootlin.com> + * Copyright 2020-2022 Bootlin + * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com> + */ + +#ifndef _SUN8I_A83T_MIPI_CSI2_REG_H_ +#define _SUN8I_A83T_MIPI_CSI2_REG_H_ + +#define SUN8I_A83T_MIPI_CSI2_VERSION_REG 0x0 +#define SUN8I_A83T_MIPI_CSI2_CTRL_REG 0x4 +#define SUN8I_A83T_MIPI_CSI2_CTRL_INIT_VALUE 0xb8c39bec +#define SUN8I_A83T_MIPI_CSI2_CTRL_RESET_N BIT(31) +#define SUN8I_A83T_MIPI_CSI2_RX_PKT_NUM_REG 0x8 +#define SUN8I_A83T_MIPI_CSI2_RX_PKT_NUM_INIT_VALUE 0xb8d257f8 +#define SUN8I_A83T_MIPI_CSI2_RSVD0_REG 0xc + +#define SUN8I_A83T_MIPI_CSI2_RSVD1_REG 0x18 +#define SUN8I_A83T_MIPI_CSI2_RSVD1_HW_LOCK_VALUE 0xb8c8a30c +#define SUN8I_A83T_MIPI_CSI2_RSVD2_REG 0x1c +#define SUN8I_A83T_MIPI_CSI2_RSVD2_HW_LOCK_VALUE 0xb8df8ad7 +#define SUN8I_A83T_MIPI_CSI2_INT_STA0_REG 0x20 +#define SUN8I_A83T_MIPI_CSI2_INT_STA0_ECC_ERR_DBL BIT(28) +#define SUN8I_A83T_MIPI_CSI2_INT_STA0_LINE_CKSM_ERR_VC3 BIT(27) +#define SUN8I_A83T_MIPI_CSI2_INT_STA0_LINE_CKSM_ERR_VC2 BIT(26) +#define SUN8I_A83T_MIPI_CSI2_INT_STA0_LINE_CKSM_ERR_VC1 BIT(25) +#define SUN8I_A83T_MIPI_CSI2_INT_STA0_LINE_CKSM_ERR_VC0 BIT(24) +#define SUN8I_A83T_MIPI_CSI2_INT_STA0_LINE_SEQ_ERR_DT3 BIT(23) +#define SUN8I_A83T_MIPI_CSI2_INT_STA0_LINE_SEQ_ERR_DT2 BIT(22) +#define SUN8I_A83T_MIPI_CSI2_INT_STA0_LINE_SEQ_ERR_DT1 BIT(21) +#define SUN8I_A83T_MIPI_CSI2_INT_STA0_LINE_SEQ_ERR_DT0 BIT(20) +#define SUN8I_A83T_MIPI_CSI2_INT_STA0_LS_LE_ERR_DT3 BIT(19) +#define SUN8I_A83T_MIPI_CSI2_INT_STA0_LS_LE_ERR_DT2 BIT(18) +#define SUN8I_A83T_MIPI_CSI2_INT_STA0_LS_LE_ERR_DT1 BIT(17) +#define SUN8I_A83T_MIPI_CSI2_INT_STA0_LS_LE_ERR_DT0 BIT(16) +#define SUN8I_A83T_MIPI_CSI2_INT_STA0_CRC_ERR_VC3 BIT(15) +#define SUN8I_A83T_MIPI_CSI2_INT_STA0_CRC_ERR_VC2 BIT(14) +#define SUN8I_A83T_MIPI_CSI2_INT_STA0_CRC_ERR_VC1 BIT(13) +#define SUN8I_A83T_MIPI_CSI2_INT_STA0_CRC_ERR_VC0 BIT(12) +#define SUN8I_A83T_MIPI_CSI2_INT_STA0_FRM_SEQ_ERR_VC3 BIT(11) +#define SUN8I_A83T_MIPI_CSI2_INT_STA0_FRM_SEQ_ERR_VC2 BIT(10) +#define SUN8I_A83T_MIPI_CSI2_INT_STA0_FRM_SEQ_ERR_VC1 BIT(9) +#define SUN8I_A83T_MIPI_CSI2_INT_STA0_FRM_SEQ_ERR_VC0 BIT(8) +#define SUN8I_A83T_MIPI_CSI2_INT_STA0_FS_FE_ERR_VC3 BIT(7) +#define SUN8I_A83T_MIPI_CSI2_INT_STA0_FS_FE_ERR_VC2 BIT(6) +#define SUN8I_A83T_MIPI_CSI2_INT_STA0_FS_FE_ERR_VC1 BIT(5) +#define SUN8I_A83T_MIPI_CSI2_INT_STA0_FS_FE_ERR_VC0 BIT(4) +#define SUN8I_A83T_MIPI_CSI2_INT_STA0_SOT_SYNC_ERR_3 BIT(3) +#define SUN8I_A83T_MIPI_CSI2_INT_STA0_SOT_SYNC_ERR_2 BIT(2) +#define SUN8I_A83T_MIPI_CSI2_INT_STA0_SOT_SYNC_ERR_1 BIT(1) +#define SUN8I_A83T_MIPI_CSI2_INT_STA0_SOT_SYNC_ERR_0 BIT(0) +#define SUN8I_A83T_MIPI_CSI2_INT_STA1_REG 0x24 +#define SUN8I_A83T_MIPI_CSI2_INT_STA1_LINE_SEQ_ERR_DT7 BIT(23) +#define SUN8I_A83T_MIPI_CSI2_INT_STA1_LINE_SEQ_ERR_DT6 BIT(22) +#define SUN8I_A83T_MIPI_CSI2_INT_STA1_LINE_SEQ_ERR_DT5 BIT(21) +#define SUN8I_A83T_MIPI_CSI2_INT_STA1_LINE_SEQ_ERR_DT4 BIT(20) +#define SUN8I_A83T_MIPI_CSI2_INT_STA1_LS_LE_ERR_DT7 BIT(19) +#define SUN8I_A83T_MIPI_CSI2_INT_STA1_LS_LE_ERR_DT6 BIT(18) +#define SUN8I_A83T_MIPI_CSI2_INT_STA1_LS_LE_ERR_DT5 BIT(17) +#define SUN8I_A83T_MIPI_CSI2_INT_STA1_LS_LE_ERR_DT4 BIT(16) +#define SUN8I_A83T_MIPI_CSI2_INT_STA1_DT_ERR_VC3 BIT(15) +#define SUN8I_A83T_MIPI_CSI2_INT_STA1_DT_ERR_VC2 BIT(14) +#define SUN8I_A83T_MIPI_CSI2_INT_STA1_DT_ERR_VC1 BIT(13) +#define SUN8I_A83T_MIPI_CSI2_INT_STA1_DT_ERR_VC0 BIT(12) +#define SUN8I_A83T_MIPI_CSI2_INT_STA1_ECC_ERR1_VC3 BIT(11) +#define SUN8I_A83T_MIPI_CSI2_INT_STA1_ECC_ERR1_VC2 BIT(10) +#define SUN8I_A83T_MIPI_CSI2_INT_STA1_ECC_ERR1_VC1 BIT(9) +#define SUN8I_A83T_MIPI_CSI2_INT_STA1_ECC_ERR1_VC0 BIT(8) +#define SUN8I_A83T_MIPI_CSI2_INT_STA1_SOT_ERR_3 BIT(7) +#define SUN8I_A83T_MIPI_CSI2_INT_STA1_SOT_ERR_2 BIT(6) +#define SUN8I_A83T_MIPI_CSI2_INT_STA1_SOT_ERR_1 BIT(5) +#define SUN8I_A83T_MIPI_CSI2_INT_STA1_SOT_ERR_0 BIT(4) +#define SUN8I_A83T_MIPI_CSI2_INT_STA1_ESC_ENTRY_ERR_3 BIT(3) +#define SUN8I_A83T_MIPI_CSI2_INT_STA1_ESC_ENTRY_ERR_2 BIT(2) +#define SUN8I_A83T_MIPI_CSI2_INT_STA1_ESC_ENTRY_ERR_1 BIT(1) +#define SUN8I_A83T_MIPI_CSI2_INT_STA1_ESC_ENTRY_ERR_0 BIT(0) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_REG 0x28 +#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_ECC_ERR_DBL BIT(28) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_CKSM_ERR_VC3 BIT(27) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_CKSM_ERR_VC2 BIT(26) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_CKSM_ERR_VC1 BIT(25) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_CKSM_ERR_VC0 BIT(24) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_LINE_SEQ_ERR_DT3 BIT(23) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_LINE_SEQ_ERR_DT2 BIT(22) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_LINE_SEQ_ERR_DT1 BIT(21) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_LINE_SEQ_ERR_DT0 BIT(20) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_LS_LE_ERR_DT3 BIT(19) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_LS_LE_ERR_DT2 BIT(18) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_LS_LE_ERR_DT1 BIT(17) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_LS_LE_ERR_DT0 BIT(16) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_CRC_ERR_VC3 BIT(15) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_CRC_ERR_VC2 BIT(14) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_CRC_ERR_VC1 BIT(13) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_CRC_ERR_VC0 BIT(12) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_FRM_SEQ_ERR_VC3 BIT(11) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_FRM_SEQ_ERR_VC2 BIT(10) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_FRM_SEQ_ERR_VC1 BIT(9) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_FRM_SEQ_ERR_VC0 BIT(8) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_FS_FE_ERR_VC3 BIT(7) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_FS_FE_ERR_VC2 BIT(6) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_FS_FE_ERR_VC1 BIT(5) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_FS_FE_ERR_VC0 BIT(4) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_SOT_SYNC_ERR_3 BIT(3) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_SOT_SYNC_ERR_2 BIT(2) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_SOT_SYNC_ERR_1 BIT(1) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK0_SOT_SYNC_ERR_0 BIT(0) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_REG 0x2c +#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_DT_ERR_VC3 BIT(15) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_DT_ERR_VC2 BIT(14) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_DT_ERR_VC1 BIT(13) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_DT_ERR_VC0 BIT(12) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_ECC_ERR1_VC3 BIT(11) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_ECC_ERR1_VC2 BIT(10) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_ECC_ERR1_VC1 BIT(9) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_ECC_ERR1_VC0 BIT(8) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_SOT_ERR_3 BIT(7) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_SOT_ERR_2 BIT(6) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_SOT_ERR_1 BIT(5) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_SOT_ERR_0 BIT(4) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_ESC_ENTRY_ERR_3 BIT(3) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_ESC_ENTRY_ERR_2 BIT(2) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_ESC_ENTRY_ERR_1 BIT(1) +#define SUN8I_A83T_MIPI_CSI2_INT_MSK1_ESC_ENTRY_ERR_0 BIT(0) + +#define SUN8I_A83T_MIPI_CSI2_CFG_REG 0x100 +#define SUN8I_A83T_MIPI_CSI2_CFG_INIT_VALUE 0xb8c64f24 +#define SUN8I_A83T_MIPI_CSI2_CFG_SYNC_EN BIT(31) +#define SUN8I_A83T_MIPI_CSI2_CFG_BYPASS_ECC_EN BIT(29) +#define SUN8I_A83T_MIPI_CSI2_CFG_UNPKT_EN BIT(28) +#define SUN8I_A83T_MIPI_CSI2_CFG_NONE_UNPKT_RX_MODE BIT(27) +#define SUN8I_A83T_MIPI_CSI2_CFG_YC_SWAB BIT(26) +#define SUN8I_A83T_MIPI_CSI2_CFG_N_BYTE BIT(24) +#define SUN8I_A83T_MIPI_CSI2_CFG_SYNC_DLY_CYCLE(v) (((v) << 18) & \ + GENMASK(22, 18)) +#define SUN8I_A83T_MIPI_CSI2_CFG_N_CHANNEL(v) ((((v) - 1) << 16) & \ + GENMASK(17, 16)) +#define SUN8I_A83T_MIPI_CSI2_CFG_N_LANE(v) ((((v) - 1) << 4) & \ + GENMASK(5, 4)) +#define SUN8I_A83T_MIPI_CSI2_VCDT0_REG 0x104 +#define SUN8I_A83T_MIPI_CSI2_VCDT0_CH_VC(ch, vc) (((vc) & GENMASK(1, 0)) << \ + ((ch) * 8 + 6)) +#define SUN8I_A83T_MIPI_CSI2_VCDT0_CH_DT(ch, t) (((t) & GENMASK(5, 0)) << \ + ((ch) * 8)) +#define SUN8I_A83T_MIPI_CSI2_VCDT1_REG 0x108 +#define SUN8I_A83T_MIPI_CSI2_VCDT1_CH_VC(ch, vc) (((vc) & GENMASK(1, 0)) << \ + (((ch) - 4) * 8 + 6)) +#define SUN8I_A83T_MIPI_CSI2_VCDT1_CH_DT(ch, t) (((t) & GENMASK(5, 0)) << \ + (((ch) - 4) * 8)) + +#endif diff --git a/drivers/media/platform/ti/cal/cal-camerarx.c b/drivers/media/platform/ti/cal/cal-camerarx.c index e69fed117fea..e136d70b4048 100644 --- a/drivers/media/platform/ti/cal/cal-camerarx.c +++ b/drivers/media/platform/ti/cal/cal-camerarx.c @@ -305,7 +305,7 @@ static int cal_camerarx_start(struct cal_camerarx *phy) /* * CSI-2 PHY Link Initialization Sequence, according to the DRA74xP / * DRA75xP / DRA76xP / DRA77xP TRM. The DRA71x / DRA72x and the AM65x / - * DRA80xM TRMs have a a slightly simplified sequence. + * DRA80xM TRMs have a slightly simplified sequence. */ /* @@ -592,7 +592,7 @@ int cal_camerarx_get_remote_frame_desc(struct cal_camerarx *phy, if (!phy->source) return -EPIPE; - pad = media_entity_remote_pad(&phy->pads[CAL_CAMERARX_PAD_SINK]); + pad = media_pad_remote_pad_first(&phy->pads[CAL_CAMERARX_PAD_SINK]); if (!pad) return -EPIPE; diff --git a/drivers/media/platform/ti/cal/cal-video.c b/drivers/media/platform/ti/cal/cal-video.c index 07ae1a34e6b0..776da0cfcdbe 100644 --- a/drivers/media/platform/ti/cal/cal-video.c +++ b/drivers/media/platform/ti/cal/cal-video.c @@ -685,7 +685,7 @@ static int cal_video_check_format(struct cal_ctx *ctx) const struct v4l2_mbus_framefmt *format; struct media_pad *remote_pad; - remote_pad = media_entity_remote_pad(&ctx->pad); + remote_pad = media_pad_remote_pad_first(&ctx->pad); if (!remote_pad) return -ENODEV; diff --git a/drivers/media/platform/ti/davinci/vpif.c b/drivers/media/platform/ti/davinci/vpif.c index 97ef770266af..da27da4c165a 100644 --- a/drivers/media/platform/ti/davinci/vpif.c +++ b/drivers/media/platform/ti/davinci/vpif.c @@ -469,6 +469,7 @@ static int vpif_probe(struct platform_device *pdev) endpoint); if (!endpoint) return 0; + of_node_put(endpoint); /* * For DT platforms, manually create platform_devices for diff --git a/drivers/media/platform/ti/davinci/vpif.h b/drivers/media/platform/ti/davinci/vpif.h index c6d1d890478a..651943e3e375 100644 --- a/drivers/media/platform/ti/davinci/vpif.h +++ b/drivers/media/platform/ti/davinci/vpif.h @@ -1,16 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * VPIF header file * * Copyright (C) 2009 Texas Instruments Incorporated - https://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed .as is. WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef VPIF_H @@ -685,4 +677,3 @@ struct vpif_params { }; #endif /* End of #ifndef VPIF_H */ - diff --git a/drivers/media/platform/ti/davinci/vpif_display.h b/drivers/media/platform/ti/davinci/vpif_display.h index f98062e79167..f27474e0fc36 100644 --- a/drivers/media/platform/ti/davinci/vpif_display.h +++ b/drivers/media/platform/ti/davinci/vpif_display.h @@ -1,16 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * VPIF display header file * * Copyright (C) 2009 Texas Instruments Incorporated - https://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed .as is. WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef VPIF_DISPLAY_H diff --git a/drivers/media/platform/ti/omap/omap_voutlib.c b/drivers/media/platform/ti/omap/omap_voutlib.c index 480a7e95533d..fdea2309ee37 100644 --- a/drivers/media/platform/ti/omap/omap_voutlib.c +++ b/drivers/media/platform/ti/omap/omap_voutlib.c @@ -314,7 +314,7 @@ unsigned long omap_vout_alloc_buffer(u32 buf_size, u32 *phys_addr) if (virt_addr) { while (size > 0) { - SetPageReserved(virt_to_page(addr)); + SetPageReserved(virt_to_page((void *)addr)); addr += PAGE_SIZE; size -= PAGE_SIZE; } @@ -335,7 +335,7 @@ void omap_vout_free_buffer(unsigned long virtaddr, u32 buf_size) order = get_order(size); while (size > 0) { - ClearPageReserved(virt_to_page(addr)); + ClearPageReserved(virt_to_page((void *)addr)); addr += PAGE_SIZE; size -= PAGE_SIZE; } diff --git a/drivers/media/platform/ti/omap3isp/isp.c b/drivers/media/platform/ti/omap3isp/isp.c index 4c937f3f323e..d251736eb420 100644 --- a/drivers/media/platform/ti/omap3isp/isp.c +++ b/drivers/media/platform/ti/omap3isp/isp.c @@ -700,7 +700,7 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe, if (!(pad->flags & MEDIA_PAD_FL_SINK)) break; - pad = media_entity_remote_pad(pad); + pad = media_pad_remote_pad_first(pad); if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; @@ -797,7 +797,7 @@ static int isp_pipeline_disable(struct isp_pipeline *pipe) if (!(pad->flags & MEDIA_PAD_FL_SINK)) break; - pad = media_entity_remote_pad(pad); + pad = media_pad_remote_pad_first(pad); if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; @@ -942,7 +942,7 @@ static int isp_pipeline_is_last(struct media_entity *me) pipe = to_isp_pipeline(me); if (pipe->stream_state == ISP_PIPELINE_STREAM_STOPPED) return 0; - pad = media_entity_remote_pad(&pipe->output->pad); + pad = media_pad_remote_pad_first(&pipe->output->pad); return pad->entity == me; } diff --git a/drivers/media/platform/ti/omap3isp/ispccdc.c b/drivers/media/platform/ti/omap3isp/ispccdc.c index 108b5e9f82cb..11afb8aec292 100644 --- a/drivers/media/platform/ti/omap3isp/ispccdc.c +++ b/drivers/media/platform/ti/omap3isp/ispccdc.c @@ -1133,7 +1133,7 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) ccdc->bt656 = false; ccdc->fields = 0; - pad = media_entity_remote_pad(&ccdc->pads[CCDC_PAD_SINK]); + pad = media_pad_remote_pad_first(&ccdc->pads[CCDC_PAD_SINK]); sensor = media_entity_to_v4l2_subdev(pad->entity); if (ccdc->input == CCDC_INPUT_PARALLEL) { struct v4l2_subdev *sd = diff --git a/drivers/media/platform/ti/omap3isp/ispccp2.c b/drivers/media/platform/ti/omap3isp/ispccp2.c index acb58b6ddba1..fc90ff88464f 100644 --- a/drivers/media/platform/ti/omap3isp/ispccp2.c +++ b/drivers/media/platform/ti/omap3isp/ispccp2.c @@ -357,7 +357,7 @@ static int ccp2_if_configure(struct isp_ccp2_device *ccp2) ccp2_pwr_cfg(ccp2); - pad = media_entity_remote_pad(&ccp2->pads[CCP2_PAD_SINK]); + pad = media_pad_remote_pad_first(&ccp2->pads[CCP2_PAD_SINK]); sensor = media_entity_to_v4l2_subdev(pad->entity); buscfg = v4l2_subdev_to_bus_cfg(pipe->external); diff --git a/drivers/media/platform/ti/omap3isp/ispcsi2.c b/drivers/media/platform/ti/omap3isp/ispcsi2.c index 6302e0c94034..6870980a2fa9 100644 --- a/drivers/media/platform/ti/omap3isp/ispcsi2.c +++ b/drivers/media/platform/ti/omap3isp/ispcsi2.c @@ -561,7 +561,7 @@ static int csi2_configure(struct isp_csi2_device *csi2) if (csi2->contexts[0].enabled || csi2->ctrl.if_enable) return -EBUSY; - pad = media_entity_remote_pad(&csi2->pads[CSI2_PAD_SINK]); + pad = media_pad_remote_pad_first(&csi2->pads[CSI2_PAD_SINK]); sensor = media_entity_to_v4l2_subdev(pad->entity); buscfg = v4l2_subdev_to_bus_cfg(pipe->external); diff --git a/drivers/media/platform/ti/omap3isp/ispvideo.c b/drivers/media/platform/ti/omap3isp/ispvideo.c index 8811d6dd4ee7..d7059180e80e 100644 --- a/drivers/media/platform/ti/omap3isp/ispvideo.c +++ b/drivers/media/platform/ti/omap3isp/ispvideo.c @@ -206,7 +206,7 @@ isp_video_remote_subdev(struct isp_video *video, u32 *pad) { struct media_pad *remote; - remote = media_entity_remote_pad(&video->pad); + remote = media_pad_remote_pad_first(&video->pad); if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) return NULL; @@ -981,7 +981,7 @@ static int isp_video_check_external_subdevs(struct isp_video *video, continue; /* ISP entities have always sink pad == 0. Find source. */ - source_pad = media_entity_remote_pad(&ents[i]->pads[0]); + source_pad = media_pad_remote_pad_first(&ents[i]->pads[0]); if (source_pad == NULL) continue; diff --git a/drivers/media/platform/video-mux.c b/drivers/media/platform/video-mux.c index b31e5913a4cd..71d97042a470 100644 --- a/drivers/media/platform/video-mux.c +++ b/drivers/media/platform/video-mux.c @@ -118,7 +118,7 @@ static int video_mux_s_stream(struct v4l2_subdev *sd, int enable) return -EINVAL; } - pad = media_entity_remote_pad(&sd->entity.pads[vmux->active]); + pad = media_pad_remote_pad_first(&sd->entity.pads[vmux->active]); if (!pad) { dev_err(sd->dev, "Failed to find remote source pad\n"); return -ENOLINK; diff --git a/drivers/media/platform/xilinx/xilinx-csi2rxss.c b/drivers/media/platform/xilinx/xilinx-csi2rxss.c index 051c60cba1e0..cf8e892c47f0 100644 --- a/drivers/media/platform/xilinx/xilinx-csi2rxss.c +++ b/drivers/media/platform/xilinx/xilinx-csi2rxss.c @@ -474,7 +474,7 @@ static struct v4l2_subdev *xcsi2rxss_get_remote_subdev(struct media_pad *local) { struct media_pad *remote; - remote = media_entity_remote_pad(local); + remote = media_pad_remote_pad_first(local); if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) return NULL; diff --git a/drivers/media/platform/xilinx/xilinx-dma.c b/drivers/media/platform/xilinx/xilinx-dma.c index 338c3661d809..2d1ef7a25c33 100644 --- a/drivers/media/platform/xilinx/xilinx-dma.c +++ b/drivers/media/platform/xilinx/xilinx-dma.c @@ -44,7 +44,7 @@ xvip_dma_remote_subdev(struct media_pad *local, u32 *pad) { struct media_pad *remote; - remote = media_entity_remote_pad(local); + remote = media_pad_remote_pad_first(local); if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) return NULL; @@ -107,7 +107,7 @@ static int xvip_pipeline_start_stop(struct xvip_pipeline *pipe, bool start) if (!(pad->flags & MEDIA_PAD_FL_SINK)) break; - pad = media_entity_remote_pad(pad); + pad = media_pad_remote_pad_first(pad); if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; diff --git a/drivers/media/platform/xilinx/xilinx-vip.h b/drivers/media/platform/xilinx/xilinx-vip.h index d0b0e0600952..48fe229c5b33 100644 --- a/drivers/media/platform/xilinx/xilinx-vip.h +++ b/drivers/media/platform/xilinx/xilinx-vip.h @@ -28,8 +28,8 @@ struct clk; #define XVIP_MAX_HEIGHT 7680 /* - * Pad IDs. IP cores with with multiple inputs or outputs should define - * their own values. + * Pad IDs. IP cores with multiple inputs or outputs should define their own + * values. */ #define XVIP_PAD_SINK 0 #define XVIP_PAD_SOURCE 1 diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c index 3155e876616d..fff4dd48eaca 100644 --- a/drivers/media/rc/ati_remote.c +++ b/drivers/media/rc/ati_remote.c @@ -135,8 +135,6 @@ MODULE_PARM_DESC(mouse, "Enable mouse device, default = yes"); #define dbginfo(dev, format, arg...) \ do { if (debug) dev_info(dev , format , ## arg); } while (0) -#undef err -#define err(format, arg...) printk(KERN_ERR format , ## arg) struct ati_receiver_type { /* either default_keymap or get_default_keymap should be set */ @@ -816,11 +814,12 @@ static int ati_remote_probe(struct usb_interface *interface, struct ati_receiver_type *type = (struct ati_receiver_type *)id->driver_info; struct ati_remote *ati_remote; struct input_dev *input_dev; + struct device *device = &interface->dev; struct rc_dev *rc_dev; int err = -ENOMEM; if (iface_host->desc.bNumEndpoints != 2) { - err("%s: Unexpected desc.bNumEndpoints\n", __func__); + dev_err(device, "%s: Unexpected desc.bNumEndpoints\n", __func__); return -ENODEV; } @@ -828,15 +827,15 @@ static int ati_remote_probe(struct usb_interface *interface, endpoint_out = &iface_host->endpoint[1].desc; if (!usb_endpoint_is_int_in(endpoint_in)) { - err("%s: Unexpected endpoint_in\n", __func__); + dev_err(device, "%s: Unexpected endpoint_in\n", __func__); return -ENODEV; } if (le16_to_cpu(endpoint_in->wMaxPacketSize) == 0) { - err("%s: endpoint_in message size==0? \n", __func__); + dev_err(device, "%s: endpoint_in message size==0?\n", __func__); return -ENODEV; } if (!usb_endpoint_is_int_out(endpoint_out)) { - err("%s: Unexpected endpoint_out\n", __func__); + dev_err(device, "%s: Unexpected endpoint_out\n", __func__); return -ENODEV; } diff --git a/drivers/media/rc/igorplugusb.c b/drivers/media/rc/igorplugusb.c index b40dbf500186..1464ef9c55bc 100644 --- a/drivers/media/rc/igorplugusb.c +++ b/drivers/media/rc/igorplugusb.c @@ -38,7 +38,7 @@ struct igorplugusb { struct timer_list timer; - uint8_t buf_in[MAX_PACKET]; + u8 *buf_in; char phys[64]; }; @@ -110,7 +110,6 @@ static void igorplugusb_callback(struct urb *urb) case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: - usb_unlink_urb(urb); return; default: dev_warn(ir->dev, "Error: urb status = %d\n", urb->status); @@ -126,7 +125,7 @@ static void igorplugusb_cmd(struct igorplugusb *ir, int cmd) ir->request.bRequest = cmd; ir->urb->transfer_flags = 0; ret = usb_submit_urb(ir->urb, GFP_ATOMIC); - if (ret) + if (ret && ret != -EPERM) dev_err(ir->dev, "submit urb failed: %d", ret); } @@ -171,15 +170,18 @@ static int igorplugusb_probe(struct usb_interface *intf, ir->request.bRequest = GET_INFRACODE; ir->request.bRequestType = USB_TYPE_VENDOR | USB_DIR_IN; - ir->request.wLength = cpu_to_le16(sizeof(ir->buf_in)); + ir->request.wLength = cpu_to_le16(MAX_PACKET); ir->urb = usb_alloc_urb(0, GFP_KERNEL); if (!ir->urb) goto fail; + ir->buf_in = kmalloc(MAX_PACKET, GFP_KERNEL); + if (!ir->buf_in) + goto fail; usb_fill_control_urb(ir->urb, udev, usb_rcvctrlpipe(udev, 0), (uint8_t *)&ir->request, - ir->buf_in, sizeof(ir->buf_in), igorplugusb_callback, ir); + ir->buf_in, MAX_PACKET, igorplugusb_callback, ir); usb_make_path(udev, ir->phys, sizeof(ir->phys)); @@ -220,9 +222,12 @@ static int igorplugusb_probe(struct usb_interface *intf, return 0; fail: - rc_free_device(ir->rc); - usb_free_urb(ir->urb); + usb_poison_urb(ir->urb); del_timer(&ir->timer); + usb_unpoison_urb(ir->urb); + usb_free_urb(ir->urb); + rc_free_device(ir->rc); + kfree(ir->buf_in); return ret; } @@ -232,10 +237,12 @@ static void igorplugusb_disconnect(struct usb_interface *intf) struct igorplugusb *ir = usb_get_intfdata(intf); rc_unregister_device(ir->rc); + usb_poison_urb(ir->urb); del_timer_sync(&ir->timer); usb_set_intfdata(intf, NULL); - usb_kill_urb(ir->urb); + usb_unpoison_urb(ir->urb); usb_free_urb(ir->urb); + kfree(ir->buf_in); } static const struct usb_device_id igorplugusb_table[] = { diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c index c9cb8277723f..276bf3c8a8cb 100644 --- a/drivers/media/rc/iguanair.c +++ b/drivers/media/rc/iguanair.c @@ -149,10 +149,8 @@ static void iguanair_rx(struct urb *urb) return; ir = urb->context; - if (!ir) { - usb_unlink_urb(urb); + if (!ir) return; - } switch (urb->status) { case 0: @@ -161,7 +159,6 @@ static void iguanair_rx(struct urb *urb) case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: - usb_unlink_urb(urb); return; case -EPIPE: default: diff --git a/drivers/media/rc/imon_raw.c b/drivers/media/rc/imon_raw.c index d41580f6e4c7..b02ded52f19e 100644 --- a/drivers/media/rc/imon_raw.c +++ b/drivers/media/rc/imon_raw.c @@ -14,7 +14,7 @@ struct imon { struct device *dev; struct urb *ir_urb; struct rc_dev *rcdev; - __be64 ir_buf; + __be64 *ir_buf; char phys[64]; }; @@ -29,7 +29,7 @@ struct imon { static void imon_ir_data(struct imon *imon) { struct ir_raw_event rawir = {}; - u64 data = be64_to_cpu(imon->ir_buf); + u64 data = be64_to_cpup(imon->ir_buf); u8 packet_no = data & 0xff; int offset = 40; int bit; @@ -37,7 +37,7 @@ static void imon_ir_data(struct imon *imon) if (packet_no == 0xff) return; - dev_dbg(imon->dev, "data: %*ph", 8, &imon->ir_buf); + dev_dbg(imon->dev, "data: %*ph", 8, imon->ir_buf); /* * Only the first 5 bytes contain IR data. Right shift so we move @@ -137,10 +137,16 @@ static int imon_probe(struct usb_interface *intf, if (!imon->ir_urb) return -ENOMEM; + imon->ir_buf = kmalloc(sizeof(__be64), GFP_KERNEL); + if (!imon->ir_buf) { + ret = -ENOMEM; + goto free_urb; + } + imon->dev = &intf->dev; usb_fill_int_urb(imon->ir_urb, udev, usb_rcvintpipe(udev, ir_ep->bEndpointAddress), - &imon->ir_buf, sizeof(imon->ir_buf), + imon->ir_buf, sizeof(__be64), imon_ir_rx, imon, ir_ep->bInterval); rcdev = devm_rc_allocate_device(&intf->dev, RC_DRIVER_IR_RAW); @@ -177,6 +183,7 @@ static int imon_probe(struct usb_interface *intf, free_urb: usb_free_urb(imon->ir_urb); + kfree(imon->ir_buf); return ret; } @@ -186,6 +193,7 @@ static void imon_disconnect(struct usb_interface *intf) usb_kill_urb(imon->ir_urb); usb_free_urb(imon->ir_urb); + kfree(imon->ir_buf); } static const struct usb_device_id imon_table[] = { diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index 765375bda0c6..25ab61dae126 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -715,7 +715,7 @@ int lirc_register(struct rc_dev *dev) const char *rx_type, *tx_type; int err, minor; - minor = ida_simple_get(&lirc_ida, 0, RC_DEV_MAX, GFP_KERNEL); + minor = ida_alloc_max(&lirc_ida, RC_DEV_MAX - 1, GFP_KERNEL); if (minor < 0) return minor; @@ -760,7 +760,7 @@ int lirc_register(struct rc_dev *dev) return 0; out_ida: - ida_simple_remove(&lirc_ida, minor); + ida_free(&lirc_ida, minor); return err; } @@ -778,7 +778,7 @@ void lirc_unregister(struct rc_dev *dev) spin_unlock_irqrestore(&dev->lirc_fh_lock, flags); cdev_device_del(&dev->lirc_cdev, &dev->lirc_dev); - ida_simple_remove(&lirc_ida, MINOR(dev->lirc_dev.devt)); + ida_free(&lirc_ida, MINOR(dev->lirc_dev.devt)); } int __init lirc_dev_init(void) diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index b90438a71c80..eba0cd30e314 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -786,7 +786,8 @@ static void ir_do_keydown(struct rc_dev *dev, enum rc_proto protocol, dev->last_toggle != toggle); struct lirc_scancode sc = { .scancode = scancode, .rc_proto = protocol, - .flags = toggle ? LIRC_SCANCODE_FLAG_TOGGLE : 0, + .flags = (toggle ? LIRC_SCANCODE_FLAG_TOGGLE : 0) | + (!new_event ? LIRC_SCANCODE_FLAG_REPEAT : 0), .keycode = keycode }; @@ -1897,7 +1898,7 @@ int rc_register_device(struct rc_dev *dev) if (!dev) return -EINVAL; - minor = ida_simple_get(&rc_ida, 0, RC_DEV_MAX, GFP_KERNEL); + minor = ida_alloc_max(&rc_ida, RC_DEV_MAX - 1, GFP_KERNEL); if (minor < 0) return minor; @@ -1980,7 +1981,7 @@ out_rx_free: out_raw: ir_raw_event_free(dev); out_minor: - ida_simple_remove(&rc_ida, minor); + ida_free(&rc_ida, minor); return rc; } EXPORT_SYMBOL_GPL(rc_register_device); @@ -2040,7 +2041,7 @@ void rc_unregister_device(struct rc_dev *dev) device_del(&dev->dev); - ida_simple_remove(&rc_ida, dev->minor); + ida_free(&rc_ida, dev->minor); if (!dev->managed_alloc) rc_free_device(dev); diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c index cb22316b3f00..9f2947af33aa 100644 --- a/drivers/media/rc/redrat3.c +++ b/drivers/media/rc/redrat3.c @@ -1155,9 +1155,9 @@ static int redrat3_dev_resume(struct usb_interface *intf) { struct redrat3_dev *rr3 = usb_get_intfdata(intf); - if (usb_submit_urb(rr3->narrow_urb, GFP_ATOMIC)) + if (usb_submit_urb(rr3->narrow_urb, GFP_NOIO)) return -EIO; - if (usb_submit_urb(rr3->wide_urb, GFP_ATOMIC)) + if (usb_submit_urb(rr3->wide_urb, GFP_NOIO)) return -EIO; led_classdev_resume(&rr3->led); return 0; diff --git a/drivers/media/rc/streamzap.c b/drivers/media/rc/streamzap.c index deb85330c940..9b209e687f25 100644 --- a/drivers/media/rc/streamzap.c +++ b/drivers/media/rc/streamzap.c @@ -406,7 +406,7 @@ static int streamzap_resume(struct usb_interface *intf) { struct streamzap_ir *sz = usb_get_intfdata(intf); - if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) { + if (usb_submit_urb(sz->urb_in, GFP_NOIO)) { dev_err(sz->dev, "Error submitting urb\n"); return -EIO; } diff --git a/drivers/media/rc/ttusbir.c b/drivers/media/rc/ttusbir.c index 629787d53ee1..560a26f3965c 100644 --- a/drivers/media/rc/ttusbir.c +++ b/drivers/media/rc/ttusbir.c @@ -90,7 +90,6 @@ static void ttusbir_bulk_complete(struct urb *urb) case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: - usb_unlink_urb(urb); return; case -EPIPE: default: @@ -166,7 +165,6 @@ static void ttusbir_urb_complete(struct urb *urb) case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: - usb_unlink_urb(urb); return; case -EPIPE: default: @@ -402,7 +400,7 @@ static int ttusbir_resume(struct usb_interface *intf) led_classdev_resume(&tt->led); for (i = 0; i < NUM_URBS; i++) { - rc = usb_submit_urb(tt->urb[i], GFP_KERNEL); + rc = usb_submit_urb(tt->urb[i], GFP_NOIO); if (rc) { dev_warn(tt->dev, "failed to submit urb: %d\n", rc); break; diff --git a/drivers/media/rc/xbox_remote.c b/drivers/media/rc/xbox_remote.c index 7424b2031152..a1572381d097 100644 --- a/drivers/media/rc/xbox_remote.c +++ b/drivers/media/rc/xbox_remote.c @@ -163,8 +163,8 @@ static void xbox_remote_rc_init(struct xbox_remote *xbox_remote) rdev->dev.parent = &xbox_remote->interface->dev; } -static int xbox_remote_initialize(struct xbox_remote *xbox_remote, - struct usb_endpoint_descriptor *endpoint_in) +static void xbox_remote_initialize(struct xbox_remote *xbox_remote, + struct usb_endpoint_descriptor *endpoint_in) { struct usb_device *udev = xbox_remote->udev; int pipe, maxp; @@ -177,8 +177,6 @@ static int xbox_remote_initialize(struct xbox_remote *xbox_remote, usb_fill_int_urb(xbox_remote->irq_urb, udev, pipe, xbox_remote->inbuf, maxp, xbox_remote_irq_in, xbox_remote, endpoint_in->bInterval); - - return 0; } /* @@ -249,9 +247,7 @@ static int xbox_remote_probe(struct usb_interface *interface, xbox_remote_rc_init(xbox_remote); /* Device Hardware Initialization */ - err = xbox_remote_initialize(xbox_remote, endpoint_in); - if (err) - goto exit_kill_urbs; + xbox_remote_initialize(xbox_remote, endpoint_in); /* Set up and register rc device */ err = rc_register_device(xbox_remote->rdev); diff --git a/drivers/media/test-drivers/vicodec/vicodec-core.c b/drivers/media/test-drivers/vicodec/vicodec-core.c index be43f7d32df9..1d1bee111732 100644 --- a/drivers/media/test-drivers/vicodec/vicodec-core.c +++ b/drivers/media/test-drivers/vicodec/vicodec-core.c @@ -280,17 +280,13 @@ static int device_process(struct vicodec_ctx *ctx, */ if (!(ntohl(ctx->state.header.flags) & V4L2_FWHT_FL_I_FRAME)) { struct vb2_buffer *ref_vb2_buf; - int ref_buf_idx; struct vb2_queue *vq_cap = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - ref_buf_idx = vb2_find_timestamp(vq_cap, - ctx->state.ref_frame_ts, 0); - if (ref_buf_idx < 0) + ref_vb2_buf = vb2_find_buffer(vq_cap, ctx->state.ref_frame_ts); + if (!ref_vb2_buf) return -EINVAL; - - ref_vb2_buf = vq_cap->bufs[ref_buf_idx]; if (ref_vb2_buf->state == VB2_BUF_STATE_ERROR) ret = -EINVAL; ctx->state.ref_frame.buf = diff --git a/drivers/media/test-drivers/vimc/Makefile b/drivers/media/test-drivers/vimc/Makefile index a53b2b532e9f..9b9631562473 100644 --- a/drivers/media/test-drivers/vimc/Makefile +++ b/drivers/media/test-drivers/vimc/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 vimc-y := vimc-core.o vimc-common.o vimc-streamer.o vimc-capture.o \ - vimc-debayer.o vimc-scaler.o vimc-sensor.o + vimc-debayer.o vimc-scaler.o vimc-sensor.o vimc-lens.o obj-$(CONFIG_VIDEO_VIMC) += vimc.o diff --git a/drivers/media/test-drivers/vimc/vimc-capture.c b/drivers/media/test-drivers/vimc/vimc-capture.c index d1e2d0739c00..6c437802f91f 100644 --- a/drivers/media/test-drivers/vimc/vimc-capture.c +++ b/drivers/media/test-drivers/vimc/vimc-capture.c @@ -13,7 +13,7 @@ #include "vimc-common.h" #include "vimc-streamer.h" -struct vimc_cap_device { +struct vimc_capture_device { struct vimc_ent_device ved; struct video_device vdev; struct v4l2_pix_format format; @@ -41,7 +41,7 @@ static const struct v4l2_pix_format fmt_default = { .colorspace = V4L2_COLORSPACE_SRGB, }; -struct vimc_cap_buffer { +struct vimc_capture_buffer { /* * struct vb2_v4l2_buffer must be the first element * the videobuf2 framework will allocate this struct based on @@ -52,7 +52,7 @@ struct vimc_cap_buffer { struct list_head list; }; -static int vimc_cap_querycap(struct file *file, void *priv, +static int vimc_capture_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { strscpy(cap->driver, VIMC_PDEV_NAME, sizeof(cap->driver)); @@ -63,26 +63,26 @@ static int vimc_cap_querycap(struct file *file, void *priv, return 0; } -static void vimc_cap_get_format(struct vimc_ent_device *ved, +static void vimc_capture_get_format(struct vimc_ent_device *ved, struct v4l2_pix_format *fmt) { - struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device, + struct vimc_capture_device *vcapture = container_of(ved, struct vimc_capture_device, ved); - *fmt = vcap->format; + *fmt = vcapture->format; } -static int vimc_cap_g_fmt_vid_cap(struct file *file, void *priv, +static int vimc_capture_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct vimc_cap_device *vcap = video_drvdata(file); + struct vimc_capture_device *vcapture = video_drvdata(file); - f->fmt.pix = vcap->format; + f->fmt.pix = vcapture->format; return 0; } -static int vimc_cap_try_fmt_vid_cap(struct file *file, void *priv, +static int vimc_capture_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct v4l2_pix_format *format = &f->fmt.pix; @@ -114,40 +114,40 @@ static int vimc_cap_try_fmt_vid_cap(struct file *file, void *priv, return 0; } -static int vimc_cap_s_fmt_vid_cap(struct file *file, void *priv, +static int vimc_capture_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct vimc_cap_device *vcap = video_drvdata(file); + struct vimc_capture_device *vcapture = video_drvdata(file); int ret; /* Do not change the format while stream is on */ - if (vb2_is_busy(&vcap->queue)) + if (vb2_is_busy(&vcapture->queue)) return -EBUSY; - ret = vimc_cap_try_fmt_vid_cap(file, priv, f); + ret = vimc_capture_try_fmt_vid_cap(file, priv, f); if (ret) return ret; - dev_dbg(vcap->ved.dev, "%s: format update: " + dev_dbg(vcapture->ved.dev, "%s: format update: " "old:%dx%d (0x%x, %d, %d, %d, %d) " - "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vcap->vdev.name, + "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vcapture->vdev.name, /* old */ - vcap->format.width, vcap->format.height, - vcap->format.pixelformat, vcap->format.colorspace, - vcap->format.quantization, vcap->format.xfer_func, - vcap->format.ycbcr_enc, + vcapture->format.width, vcapture->format.height, + vcapture->format.pixelformat, vcapture->format.colorspace, + vcapture->format.quantization, vcapture->format.xfer_func, + vcapture->format.ycbcr_enc, /* new */ f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.pixelformat, f->fmt.pix.colorspace, f->fmt.pix.quantization, f->fmt.pix.xfer_func, f->fmt.pix.ycbcr_enc); - vcap->format = f->fmt.pix; + vcapture->format = f->fmt.pix; return 0; } -static int vimc_cap_enum_fmt_vid_cap(struct file *file, void *priv, +static int vimc_capture_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { const struct vimc_pix_map *vpix; @@ -169,7 +169,7 @@ static int vimc_cap_enum_fmt_vid_cap(struct file *file, void *priv, return 0; } -static int vimc_cap_enum_framesizes(struct file *file, void *fh, +static int vimc_capture_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize) { const struct vimc_pix_map *vpix; @@ -193,7 +193,7 @@ static int vimc_cap_enum_framesizes(struct file *file, void *fh, return 0; } -static const struct v4l2_file_operations vimc_cap_fops = { +static const struct v4l2_file_operations vimc_capture_fops = { .owner = THIS_MODULE, .open = v4l2_fh_open, .release = vb2_fop_release, @@ -203,14 +203,14 @@ static const struct v4l2_file_operations vimc_cap_fops = { .mmap = vb2_fop_mmap, }; -static const struct v4l2_ioctl_ops vimc_cap_ioctl_ops = { - .vidioc_querycap = vimc_cap_querycap, +static const struct v4l2_ioctl_ops vimc_capture_ioctl_ops = { + .vidioc_querycap = vimc_capture_querycap, - .vidioc_g_fmt_vid_cap = vimc_cap_g_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vimc_cap_s_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vimc_cap_try_fmt_vid_cap, - .vidioc_enum_fmt_vid_cap = vimc_cap_enum_fmt_vid_cap, - .vidioc_enum_framesizes = vimc_cap_enum_framesizes, + .vidioc_g_fmt_vid_cap = vimc_capture_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vimc_capture_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vimc_capture_try_fmt_vid_cap, + .vidioc_enum_fmt_vid_cap = vimc_capture_enum_fmt_vid_cap, + .vidioc_enum_framesizes = vimc_capture_enum_framesizes, .vidioc_reqbufs = vb2_ioctl_reqbufs, .vidioc_create_bufs = vb2_ioctl_create_bufs, @@ -223,40 +223,40 @@ static const struct v4l2_ioctl_ops vimc_cap_ioctl_ops = { .vidioc_streamoff = vb2_ioctl_streamoff, }; -static void vimc_cap_return_all_buffers(struct vimc_cap_device *vcap, +static void vimc_capture_return_all_buffers(struct vimc_capture_device *vcapture, enum vb2_buffer_state state) { - struct vimc_cap_buffer *vbuf, *node; + struct vimc_capture_buffer *vbuf, *node; - spin_lock(&vcap->qlock); + spin_lock(&vcapture->qlock); - list_for_each_entry_safe(vbuf, node, &vcap->buf_list, list) { + list_for_each_entry_safe(vbuf, node, &vcapture->buf_list, list) { list_del(&vbuf->list); vb2_buffer_done(&vbuf->vb2.vb2_buf, state); } - spin_unlock(&vcap->qlock); + spin_unlock(&vcapture->qlock); } -static int vimc_cap_start_streaming(struct vb2_queue *vq, unsigned int count) +static int vimc_capture_start_streaming(struct vb2_queue *vq, unsigned int count) { - struct vimc_cap_device *vcap = vb2_get_drv_priv(vq); - struct media_entity *entity = &vcap->vdev.entity; + struct vimc_capture_device *vcapture = vb2_get_drv_priv(vq); + struct media_entity *entity = &vcapture->vdev.entity; int ret; - vcap->sequence = 0; + vcapture->sequence = 0; /* Start the media pipeline */ - ret = media_pipeline_start(entity, &vcap->stream.pipe); + ret = media_pipeline_start(entity, &vcapture->stream.pipe); if (ret) { - vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED); + vimc_capture_return_all_buffers(vcapture, VB2_BUF_STATE_QUEUED); return ret; } - ret = vimc_streamer_s_stream(&vcap->stream, &vcap->ved, 1); + ret = vimc_streamer_s_stream(&vcapture->stream, &vcapture->ved, 1); if (ret) { media_pipeline_stop(entity); - vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED); + vimc_capture_return_all_buffers(vcapture, VB2_BUF_STATE_QUEUED); return ret; } @@ -267,65 +267,65 @@ static int vimc_cap_start_streaming(struct vb2_queue *vq, unsigned int count) * Stop the stream engine. Any remaining buffers in the stream queue are * dequeued and passed on to the vb2 framework marked as STATE_ERROR. */ -static void vimc_cap_stop_streaming(struct vb2_queue *vq) +static void vimc_capture_stop_streaming(struct vb2_queue *vq) { - struct vimc_cap_device *vcap = vb2_get_drv_priv(vq); + struct vimc_capture_device *vcapture = vb2_get_drv_priv(vq); - vimc_streamer_s_stream(&vcap->stream, &vcap->ved, 0); + vimc_streamer_s_stream(&vcapture->stream, &vcapture->ved, 0); /* Stop the media pipeline */ - media_pipeline_stop(&vcap->vdev.entity); + media_pipeline_stop(&vcapture->vdev.entity); /* Release all active buffers */ - vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_ERROR); + vimc_capture_return_all_buffers(vcapture, VB2_BUF_STATE_ERROR); } -static void vimc_cap_buf_queue(struct vb2_buffer *vb2_buf) +static void vimc_capture_buf_queue(struct vb2_buffer *vb2_buf) { - struct vimc_cap_device *vcap = vb2_get_drv_priv(vb2_buf->vb2_queue); - struct vimc_cap_buffer *buf = container_of(vb2_buf, - struct vimc_cap_buffer, + struct vimc_capture_device *vcapture = vb2_get_drv_priv(vb2_buf->vb2_queue); + struct vimc_capture_buffer *buf = container_of(vb2_buf, + struct vimc_capture_buffer, vb2.vb2_buf); - spin_lock(&vcap->qlock); - list_add_tail(&buf->list, &vcap->buf_list); - spin_unlock(&vcap->qlock); + spin_lock(&vcapture->qlock); + list_add_tail(&buf->list, &vcapture->buf_list); + spin_unlock(&vcapture->qlock); } -static int vimc_cap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, +static int vimc_capture_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], struct device *alloc_devs[]) { - struct vimc_cap_device *vcap = vb2_get_drv_priv(vq); + struct vimc_capture_device *vcapture = vb2_get_drv_priv(vq); if (*nplanes) - return sizes[0] < vcap->format.sizeimage ? -EINVAL : 0; + return sizes[0] < vcapture->format.sizeimage ? -EINVAL : 0; /* We don't support multiplanes for now */ *nplanes = 1; - sizes[0] = vcap->format.sizeimage; + sizes[0] = vcapture->format.sizeimage; return 0; } -static int vimc_cap_buffer_prepare(struct vb2_buffer *vb) +static int vimc_capture_buffer_prepare(struct vb2_buffer *vb) { - struct vimc_cap_device *vcap = vb2_get_drv_priv(vb->vb2_queue); - unsigned long size = vcap->format.sizeimage; + struct vimc_capture_device *vcapture = vb2_get_drv_priv(vb->vb2_queue); + unsigned long size = vcapture->format.sizeimage; if (vb2_plane_size(vb, 0) < size) { - dev_err(vcap->ved.dev, "%s: buffer too small (%lu < %lu)\n", - vcap->vdev.name, vb2_plane_size(vb, 0), size); + dev_err(vcapture->ved.dev, "%s: buffer too small (%lu < %lu)\n", + vcapture->vdev.name, vb2_plane_size(vb, 0), size); return -EINVAL; } return 0; } -static const struct vb2_ops vimc_cap_qops = { - .start_streaming = vimc_cap_start_streaming, - .stop_streaming = vimc_cap_stop_streaming, - .buf_queue = vimc_cap_buf_queue, - .queue_setup = vimc_cap_queue_setup, - .buf_prepare = vimc_cap_buffer_prepare, +static const struct vb2_ops vimc_capture_qops = { + .start_streaming = vimc_capture_start_streaming, + .stop_streaming = vimc_capture_stop_streaming, + .buf_queue = vimc_capture_buf_queue, + .queue_setup = vimc_capture_queue_setup, + .buf_prepare = vimc_capture_buffer_prepare, /* * Since q->lock is set we can use the standard * vb2_ops_wait_prepare/finish helper functions. @@ -334,107 +334,107 @@ static const struct vb2_ops vimc_cap_qops = { .wait_finish = vb2_ops_wait_finish, }; -static const struct media_entity_operations vimc_cap_mops = { +static const struct media_entity_operations vimc_capture_mops = { .link_validate = vimc_vdev_link_validate, }; -static void vimc_cap_release(struct vimc_ent_device *ved) +static void vimc_capture_release(struct vimc_ent_device *ved) { - struct vimc_cap_device *vcap = - container_of(ved, struct vimc_cap_device, ved); + struct vimc_capture_device *vcapture = + container_of(ved, struct vimc_capture_device, ved); - media_entity_cleanup(vcap->ved.ent); - kfree(vcap); + media_entity_cleanup(vcapture->ved.ent); + kfree(vcapture); } -static void vimc_cap_unregister(struct vimc_ent_device *ved) +static void vimc_capture_unregister(struct vimc_ent_device *ved) { - struct vimc_cap_device *vcap = - container_of(ved, struct vimc_cap_device, ved); + struct vimc_capture_device *vcapture = + container_of(ved, struct vimc_capture_device, ved); - vb2_video_unregister_device(&vcap->vdev); + vb2_video_unregister_device(&vcapture->vdev); } -static void *vimc_cap_process_frame(struct vimc_ent_device *ved, +static void *vimc_capture_process_frame(struct vimc_ent_device *ved, const void *frame) { - struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device, + struct vimc_capture_device *vcapture = container_of(ved, struct vimc_capture_device, ved); - struct vimc_cap_buffer *vimc_buf; + struct vimc_capture_buffer *vimc_buf; void *vbuf; - spin_lock(&vcap->qlock); + spin_lock(&vcapture->qlock); /* Get the first entry of the list */ - vimc_buf = list_first_entry_or_null(&vcap->buf_list, + vimc_buf = list_first_entry_or_null(&vcapture->buf_list, typeof(*vimc_buf), list); if (!vimc_buf) { - spin_unlock(&vcap->qlock); + spin_unlock(&vcapture->qlock); return ERR_PTR(-EAGAIN); } /* Remove this entry from the list */ list_del(&vimc_buf->list); - spin_unlock(&vcap->qlock); + spin_unlock(&vcapture->qlock); /* Fill the buffer */ vimc_buf->vb2.vb2_buf.timestamp = ktime_get_ns(); - vimc_buf->vb2.sequence = vcap->sequence++; - vimc_buf->vb2.field = vcap->format.field; + vimc_buf->vb2.sequence = vcapture->sequence++; + vimc_buf->vb2.field = vcapture->format.field; vbuf = vb2_plane_vaddr(&vimc_buf->vb2.vb2_buf, 0); - memcpy(vbuf, frame, vcap->format.sizeimage); + memcpy(vbuf, frame, vcapture->format.sizeimage); /* Set it as ready */ vb2_set_plane_payload(&vimc_buf->vb2.vb2_buf, 0, - vcap->format.sizeimage); + vcapture->format.sizeimage); vb2_buffer_done(&vimc_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE); return NULL; } -static struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc, +static struct vimc_ent_device *vimc_capture_add(struct vimc_device *vimc, const char *vcfg_name) { struct v4l2_device *v4l2_dev = &vimc->v4l2_dev; const struct vimc_pix_map *vpix; - struct vimc_cap_device *vcap; + struct vimc_capture_device *vcapture; struct video_device *vdev; struct vb2_queue *q; int ret; - /* Allocate the vimc_cap_device struct */ - vcap = kzalloc(sizeof(*vcap), GFP_KERNEL); - if (!vcap) + /* Allocate the vimc_capture_device struct */ + vcapture = kzalloc(sizeof(*vcapture), GFP_KERNEL); + if (!vcapture) return ERR_PTR(-ENOMEM); /* Initialize the media entity */ - vcap->vdev.entity.name = vcfg_name; - vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L; - vcap->pad.flags = MEDIA_PAD_FL_SINK; - ret = media_entity_pads_init(&vcap->vdev.entity, - 1, &vcap->pad); + vcapture->vdev.entity.name = vcfg_name; + vcapture->vdev.entity.function = MEDIA_ENT_F_IO_V4L; + vcapture->pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&vcapture->vdev.entity, + 1, &vcapture->pad); if (ret) - goto err_free_vcap; + goto err_free_vcapture; /* Initialize the lock */ - mutex_init(&vcap->lock); + mutex_init(&vcapture->lock); /* Initialize the vb2 queue */ - q = &vcap->queue; + q = &vcapture->queue; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; q->io_modes = VB2_MMAP | VB2_DMABUF; if (vimc_allocator == VIMC_ALLOCATOR_VMALLOC) q->io_modes |= VB2_USERPTR; - q->drv_priv = vcap; - q->buf_struct_size = sizeof(struct vimc_cap_buffer); - q->ops = &vimc_cap_qops; + q->drv_priv = vcapture; + q->buf_struct_size = sizeof(struct vimc_capture_buffer); + q->ops = &vimc_capture_qops; q->mem_ops = vimc_allocator == VIMC_ALLOCATOR_DMA_CONTIG ? &vb2_dma_contig_memops : &vb2_vmalloc_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->min_buffers_needed = 2; - q->lock = &vcap->lock; + q->lock = &vcapture->lock; q->dev = v4l2_dev->dev; ret = vb2_queue_init(q); @@ -445,57 +445,57 @@ static struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc, } /* Initialize buffer list and its lock */ - INIT_LIST_HEAD(&vcap->buf_list); - spin_lock_init(&vcap->qlock); + INIT_LIST_HEAD(&vcapture->buf_list); + spin_lock_init(&vcapture->qlock); /* Set default frame format */ - vcap->format = fmt_default; - vpix = vimc_pix_map_by_pixelformat(vcap->format.pixelformat); - vcap->format.bytesperline = vcap->format.width * vpix->bpp; - vcap->format.sizeimage = vcap->format.bytesperline * - vcap->format.height; + vcapture->format = fmt_default; + vpix = vimc_pix_map_by_pixelformat(vcapture->format.pixelformat); + vcapture->format.bytesperline = vcapture->format.width * vpix->bpp; + vcapture->format.sizeimage = vcapture->format.bytesperline * + vcapture->format.height; /* Fill the vimc_ent_device struct */ - vcap->ved.ent = &vcap->vdev.entity; - vcap->ved.process_frame = vimc_cap_process_frame; - vcap->ved.vdev_get_format = vimc_cap_get_format; - vcap->ved.dev = vimc->mdev.dev; + vcapture->ved.ent = &vcapture->vdev.entity; + vcapture->ved.process_frame = vimc_capture_process_frame; + vcapture->ved.vdev_get_format = vimc_capture_get_format; + vcapture->ved.dev = vimc->mdev.dev; /* Initialize the video_device struct */ - vdev = &vcap->vdev; + vdev = &vcapture->vdev; vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_IO_MC; - vdev->entity.ops = &vimc_cap_mops; + vdev->entity.ops = &vimc_capture_mops; vdev->release = video_device_release_empty; - vdev->fops = &vimc_cap_fops; - vdev->ioctl_ops = &vimc_cap_ioctl_ops; - vdev->lock = &vcap->lock; + vdev->fops = &vimc_capture_fops; + vdev->ioctl_ops = &vimc_capture_ioctl_ops; + vdev->lock = &vcapture->lock; vdev->queue = q; vdev->v4l2_dev = v4l2_dev; vdev->vfl_dir = VFL_DIR_RX; strscpy(vdev->name, vcfg_name, sizeof(vdev->name)); - video_set_drvdata(vdev, &vcap->ved); + video_set_drvdata(vdev, &vcapture->ved); /* Register the video_device with the v4l2 and the media framework */ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) { dev_err(vimc->mdev.dev, "%s: video register failed (err=%d)\n", - vcap->vdev.name, ret); + vcapture->vdev.name, ret); goto err_clean_m_ent; } - return &vcap->ved; + return &vcapture->ved; err_clean_m_ent: - media_entity_cleanup(&vcap->vdev.entity); -err_free_vcap: - kfree(vcap); + media_entity_cleanup(&vcapture->vdev.entity); +err_free_vcapture: + kfree(vcapture); return ERR_PTR(ret); } -struct vimc_ent_type vimc_cap_type = { - .add = vimc_cap_add, - .unregister = vimc_cap_unregister, - .release = vimc_cap_release +struct vimc_ent_type vimc_capture_type = { + .add = vimc_capture_add, + .unregister = vimc_capture_unregister, + .release = vimc_capture_release }; diff --git a/drivers/media/test-drivers/vimc/vimc-common.h b/drivers/media/test-drivers/vimc/vimc-common.h index ba1930772589..7641a101a728 100644 --- a/drivers/media/test-drivers/vimc/vimc-common.h +++ b/drivers/media/test-drivers/vimc/vimc-common.h @@ -167,10 +167,11 @@ struct vimc_ent_config { */ bool vimc_is_source(struct media_entity *ent); -extern struct vimc_ent_type vimc_sen_type; -extern struct vimc_ent_type vimc_deb_type; -extern struct vimc_ent_type vimc_sca_type; -extern struct vimc_ent_type vimc_cap_type; +extern struct vimc_ent_type vimc_sensor_type; +extern struct vimc_ent_type vimc_debayer_type; +extern struct vimc_ent_type vimc_scaler_type; +extern struct vimc_ent_type vimc_capture_type; +extern struct vimc_ent_type vimc_lens_type; /** * vimc_pix_map_by_index - get vimc_pix_map struct by its index diff --git a/drivers/media/test-drivers/vimc/vimc-core.c b/drivers/media/test-drivers/vimc/vimc-core.c index 06edf9d4d92c..2ae7a0f11ebf 100644 --- a/drivers/media/test-drivers/vimc/vimc-core.c +++ b/drivers/media/test-drivers/vimc/vimc-core.c @@ -24,7 +24,7 @@ MODULE_PARM_DESC(allocator, " memory allocator selection, default is 0.\n" #define VIMC_MDEV_MODEL_NAME "VIMC MDEV" -#define VIMC_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) { \ +#define VIMC_DATA_LINK(src, srcpad, sink, sinkpad, link_flags) { \ .src_ent = src, \ .src_pad = srcpad, \ .sink_ent = sink, \ @@ -32,8 +32,13 @@ MODULE_PARM_DESC(allocator, " memory allocator selection, default is 0.\n" .flags = link_flags, \ } -/* Structure which describes links between entities */ -struct vimc_ent_link { +#define VIMC_ANCILLARY_LINK(primary, ancillary) { \ + .primary_ent = primary, \ + .ancillary_ent = ancillary \ +} + +/* Structure which describes data links between entities */ +struct vimc_data_link { unsigned int src_ent; u16 src_pad; unsigned int sink_ent; @@ -41,12 +46,35 @@ struct vimc_ent_link { u32 flags; }; +/* Enum to improve clarity when defining vimc_data_links */ +enum vimc_data_link_ents { + SENSOR_A, + SENSOR_B, + DEBAYER_A, + DEBAYER_B, + RAW_CAPTURE_0, + RAW_CAPTURE_1, + RGB_YUV_INPUT, + SCALER, + RGB_YUV_CAPTURE, + LENS_A, + LENS_B, +}; + +/* Structure which describes ancillary links between entities */ +struct vimc_ancillary_link { + unsigned int primary_ent; + unsigned int ancillary_ent; +}; + /* Structure which describes the whole topology */ struct vimc_pipeline_config { const struct vimc_ent_config *ents; size_t num_ents; - const struct vimc_ent_link *links; - size_t num_links; + const struct vimc_data_link *data_links; + size_t num_data_links; + const struct vimc_ancillary_link *ancillary_links; + size_t num_ancillary_links; }; /* -------------------------------------------------------------------------- @@ -54,69 +82,91 @@ struct vimc_pipeline_config { */ static struct vimc_ent_config ent_config[] = { - { + [SENSOR_A] = { .name = "Sensor A", - .type = &vimc_sen_type + .type = &vimc_sensor_type }, - { + [SENSOR_B] = { .name = "Sensor B", - .type = &vimc_sen_type + .type = &vimc_sensor_type }, - { + [DEBAYER_A] = { .name = "Debayer A", - .type = &vimc_deb_type + .type = &vimc_debayer_type }, - { + [DEBAYER_B] = { .name = "Debayer B", - .type = &vimc_deb_type + .type = &vimc_debayer_type }, - { + [RAW_CAPTURE_0] = { .name = "Raw Capture 0", - .type = &vimc_cap_type + .type = &vimc_capture_type }, - { + [RAW_CAPTURE_1] = { .name = "Raw Capture 1", - .type = &vimc_cap_type + .type = &vimc_capture_type }, - { + [RGB_YUV_INPUT] = { /* TODO: change this to vimc-input when it is implemented */ .name = "RGB/YUV Input", - .type = &vimc_sen_type + .type = &vimc_sensor_type }, - { + [SCALER] = { .name = "Scaler", - .type = &vimc_sca_type + .type = &vimc_scaler_type }, - { + [RGB_YUV_CAPTURE] = { .name = "RGB/YUV Capture", - .type = &vimc_cap_type + .type = &vimc_capture_type + }, + [LENS_A] = { + .name = "Lens A", + .type = &vimc_lens_type + }, + [LENS_B] = { + .name = "Lens B", + .type = &vimc_lens_type }, }; -static const struct vimc_ent_link ent_links[] = { +static const struct vimc_data_link data_links[] = { /* Link: Sensor A (Pad 0)->(Pad 0) Debayer A */ - VIMC_ENT_LINK(0, 0, 2, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), + VIMC_DATA_LINK(SENSOR_A, 0, DEBAYER_A, 0, + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), /* Link: Sensor A (Pad 0)->(Pad 0) Raw Capture 0 */ - VIMC_ENT_LINK(0, 0, 4, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), + VIMC_DATA_LINK(SENSOR_A, 0, RAW_CAPTURE_0, 0, + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), /* Link: Sensor B (Pad 0)->(Pad 0) Debayer B */ - VIMC_ENT_LINK(1, 0, 3, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), + VIMC_DATA_LINK(SENSOR_B, 0, DEBAYER_B, 0, + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), /* Link: Sensor B (Pad 0)->(Pad 0) Raw Capture 1 */ - VIMC_ENT_LINK(1, 0, 5, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), + VIMC_DATA_LINK(SENSOR_B, 0, RAW_CAPTURE_1, 0, + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), /* Link: Debayer A (Pad 1)->(Pad 0) Scaler */ - VIMC_ENT_LINK(2, 1, 7, 0, MEDIA_LNK_FL_ENABLED), + VIMC_DATA_LINK(DEBAYER_A, 1, SCALER, 0, MEDIA_LNK_FL_ENABLED), /* Link: Debayer B (Pad 1)->(Pad 0) Scaler */ - VIMC_ENT_LINK(3, 1, 7, 0, 0), + VIMC_DATA_LINK(DEBAYER_B, 1, SCALER, 0, 0), /* Link: RGB/YUV Input (Pad 0)->(Pad 0) Scaler */ - VIMC_ENT_LINK(6, 0, 7, 0, 0), + VIMC_DATA_LINK(RGB_YUV_INPUT, 0, SCALER, 0, 0), /* Link: Scaler (Pad 1)->(Pad 0) RGB/YUV Capture */ - VIMC_ENT_LINK(7, 1, 8, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), + VIMC_DATA_LINK(SCALER, 1, RGB_YUV_CAPTURE, 0, + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), +}; + +static const struct vimc_ancillary_link ancillary_links[] = { + /* Link: Sensor A -> Lens A */ + VIMC_ANCILLARY_LINK(0, 9), + /* Link: Sensor B -> Lens B */ + VIMC_ANCILLARY_LINK(1, 10), }; static struct vimc_pipeline_config pipe_cfg = { - .ents = ent_config, - .num_ents = ARRAY_SIZE(ent_config), - .links = ent_links, - .num_links = ARRAY_SIZE(ent_links) + .ents = ent_config, + .num_ents = ARRAY_SIZE(ent_config), + .data_links = data_links, + .num_data_links = ARRAY_SIZE(data_links), + .ancillary_links = ancillary_links, + .num_ancillary_links = ARRAY_SIZE(ancillary_links), }; /* -------------------------------------------------------------------------- */ @@ -135,8 +185,8 @@ static int vimc_create_links(struct vimc_device *vimc) int ret; /* Initialize the links between entities */ - for (i = 0; i < vimc->pipe_cfg->num_links; i++) { - const struct vimc_ent_link *link = &vimc->pipe_cfg->links[i]; + for (i = 0; i < vimc->pipe_cfg->num_data_links; i++) { + const struct vimc_data_link *link = &vimc->pipe_cfg->data_links[i]; struct vimc_ent_device *ved_src = vimc->ent_devs[link->src_ent]; @@ -150,6 +200,22 @@ static int vimc_create_links(struct vimc_device *vimc) goto err_rm_links; } + for (i = 0; i < vimc->pipe_cfg->num_ancillary_links; i++) { + const struct vimc_ancillary_link *link = &vimc->pipe_cfg->ancillary_links[i]; + + struct vimc_ent_device *ved_primary = + vimc->ent_devs[link->primary_ent]; + struct vimc_ent_device *ved_ancillary = + vimc->ent_devs[link->ancillary_ent]; + struct media_link *ret_link = + media_create_ancillary_link(ved_primary->ent, ved_ancillary->ent); + + if (IS_ERR(ret_link)) { + ret = PTR_ERR(ret_link); + goto err_rm_links; + } + } + return 0; err_rm_links: diff --git a/drivers/media/test-drivers/vimc/vimc-debayer.c b/drivers/media/test-drivers/vimc/vimc-debayer.c index 2d06cdbacc76..f671251fdf0e 100644 --- a/drivers/media/test-drivers/vimc/vimc-debayer.c +++ b/drivers/media/test-drivers/vimc/vimc-debayer.c @@ -15,28 +15,29 @@ #include "vimc-common.h" -enum vimc_deb_rgb_colors { - VIMC_DEB_RED = 0, - VIMC_DEB_GREEN = 1, - VIMC_DEB_BLUE = 2, +enum vimc_debayer_rgb_colors { + VIMC_DEBAYER_RED = 0, + VIMC_DEBAYER_GREEN = 1, + VIMC_DEBAYER_BLUE = 2, }; -struct vimc_deb_pix_map { +struct vimc_debayer_pix_map { u32 code; - enum vimc_deb_rgb_colors order[2][2]; + enum vimc_debayer_rgb_colors order[2][2]; }; -struct vimc_deb_device { +struct vimc_debayer_device { struct vimc_ent_device ved; struct v4l2_subdev sd; /* The active format */ struct v4l2_mbus_framefmt sink_fmt; u32 src_code; - void (*set_rgb_src)(struct vimc_deb_device *vdeb, unsigned int lin, - unsigned int col, unsigned int rgb[3]); + void (*set_rgb_src)(struct vimc_debayer_device *vdebayer, + unsigned int lin, unsigned int col, + unsigned int rgb[3]); /* Values calculated when the stream starts */ u8 *src_frame; - const struct vimc_deb_pix_map *sink_pix_map; + const struct vimc_debayer_pix_map *sink_pix_map; unsigned int sink_bpp; unsigned int mean_win_size; struct v4l2_ctrl_handler hdl; @@ -51,7 +52,7 @@ static const struct v4l2_mbus_framefmt sink_fmt_default = { .colorspace = V4L2_COLORSPACE_SRGB, }; -static const u32 vimc_deb_src_mbus_codes[] = { +static const u32 vimc_debayer_src_mbus_codes[] = { MEDIA_BUS_FMT_GBR888_1X24, MEDIA_BUS_FMT_BGR888_1X24, MEDIA_BUS_FMT_BGR888_3X8, @@ -64,95 +65,95 @@ static const u32 vimc_deb_src_mbus_codes[] = { MEDIA_BUS_FMT_RGB888_1X32_PADHI, }; -static const struct vimc_deb_pix_map vimc_deb_pix_map_list[] = { +static const struct vimc_debayer_pix_map vimc_debayer_pix_map_list[] = { { .code = MEDIA_BUS_FMT_SBGGR8_1X8, - .order = { { VIMC_DEB_BLUE, VIMC_DEB_GREEN }, - { VIMC_DEB_GREEN, VIMC_DEB_RED } } + .order = { { VIMC_DEBAYER_BLUE, VIMC_DEBAYER_GREEN }, + { VIMC_DEBAYER_GREEN, VIMC_DEBAYER_RED } } }, { .code = MEDIA_BUS_FMT_SGBRG8_1X8, - .order = { { VIMC_DEB_GREEN, VIMC_DEB_BLUE }, - { VIMC_DEB_RED, VIMC_DEB_GREEN } } + .order = { { VIMC_DEBAYER_GREEN, VIMC_DEBAYER_BLUE }, + { VIMC_DEBAYER_RED, VIMC_DEBAYER_GREEN } } }, { .code = MEDIA_BUS_FMT_SGRBG8_1X8, - .order = { { VIMC_DEB_GREEN, VIMC_DEB_RED }, - { VIMC_DEB_BLUE, VIMC_DEB_GREEN } } + .order = { { VIMC_DEBAYER_GREEN, VIMC_DEBAYER_RED }, + { VIMC_DEBAYER_BLUE, VIMC_DEBAYER_GREEN } } }, { .code = MEDIA_BUS_FMT_SRGGB8_1X8, - .order = { { VIMC_DEB_RED, VIMC_DEB_GREEN }, - { VIMC_DEB_GREEN, VIMC_DEB_BLUE } } + .order = { { VIMC_DEBAYER_RED, VIMC_DEBAYER_GREEN }, + { VIMC_DEBAYER_GREEN, VIMC_DEBAYER_BLUE } } }, { .code = MEDIA_BUS_FMT_SBGGR10_1X10, - .order = { { VIMC_DEB_BLUE, VIMC_DEB_GREEN }, - { VIMC_DEB_GREEN, VIMC_DEB_RED } } + .order = { { VIMC_DEBAYER_BLUE, VIMC_DEBAYER_GREEN }, + { VIMC_DEBAYER_GREEN, VIMC_DEBAYER_RED } } }, { .code = MEDIA_BUS_FMT_SGBRG10_1X10, - .order = { { VIMC_DEB_GREEN, VIMC_DEB_BLUE }, - { VIMC_DEB_RED, VIMC_DEB_GREEN } } + .order = { { VIMC_DEBAYER_GREEN, VIMC_DEBAYER_BLUE }, + { VIMC_DEBAYER_RED, VIMC_DEBAYER_GREEN } } }, { .code = MEDIA_BUS_FMT_SGRBG10_1X10, - .order = { { VIMC_DEB_GREEN, VIMC_DEB_RED }, - { VIMC_DEB_BLUE, VIMC_DEB_GREEN } } + .order = { { VIMC_DEBAYER_GREEN, VIMC_DEBAYER_RED }, + { VIMC_DEBAYER_BLUE, VIMC_DEBAYER_GREEN } } }, { .code = MEDIA_BUS_FMT_SRGGB10_1X10, - .order = { { VIMC_DEB_RED, VIMC_DEB_GREEN }, - { VIMC_DEB_GREEN, VIMC_DEB_BLUE } } + .order = { { VIMC_DEBAYER_RED, VIMC_DEBAYER_GREEN }, + { VIMC_DEBAYER_GREEN, VIMC_DEBAYER_BLUE } } }, { .code = MEDIA_BUS_FMT_SBGGR12_1X12, - .order = { { VIMC_DEB_BLUE, VIMC_DEB_GREEN }, - { VIMC_DEB_GREEN, VIMC_DEB_RED } } + .order = { { VIMC_DEBAYER_BLUE, VIMC_DEBAYER_GREEN }, + { VIMC_DEBAYER_GREEN, VIMC_DEBAYER_RED } } }, { .code = MEDIA_BUS_FMT_SGBRG12_1X12, - .order = { { VIMC_DEB_GREEN, VIMC_DEB_BLUE }, - { VIMC_DEB_RED, VIMC_DEB_GREEN } } + .order = { { VIMC_DEBAYER_GREEN, VIMC_DEBAYER_BLUE }, + { VIMC_DEBAYER_RED, VIMC_DEBAYER_GREEN } } }, { .code = MEDIA_BUS_FMT_SGRBG12_1X12, - .order = { { VIMC_DEB_GREEN, VIMC_DEB_RED }, - { VIMC_DEB_BLUE, VIMC_DEB_GREEN } } + .order = { { VIMC_DEBAYER_GREEN, VIMC_DEBAYER_RED }, + { VIMC_DEBAYER_BLUE, VIMC_DEBAYER_GREEN } } }, { .code = MEDIA_BUS_FMT_SRGGB12_1X12, - .order = { { VIMC_DEB_RED, VIMC_DEB_GREEN }, - { VIMC_DEB_GREEN, VIMC_DEB_BLUE } } + .order = { { VIMC_DEBAYER_RED, VIMC_DEBAYER_GREEN }, + { VIMC_DEBAYER_GREEN, VIMC_DEBAYER_BLUE } } }, }; -static const struct vimc_deb_pix_map *vimc_deb_pix_map_by_code(u32 code) +static const struct vimc_debayer_pix_map *vimc_debayer_pix_map_by_code(u32 code) { unsigned int i; - for (i = 0; i < ARRAY_SIZE(vimc_deb_pix_map_list); i++) - if (vimc_deb_pix_map_list[i].code == code) - return &vimc_deb_pix_map_list[i]; + for (i = 0; i < ARRAY_SIZE(vimc_debayer_pix_map_list); i++) + if (vimc_debayer_pix_map_list[i].code == code) + return &vimc_debayer_pix_map_list[i]; return NULL; } -static bool vimc_deb_src_code_is_valid(u32 code) +static bool vimc_debayer_src_code_is_valid(u32 code) { unsigned int i; - for (i = 0; i < ARRAY_SIZE(vimc_deb_src_mbus_codes); i++) - if (vimc_deb_src_mbus_codes[i] == code) + for (i = 0; i < ARRAY_SIZE(vimc_debayer_src_mbus_codes); i++) + if (vimc_debayer_src_mbus_codes[i] == code) return true; return false; } -static int vimc_deb_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int vimc_debayer_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { - struct vimc_deb_device *vdeb = v4l2_get_subdevdata(sd); + struct vimc_debayer_device *vdebayer = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *mf; unsigned int i; @@ -162,45 +163,45 @@ static int vimc_deb_init_cfg(struct v4l2_subdev *sd, for (i = 1; i < sd->entity.num_pads; i++) { mf = v4l2_subdev_get_try_format(sd, sd_state, i); *mf = sink_fmt_default; - mf->code = vdeb->src_code; + mf->code = vdebayer->src_code; } return 0; } -static int vimc_deb_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_mbus_code_enum *code) +static int vimc_debayer_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) { if (VIMC_IS_SRC(code->pad)) { - if (code->index >= ARRAY_SIZE(vimc_deb_src_mbus_codes)) + if (code->index >= ARRAY_SIZE(vimc_debayer_src_mbus_codes)) return -EINVAL; - code->code = vimc_deb_src_mbus_codes[code->index]; + code->code = vimc_debayer_src_mbus_codes[code->index]; } else { - if (code->index >= ARRAY_SIZE(vimc_deb_pix_map_list)) + if (code->index >= ARRAY_SIZE(vimc_debayer_pix_map_list)) return -EINVAL; - code->code = vimc_deb_pix_map_list[code->index].code; + code->code = vimc_debayer_pix_map_list[code->index].code; } return 0; } -static int vimc_deb_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_size_enum *fse) +static int vimc_debayer_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) { if (fse->index) return -EINVAL; if (VIMC_IS_SINK(fse->pad)) { - const struct vimc_deb_pix_map *vpix = - vimc_deb_pix_map_by_code(fse->code); + const struct vimc_debayer_pix_map *vpix = + vimc_debayer_pix_map_by_code(fse->code); if (!vpix) return -EINVAL; - } else if (!vimc_deb_src_code_is_valid(fse->code)) { + } else if (!vimc_debayer_src_code_is_valid(fse->code)) { return -EINVAL; } @@ -212,30 +213,30 @@ static int vimc_deb_enum_frame_size(struct v4l2_subdev *sd, return 0; } -static int vimc_deb_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) +static int vimc_debayer_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) { - struct vimc_deb_device *vdeb = v4l2_get_subdevdata(sd); + struct vimc_debayer_device *vdebayer = v4l2_get_subdevdata(sd); /* Get the current sink format */ fmt->format = fmt->which == V4L2_SUBDEV_FORMAT_TRY ? *v4l2_subdev_get_try_format(sd, sd_state, 0) : - vdeb->sink_fmt; + vdebayer->sink_fmt; /* Set the right code for the source pad */ if (VIMC_IS_SRC(fmt->pad)) - fmt->format.code = vdeb->src_code; + fmt->format.code = vdebayer->src_code; return 0; } -static void vimc_deb_adjust_sink_fmt(struct v4l2_mbus_framefmt *fmt) +static void vimc_debayer_adjust_sink_fmt(struct v4l2_mbus_framefmt *fmt) { - const struct vimc_deb_pix_map *vpix; + const struct vimc_debayer_pix_map *vpix; /* Don't accept a code that is not on the debayer table */ - vpix = vimc_deb_pix_map_by_code(fmt->code); + vpix = vimc_debayer_pix_map_by_code(fmt->code); if (!vpix) fmt->code = sink_fmt_default.code; @@ -250,21 +251,21 @@ static void vimc_deb_adjust_sink_fmt(struct v4l2_mbus_framefmt *fmt) vimc_colorimetry_clamp(fmt); } -static int vimc_deb_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) +static int vimc_debayer_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) { - struct vimc_deb_device *vdeb = v4l2_get_subdevdata(sd); + struct vimc_debayer_device *vdebayer = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *sink_fmt; u32 *src_code; if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { /* Do not change the format while stream is on */ - if (vdeb->src_frame) + if (vdebayer->src_frame) return -EBUSY; - sink_fmt = &vdeb->sink_fmt; - src_code = &vdeb->src_code; + sink_fmt = &vdebayer->sink_fmt; + src_code = &vdebayer->src_code; } else { sink_fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); src_code = &v4l2_subdev_get_try_format(sd, sd_state, 1)->code; @@ -279,17 +280,17 @@ static int vimc_deb_set_fmt(struct v4l2_subdev *sd, fmt->format = *sink_fmt; - if (vimc_deb_src_code_is_valid(code)) + if (vimc_debayer_src_code_is_valid(code)) *src_code = code; fmt->format.code = *src_code; } else { /* Set the new format in the sink pad */ - vimc_deb_adjust_sink_fmt(&fmt->format); + vimc_debayer_adjust_sink_fmt(&fmt->format); - dev_dbg(vdeb->ved.dev, "%s: sink format update: " + dev_dbg(vdebayer->ved.dev, "%s: sink format update: " "old:%dx%d (0x%x, %d, %d, %d, %d) " - "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vdeb->sd.name, + "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vdebayer->sd.name, /* old */ sink_fmt->width, sink_fmt->height, sink_fmt->code, sink_fmt->colorspace, sink_fmt->quantization, @@ -305,97 +306,97 @@ static int vimc_deb_set_fmt(struct v4l2_subdev *sd, return 0; } -static const struct v4l2_subdev_pad_ops vimc_deb_pad_ops = { - .init_cfg = vimc_deb_init_cfg, - .enum_mbus_code = vimc_deb_enum_mbus_code, - .enum_frame_size = vimc_deb_enum_frame_size, - .get_fmt = vimc_deb_get_fmt, - .set_fmt = vimc_deb_set_fmt, +static const struct v4l2_subdev_pad_ops vimc_debayer_pad_ops = { + .init_cfg = vimc_debayer_init_cfg, + .enum_mbus_code = vimc_debayer_enum_mbus_code, + .enum_frame_size = vimc_debayer_enum_frame_size, + .get_fmt = vimc_debayer_get_fmt, + .set_fmt = vimc_debayer_set_fmt, }; -static void vimc_deb_process_rgb_frame(struct vimc_deb_device *vdeb, - unsigned int lin, - unsigned int col, - unsigned int rgb[3]) +static void vimc_debayer_process_rgb_frame(struct vimc_debayer_device *vdebayer, + unsigned int lin, + unsigned int col, + unsigned int rgb[3]) { const struct vimc_pix_map *vpix; unsigned int i, index; - vpix = vimc_pix_map_by_code(vdeb->src_code); - index = VIMC_FRAME_INDEX(lin, col, vdeb->sink_fmt.width, 3); + vpix = vimc_pix_map_by_code(vdebayer->src_code); + index = VIMC_FRAME_INDEX(lin, col, vdebayer->sink_fmt.width, 3); for (i = 0; i < 3; i++) { switch (vpix->pixelformat) { case V4L2_PIX_FMT_RGB24: - vdeb->src_frame[index + i] = rgb[i]; + vdebayer->src_frame[index + i] = rgb[i]; break; case V4L2_PIX_FMT_BGR24: - vdeb->src_frame[index + i] = rgb[2 - i]; + vdebayer->src_frame[index + i] = rgb[2 - i]; break; } } } -static int vimc_deb_s_stream(struct v4l2_subdev *sd, int enable) +static int vimc_debayer_s_stream(struct v4l2_subdev *sd, int enable) { - struct vimc_deb_device *vdeb = v4l2_get_subdevdata(sd); + struct vimc_debayer_device *vdebayer = v4l2_get_subdevdata(sd); if (enable) { const struct vimc_pix_map *vpix; unsigned int frame_size; - if (vdeb->src_frame) + if (vdebayer->src_frame) return 0; /* Calculate the frame size of the source pad */ - vpix = vimc_pix_map_by_code(vdeb->src_code); - frame_size = vdeb->sink_fmt.width * vdeb->sink_fmt.height * + vpix = vimc_pix_map_by_code(vdebayer->src_code); + frame_size = vdebayer->sink_fmt.width * vdebayer->sink_fmt.height * vpix->bpp; /* Save the bytes per pixel of the sink */ - vpix = vimc_pix_map_by_code(vdeb->sink_fmt.code); - vdeb->sink_bpp = vpix->bpp; + vpix = vimc_pix_map_by_code(vdebayer->sink_fmt.code); + vdebayer->sink_bpp = vpix->bpp; /* Get the corresponding pixel map from the table */ - vdeb->sink_pix_map = - vimc_deb_pix_map_by_code(vdeb->sink_fmt.code); + vdebayer->sink_pix_map = + vimc_debayer_pix_map_by_code(vdebayer->sink_fmt.code); /* * Allocate the frame buffer. Use vmalloc to be able to * allocate a large amount of memory */ - vdeb->src_frame = vmalloc(frame_size); - if (!vdeb->src_frame) + vdebayer->src_frame = vmalloc(frame_size); + if (!vdebayer->src_frame) return -ENOMEM; } else { - if (!vdeb->src_frame) + if (!vdebayer->src_frame) return 0; - vfree(vdeb->src_frame); - vdeb->src_frame = NULL; + vfree(vdebayer->src_frame); + vdebayer->src_frame = NULL; } return 0; } -static const struct v4l2_subdev_core_ops vimc_deb_core_ops = { +static const struct v4l2_subdev_core_ops vimc_debayer_core_ops = { .log_status = v4l2_ctrl_subdev_log_status, .subscribe_event = v4l2_ctrl_subdev_subscribe_event, .unsubscribe_event = v4l2_event_subdev_unsubscribe, }; -static const struct v4l2_subdev_video_ops vimc_deb_video_ops = { - .s_stream = vimc_deb_s_stream, +static const struct v4l2_subdev_video_ops vimc_debayer_video_ops = { + .s_stream = vimc_debayer_s_stream, }; -static const struct v4l2_subdev_ops vimc_deb_ops = { - .core = &vimc_deb_core_ops, - .pad = &vimc_deb_pad_ops, - .video = &vimc_deb_video_ops, +static const struct v4l2_subdev_ops vimc_debayer_ops = { + .core = &vimc_debayer_core_ops, + .pad = &vimc_debayer_pad_ops, + .video = &vimc_debayer_video_ops, }; -static unsigned int vimc_deb_get_val(const u8 *bytes, - const unsigned int n_bytes) +static unsigned int vimc_debayer_get_val(const u8 *bytes, + const unsigned int n_bytes) { unsigned int i; unsigned int acc = 0; @@ -406,11 +407,11 @@ static unsigned int vimc_deb_get_val(const u8 *bytes, return acc; } -static void vimc_deb_calc_rgb_sink(struct vimc_deb_device *vdeb, - const u8 *frame, - const unsigned int lin, - const unsigned int col, - unsigned int rgb[3]) +static void vimc_debayer_calc_rgb_sink(struct vimc_debayer_device *vdebayer, + const u8 *frame, + const unsigned int lin, + const unsigned int col, + unsigned int rgb[3]) { unsigned int i, seek, wlin, wcol; unsigned int n_rgb[3] = {0, 0, 0}; @@ -423,13 +424,13 @@ static void vimc_deb_calc_rgb_sink(struct vimc_deb_device *vdeb, * the top left corner of the mean window (considering the current * pixel as the center) */ - seek = vdeb->mean_win_size / 2; + seek = vdebayer->mean_win_size / 2; /* Sum the values of the colors in the mean window */ - dev_dbg(vdeb->ved.dev, + dev_dbg(vdebayer->ved.dev, "deb: %s: --- Calc pixel %dx%d, window mean %d, seek %d ---\n", - vdeb->sd.name, lin, col, vdeb->sink_fmt.height, seek); + vdebayer->sd.name, lin, col, vdebayer->sink_fmt.height, seek); /* * Iterate through all the lines in the mean window, start @@ -438,7 +439,7 @@ static void vimc_deb_calc_rgb_sink(struct vimc_deb_device *vdeb, * frame */ for (wlin = seek > lin ? 0 : lin - seek; - wlin < lin + seek + 1 && wlin < vdeb->sink_fmt.height; + wlin < lin + seek + 1 && wlin < vdebayer->sink_fmt.height; wlin++) { /* @@ -448,78 +449,80 @@ static void vimc_deb_calc_rgb_sink(struct vimc_deb_device *vdeb, * frame */ for (wcol = seek > col ? 0 : col - seek; - wcol < col + seek + 1 && wcol < vdeb->sink_fmt.width; + wcol < col + seek + 1 && wcol < vdebayer->sink_fmt.width; wcol++) { - enum vimc_deb_rgb_colors color; + enum vimc_debayer_rgb_colors color; unsigned int index; /* Check which color this pixel is */ - color = vdeb->sink_pix_map->order[wlin % 2][wcol % 2]; + color = vdebayer->sink_pix_map->order[wlin % 2][wcol % 2]; index = VIMC_FRAME_INDEX(wlin, wcol, - vdeb->sink_fmt.width, - vdeb->sink_bpp); + vdebayer->sink_fmt.width, + vdebayer->sink_bpp); - dev_dbg(vdeb->ved.dev, + dev_dbg(vdebayer->ved.dev, "deb: %s: RGB CALC: frame index %d, win pos %dx%d, color %d\n", - vdeb->sd.name, index, wlin, wcol, color); + vdebayer->sd.name, index, wlin, wcol, color); /* Get its value */ rgb[color] = rgb[color] + - vimc_deb_get_val(&frame[index], vdeb->sink_bpp); + vimc_debayer_get_val(&frame[index], + vdebayer->sink_bpp); /* Save how many values we already added */ n_rgb[color]++; - dev_dbg(vdeb->ved.dev, "deb: %s: RGB CALC: val %d, n %d\n", - vdeb->sd.name, rgb[color], n_rgb[color]); + dev_dbg(vdebayer->ved.dev, "deb: %s: RGB CALC: val %d, n %d\n", + vdebayer->sd.name, rgb[color], n_rgb[color]); } } /* Calculate the mean */ for (i = 0; i < 3; i++) { - dev_dbg(vdeb->ved.dev, + dev_dbg(vdebayer->ved.dev, "deb: %s: PRE CALC: %dx%d Color %d, val %d, n %d\n", - vdeb->sd.name, lin, col, i, rgb[i], n_rgb[i]); + vdebayer->sd.name, lin, col, i, rgb[i], n_rgb[i]); if (n_rgb[i]) rgb[i] = rgb[i] / n_rgb[i]; - dev_dbg(vdeb->ved.dev, + dev_dbg(vdebayer->ved.dev, "deb: %s: FINAL CALC: %dx%d Color %d, val %d\n", - vdeb->sd.name, lin, col, i, rgb[i]); + vdebayer->sd.name, lin, col, i, rgb[i]); } } -static void *vimc_deb_process_frame(struct vimc_ent_device *ved, - const void *sink_frame) +static void *vimc_debayer_process_frame(struct vimc_ent_device *ved, + const void *sink_frame) { - struct vimc_deb_device *vdeb = container_of(ved, struct vimc_deb_device, - ved); + struct vimc_debayer_device *vdebayer = + container_of(ved, struct vimc_debayer_device, ved); + unsigned int rgb[3]; unsigned int i, j; /* If the stream in this node is not active, just return */ - if (!vdeb->src_frame) + if (!vdebayer->src_frame) return ERR_PTR(-EINVAL); - for (i = 0; i < vdeb->sink_fmt.height; i++) - for (j = 0; j < vdeb->sink_fmt.width; j++) { - vimc_deb_calc_rgb_sink(vdeb, sink_frame, i, j, rgb); - vdeb->set_rgb_src(vdeb, i, j, rgb); + for (i = 0; i < vdebayer->sink_fmt.height; i++) + for (j = 0; j < vdebayer->sink_fmt.width; j++) { + vimc_debayer_calc_rgb_sink(vdebayer, sink_frame, i, j, rgb); + vdebayer->set_rgb_src(vdebayer, i, j, rgb); } - return vdeb->src_frame; + return vdebayer->src_frame; } -static int vimc_deb_s_ctrl(struct v4l2_ctrl *ctrl) +static int vimc_debayer_s_ctrl(struct v4l2_ctrl *ctrl) { - struct vimc_deb_device *vdeb = - container_of(ctrl->handler, struct vimc_deb_device, hdl); + struct vimc_debayer_device *vdebayer = + container_of(ctrl->handler, struct vimc_debayer_device, hdl); switch (ctrl->id) { case VIMC_CID_MEAN_WIN_SIZE: - vdeb->mean_win_size = ctrl->val; + vdebayer->mean_win_size = ctrl->val; break; default: return -EINVAL; @@ -527,29 +530,29 @@ static int vimc_deb_s_ctrl(struct v4l2_ctrl *ctrl) return 0; } -static const struct v4l2_ctrl_ops vimc_deb_ctrl_ops = { - .s_ctrl = vimc_deb_s_ctrl, +static const struct v4l2_ctrl_ops vimc_debayer_ctrl_ops = { + .s_ctrl = vimc_debayer_s_ctrl, }; -static void vimc_deb_release(struct vimc_ent_device *ved) +static void vimc_debayer_release(struct vimc_ent_device *ved) { - struct vimc_deb_device *vdeb = - container_of(ved, struct vimc_deb_device, ved); + struct vimc_debayer_device *vdebayer = + container_of(ved, struct vimc_debayer_device, ved); - v4l2_ctrl_handler_free(&vdeb->hdl); - media_entity_cleanup(vdeb->ved.ent); - kfree(vdeb); + v4l2_ctrl_handler_free(&vdebayer->hdl); + media_entity_cleanup(vdebayer->ved.ent); + kfree(vdebayer); } -static const struct v4l2_ctrl_config vimc_deb_ctrl_class = { +static const struct v4l2_ctrl_config vimc_debayer_ctrl_class = { .flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY, .id = VIMC_CID_VIMC_CLASS, .name = "VIMC Controls", .type = V4L2_CTRL_TYPE_CTRL_CLASS, }; -static const struct v4l2_ctrl_config vimc_deb_ctrl_mean_win_size = { - .ops = &vimc_deb_ctrl_ops, +static const struct v4l2_ctrl_config vimc_debayer_ctrl_mean_win_size = { + .ops = &vimc_debayer_ctrl_ops, .id = VIMC_CID_MEAN_WIN_SIZE, .name = "Debayer Mean Window Size", .type = V4L2_CTRL_TYPE_INTEGER, @@ -559,65 +562,65 @@ static const struct v4l2_ctrl_config vimc_deb_ctrl_mean_win_size = { .def = 3, }; -static struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc, - const char *vcfg_name) +static struct vimc_ent_device *vimc_debayer_add(struct vimc_device *vimc, + const char *vcfg_name) { struct v4l2_device *v4l2_dev = &vimc->v4l2_dev; - struct vimc_deb_device *vdeb; + struct vimc_debayer_device *vdebayer; int ret; - /* Allocate the vdeb struct */ - vdeb = kzalloc(sizeof(*vdeb), GFP_KERNEL); - if (!vdeb) + /* Allocate the vdebayer struct */ + vdebayer = kzalloc(sizeof(*vdebayer), GFP_KERNEL); + if (!vdebayer) return ERR_PTR(-ENOMEM); /* Create controls: */ - v4l2_ctrl_handler_init(&vdeb->hdl, 2); - v4l2_ctrl_new_custom(&vdeb->hdl, &vimc_deb_ctrl_class, NULL); - v4l2_ctrl_new_custom(&vdeb->hdl, &vimc_deb_ctrl_mean_win_size, NULL); - vdeb->sd.ctrl_handler = &vdeb->hdl; - if (vdeb->hdl.error) { - ret = vdeb->hdl.error; - goto err_free_vdeb; + v4l2_ctrl_handler_init(&vdebayer->hdl, 2); + v4l2_ctrl_new_custom(&vdebayer->hdl, &vimc_debayer_ctrl_class, NULL); + v4l2_ctrl_new_custom(&vdebayer->hdl, &vimc_debayer_ctrl_mean_win_size, NULL); + vdebayer->sd.ctrl_handler = &vdebayer->hdl; + if (vdebayer->hdl.error) { + ret = vdebayer->hdl.error; + goto err_free_vdebayer; } /* Initialize ved and sd */ - vdeb->pads[0].flags = MEDIA_PAD_FL_SINK; - vdeb->pads[1].flags = MEDIA_PAD_FL_SOURCE; + vdebayer->pads[0].flags = MEDIA_PAD_FL_SINK; + vdebayer->pads[1].flags = MEDIA_PAD_FL_SOURCE; - ret = vimc_ent_sd_register(&vdeb->ved, &vdeb->sd, v4l2_dev, + ret = vimc_ent_sd_register(&vdebayer->ved, &vdebayer->sd, v4l2_dev, vcfg_name, MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV, 2, - vdeb->pads, &vimc_deb_ops); + vdebayer->pads, &vimc_debayer_ops); if (ret) goto err_free_hdl; - vdeb->ved.process_frame = vimc_deb_process_frame; - vdeb->ved.dev = vimc->mdev.dev; - vdeb->mean_win_size = vimc_deb_ctrl_mean_win_size.def; + vdebayer->ved.process_frame = vimc_debayer_process_frame; + vdebayer->ved.dev = vimc->mdev.dev; + vdebayer->mean_win_size = vimc_debayer_ctrl_mean_win_size.def; /* Initialize the frame format */ - vdeb->sink_fmt = sink_fmt_default; + vdebayer->sink_fmt = sink_fmt_default; /* * TODO: Add support for more output formats, we only support * RGB888 for now * NOTE: the src format is always the same as the sink, except * for the code */ - vdeb->src_code = MEDIA_BUS_FMT_RGB888_1X24; - vdeb->set_rgb_src = vimc_deb_process_rgb_frame; + vdebayer->src_code = MEDIA_BUS_FMT_RGB888_1X24; + vdebayer->set_rgb_src = vimc_debayer_process_rgb_frame; - return &vdeb->ved; + return &vdebayer->ved; err_free_hdl: - v4l2_ctrl_handler_free(&vdeb->hdl); -err_free_vdeb: - kfree(vdeb); + v4l2_ctrl_handler_free(&vdebayer->hdl); +err_free_vdebayer: + kfree(vdebayer); return ERR_PTR(ret); } -struct vimc_ent_type vimc_deb_type = { - .add = vimc_deb_add, - .release = vimc_deb_release +struct vimc_ent_type vimc_debayer_type = { + .add = vimc_debayer_add, + .release = vimc_debayer_release }; diff --git a/drivers/media/test-drivers/vimc/vimc-lens.c b/drivers/media/test-drivers/vimc/vimc-lens.c new file mode 100644 index 000000000000..3ce7f4b4d2cc --- /dev/null +++ b/drivers/media/test-drivers/vimc/vimc-lens.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * vimc-lens.c Virtual Media Controller Driver + * Copyright (C) 2022 Google, Inc + * Author: yunkec@google.com (Yunke Cao) + */ + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h> +#include <media/v4l2-subdev.h> + +#include "vimc-common.h" + +#define VIMC_LENS_MAX_FOCUS_POS 1023 +#define VIMC_LENS_MAX_FOCUS_STEP 1 + +struct vimc_lens_device { + struct vimc_ent_device ved; + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + u32 focus_absolute; +}; + +static const struct v4l2_subdev_core_ops vimc_lens_core_ops = { + .log_status = v4l2_ctrl_subdev_log_status, + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_ops vimc_lens_ops = { + .core = &vimc_lens_core_ops +}; + +static int vimc_lens_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vimc_lens_device *vlens = + container_of(ctrl->handler, struct vimc_lens_device, hdl); + if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE) { + vlens->focus_absolute = ctrl->val; + return 0; + } + return -EINVAL; +} + +static const struct v4l2_ctrl_ops vimc_lens_ctrl_ops = { + .s_ctrl = vimc_lens_s_ctrl, +}; + +static struct vimc_ent_device *vimc_lens_add(struct vimc_device *vimc, + const char *vcfg_name) +{ + struct v4l2_device *v4l2_dev = &vimc->v4l2_dev; + struct vimc_lens_device *vlens; + int ret; + + /* Allocate the vlens struct */ + vlens = kzalloc(sizeof(*vlens), GFP_KERNEL); + if (!vlens) + return ERR_PTR(-ENOMEM); + + v4l2_ctrl_handler_init(&vlens->hdl, 1); + + v4l2_ctrl_new_std(&vlens->hdl, &vimc_lens_ctrl_ops, + V4L2_CID_FOCUS_ABSOLUTE, 0, + VIMC_LENS_MAX_FOCUS_POS, VIMC_LENS_MAX_FOCUS_STEP, 0); + vlens->sd.ctrl_handler = &vlens->hdl; + if (vlens->hdl.error) { + ret = vlens->hdl.error; + goto err_free_vlens; + } + vlens->ved.dev = vimc->mdev.dev; + + ret = vimc_ent_sd_register(&vlens->ved, &vlens->sd, v4l2_dev, + vcfg_name, MEDIA_ENT_F_LENS, 0, + NULL, &vimc_lens_ops); + if (ret) + goto err_free_hdl; + + return &vlens->ved; + +err_free_hdl: + v4l2_ctrl_handler_free(&vlens->hdl); +err_free_vlens: + kfree(vlens); + + return ERR_PTR(ret); +} + +static void vimc_lens_release(struct vimc_ent_device *ved) +{ + struct vimc_lens_device *vlens = + container_of(ved, struct vimc_lens_device, ved); + + v4l2_ctrl_handler_free(&vlens->hdl); + media_entity_cleanup(vlens->ved.ent); + kfree(vlens); +} + +struct vimc_ent_type vimc_lens_type = { + .add = vimc_lens_add, + .release = vimc_lens_release +}; diff --git a/drivers/media/test-drivers/vimc/vimc-scaler.c b/drivers/media/test-drivers/vimc/vimc-scaler.c index 820b8f5b502f..b671774e2784 100644 --- a/drivers/media/test-drivers/vimc/vimc-scaler.c +++ b/drivers/media/test-drivers/vimc/vimc-scaler.c @@ -16,14 +16,14 @@ /* Pad identifier */ enum vic_sca_pad { - VIMC_SCA_SINK = 0, - VIMC_SCA_SRC = 1, + VIMC_SCALER_SINK = 0, + VIMC_SCALER_SRC = 1, }; -#define VIMC_SCA_FMT_WIDTH_DEFAULT 640 -#define VIMC_SCA_FMT_HEIGHT_DEFAULT 480 +#define VIMC_SCALER_FMT_WIDTH_DEFAULT 640 +#define VIMC_SCALER_FMT_HEIGHT_DEFAULT 480 -struct vimc_sca_device { +struct vimc_scaler_device { struct vimc_ent_device ved; struct v4l2_subdev sd; struct v4l2_rect crop_rect; @@ -36,16 +36,16 @@ struct vimc_sca_device { }; static const struct v4l2_mbus_framefmt fmt_default = { - .width = VIMC_SCA_FMT_WIDTH_DEFAULT, - .height = VIMC_SCA_FMT_HEIGHT_DEFAULT, + .width = VIMC_SCALER_FMT_WIDTH_DEFAULT, + .height = VIMC_SCALER_FMT_HEIGHT_DEFAULT, .code = MEDIA_BUS_FMT_RGB888_1X24, .field = V4L2_FIELD_NONE, .colorspace = V4L2_COLORSPACE_SRGB, }; static const struct v4l2_rect crop_rect_default = { - .width = VIMC_SCA_FMT_WIDTH_DEFAULT, - .height = VIMC_SCA_FMT_HEIGHT_DEFAULT, + .width = VIMC_SCALER_FMT_WIDTH_DEFAULT, + .height = VIMC_SCALER_FMT_HEIGHT_DEFAULT, .top = 0, .left = 0, }; @@ -58,7 +58,7 @@ static const struct v4l2_rect crop_rect_min = { }; static struct v4l2_rect -vimc_sca_get_crop_bound_sink(const struct v4l2_mbus_framefmt *sink_fmt) +vimc_scaler_get_crop_bound_sink(const struct v4l2_mbus_framefmt *sink_fmt) { /* Get the crop bounds to clamp the crop rectangle correctly */ struct v4l2_rect r = { @@ -70,7 +70,7 @@ vimc_sca_get_crop_bound_sink(const struct v4l2_mbus_framefmt *sink_fmt) return r; } -static int vimc_sca_init_cfg(struct v4l2_subdev *sd, +static int vimc_scaler_init_cfg(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state) { struct v4l2_mbus_framefmt *mf; @@ -82,13 +82,13 @@ static int vimc_sca_init_cfg(struct v4l2_subdev *sd, *mf = fmt_default; } - r = v4l2_subdev_get_try_crop(sd, sd_state, VIMC_SCA_SINK); + r = v4l2_subdev_get_try_crop(sd, sd_state, VIMC_SCALER_SINK); *r = crop_rect_default; return 0; } -static int vimc_sca_enum_mbus_code(struct v4l2_subdev *sd, +static int vimc_scaler_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { @@ -109,7 +109,7 @@ static int vimc_sca_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -static int vimc_sca_enum_frame_size(struct v4l2_subdev *sd, +static int vimc_scaler_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { @@ -133,57 +133,57 @@ static int vimc_sca_enum_frame_size(struct v4l2_subdev *sd, } static struct v4l2_mbus_framefmt * -vimc_sca_pad_format(struct vimc_sca_device *vsca, +vimc_scaler_pad_format(struct vimc_scaler_device *vscaler, struct v4l2_subdev_state *sd_state, u32 pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&vsca->sd, sd_state, pad); + return v4l2_subdev_get_try_format(&vscaler->sd, sd_state, pad); else - return &vsca->fmt[pad]; + return &vscaler->fmt[pad]; } static struct v4l2_rect * -vimc_sca_pad_crop(struct vimc_sca_device *vsca, +vimc_scaler_pad_crop(struct vimc_scaler_device *vscaler, struct v4l2_subdev_state *sd_state, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(&vsca->sd, sd_state, - VIMC_SCA_SINK); + return v4l2_subdev_get_try_crop(&vscaler->sd, sd_state, + VIMC_SCALER_SINK); else - return &vsca->crop_rect; + return &vscaler->crop_rect; } -static int vimc_sca_get_fmt(struct v4l2_subdev *sd, +static int vimc_scaler_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { - struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd); + struct vimc_scaler_device *vscaler = v4l2_get_subdevdata(sd); - format->format = *vimc_sca_pad_format(vsca, sd_state, format->pad, + format->format = *vimc_scaler_pad_format(vscaler, sd_state, format->pad, format->which); return 0; } -static int vimc_sca_set_fmt(struct v4l2_subdev *sd, +static int vimc_scaler_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { - struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd); + struct vimc_scaler_device *vscaler = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *fmt; /* Do not change the active format while stream is on */ - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && vsca->src_frame) + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && vscaler->src_frame) return -EBUSY; - fmt = vimc_sca_pad_format(vsca, sd_state, format->pad, format->which); + fmt = vimc_scaler_pad_format(vscaler, sd_state, format->pad, format->which); /* * The media bus code and colorspace can only be changed on the sink * pad, the source pad only follows. */ - if (format->pad == VIMC_SCA_SINK) { + if (format->pad == VIMC_SCALER_SINK) { const struct vimc_pix_map *vpix; /* Only accept code in the pix map table in non bayer format. */ @@ -211,17 +211,17 @@ static int vimc_sca_set_fmt(struct v4l2_subdev *sd, * Propagate the sink pad format to the crop rectangle and the source * pad. */ - if (format->pad == VIMC_SCA_SINK) { + if (format->pad == VIMC_SCALER_SINK) { struct v4l2_mbus_framefmt *src_fmt; struct v4l2_rect *crop; - crop = vimc_sca_pad_crop(vsca, sd_state, format->which); + crop = vimc_scaler_pad_crop(vscaler, sd_state, format->which); crop->width = fmt->width; crop->height = fmt->height; crop->top = 0; crop->left = 0; - src_fmt = vimc_sca_pad_format(vsca, sd_state, VIMC_SCA_SRC, + src_fmt = vimc_scaler_pad_format(vscaler, sd_state, VIMC_SCALER_SRC, format->which); *src_fmt = *fmt; } @@ -231,11 +231,11 @@ static int vimc_sca_set_fmt(struct v4l2_subdev *sd, return 0; } -static int vimc_sca_get_selection(struct v4l2_subdev *sd, +static int vimc_scaler_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { - struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd); + struct vimc_scaler_device *vscaler = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *sink_fmt; if (VIMC_IS_SRC(sel->pad)) @@ -243,12 +243,12 @@ static int vimc_sca_get_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_CROP: - sel->r = *vimc_sca_pad_crop(vsca, sd_state, sel->which); + sel->r = *vimc_scaler_pad_crop(vscaler, sd_state, sel->which); break; case V4L2_SEL_TGT_CROP_BOUNDS: - sink_fmt = vimc_sca_pad_format(vsca, sd_state, VIMC_SCA_SINK, + sink_fmt = vimc_scaler_pad_format(vscaler, sd_state, VIMC_SCALER_SINK, sel->which); - sel->r = vimc_sca_get_crop_bound_sink(sink_fmt); + sel->r = vimc_scaler_get_crop_bound_sink(sink_fmt); break; default: return -EINVAL; @@ -257,22 +257,22 @@ static int vimc_sca_get_selection(struct v4l2_subdev *sd, return 0; } -static void vimc_sca_adjust_sink_crop(struct v4l2_rect *r, +static void vimc_scaler_adjust_sink_crop(struct v4l2_rect *r, const struct v4l2_mbus_framefmt *sink_fmt) { const struct v4l2_rect sink_rect = - vimc_sca_get_crop_bound_sink(sink_fmt); + vimc_scaler_get_crop_bound_sink(sink_fmt); /* Disallow rectangles smaller than the minimal one. */ v4l2_rect_set_min_size(r, &crop_rect_min); v4l2_rect_map_inside(r, &sink_rect); } -static int vimc_sca_set_selection(struct v4l2_subdev *sd, +static int vimc_scaler_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { - struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd); + struct vimc_scaler_device *vscaler = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *sink_fmt; struct v4l2_rect *crop_rect; @@ -280,165 +280,165 @@ static int vimc_sca_set_selection(struct v4l2_subdev *sd, if (VIMC_IS_SRC(sel->pad) || sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; - if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE && vsca->src_frame) + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE && vscaler->src_frame) return -EBUSY; - crop_rect = vimc_sca_pad_crop(vsca, sd_state, sel->which); - sink_fmt = vimc_sca_pad_format(vsca, sd_state, VIMC_SCA_SINK, + crop_rect = vimc_scaler_pad_crop(vscaler, sd_state, sel->which); + sink_fmt = vimc_scaler_pad_format(vscaler, sd_state, VIMC_SCALER_SINK, sel->which); - vimc_sca_adjust_sink_crop(&sel->r, sink_fmt); + vimc_scaler_adjust_sink_crop(&sel->r, sink_fmt); *crop_rect = sel->r; return 0; } -static const struct v4l2_subdev_pad_ops vimc_sca_pad_ops = { - .init_cfg = vimc_sca_init_cfg, - .enum_mbus_code = vimc_sca_enum_mbus_code, - .enum_frame_size = vimc_sca_enum_frame_size, - .get_fmt = vimc_sca_get_fmt, - .set_fmt = vimc_sca_set_fmt, - .get_selection = vimc_sca_get_selection, - .set_selection = vimc_sca_set_selection, +static const struct v4l2_subdev_pad_ops vimc_scaler_pad_ops = { + .init_cfg = vimc_scaler_init_cfg, + .enum_mbus_code = vimc_scaler_enum_mbus_code, + .enum_frame_size = vimc_scaler_enum_frame_size, + .get_fmt = vimc_scaler_get_fmt, + .set_fmt = vimc_scaler_set_fmt, + .get_selection = vimc_scaler_get_selection, + .set_selection = vimc_scaler_set_selection, }; -static int vimc_sca_s_stream(struct v4l2_subdev *sd, int enable) +static int vimc_scaler_s_stream(struct v4l2_subdev *sd, int enable) { - struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd); + struct vimc_scaler_device *vscaler = v4l2_get_subdevdata(sd); if (enable) { const struct vimc_pix_map *vpix; unsigned int frame_size; - if (vsca->src_frame) + if (vscaler->src_frame) return 0; /* Save the bytes per pixel of the sink */ - vpix = vimc_pix_map_by_code(vsca->fmt[VIMC_SCA_SINK].code); - vsca->bpp = vpix->bpp; + vpix = vimc_pix_map_by_code(vscaler->fmt[VIMC_SCALER_SINK].code); + vscaler->bpp = vpix->bpp; /* Calculate the frame size of the source pad */ - frame_size = vsca->fmt[VIMC_SCA_SRC].width - * vsca->fmt[VIMC_SCA_SRC].height * vsca->bpp; + frame_size = vscaler->fmt[VIMC_SCALER_SRC].width + * vscaler->fmt[VIMC_SCALER_SRC].height * vscaler->bpp; /* Allocate the frame buffer. Use vmalloc to be able to * allocate a large amount of memory */ - vsca->src_frame = vmalloc(frame_size); - if (!vsca->src_frame) + vscaler->src_frame = vmalloc(frame_size); + if (!vscaler->src_frame) return -ENOMEM; } else { - if (!vsca->src_frame) + if (!vscaler->src_frame) return 0; - vfree(vsca->src_frame); - vsca->src_frame = NULL; + vfree(vscaler->src_frame); + vscaler->src_frame = NULL; } return 0; } -static const struct v4l2_subdev_video_ops vimc_sca_video_ops = { - .s_stream = vimc_sca_s_stream, +static const struct v4l2_subdev_video_ops vimc_scaler_video_ops = { + .s_stream = vimc_scaler_s_stream, }; -static const struct v4l2_subdev_ops vimc_sca_ops = { - .pad = &vimc_sca_pad_ops, - .video = &vimc_sca_video_ops, +static const struct v4l2_subdev_ops vimc_scaler_ops = { + .pad = &vimc_scaler_pad_ops, + .video = &vimc_scaler_video_ops, }; -static void vimc_sca_fill_src_frame(const struct vimc_sca_device *const vsca, +static void vimc_scaler_fill_src_frame(const struct vimc_scaler_device *const vscaler, const u8 *const sink_frame) { - const struct v4l2_mbus_framefmt *src_fmt = &vsca->fmt[VIMC_SCA_SRC]; - const struct v4l2_rect *r = &vsca->crop_rect; - unsigned int snk_width = vsca->fmt[VIMC_SCA_SINK].width; + const struct v4l2_mbus_framefmt *src_fmt = &vscaler->fmt[VIMC_SCALER_SRC]; + const struct v4l2_rect *r = &vscaler->crop_rect; + unsigned int snk_width = vscaler->fmt[VIMC_SCALER_SINK].width; unsigned int src_x, src_y; - u8 *walker = vsca->src_frame; + u8 *walker = vscaler->src_frame; /* Set each pixel at the src_frame to its sink_frame equivalent */ for (src_y = 0; src_y < src_fmt->height; src_y++) { unsigned int snk_y, y_offset; snk_y = (src_y * r->height) / src_fmt->height + r->top; - y_offset = snk_y * snk_width * vsca->bpp; + y_offset = snk_y * snk_width * vscaler->bpp; for (src_x = 0; src_x < src_fmt->width; src_x++) { unsigned int snk_x, x_offset, index; snk_x = (src_x * r->width) / src_fmt->width + r->left; - x_offset = snk_x * vsca->bpp; + x_offset = snk_x * vscaler->bpp; index = y_offset + x_offset; - memcpy(walker, &sink_frame[index], vsca->bpp); - walker += vsca->bpp; + memcpy(walker, &sink_frame[index], vscaler->bpp); + walker += vscaler->bpp; } } } -static void *vimc_sca_process_frame(struct vimc_ent_device *ved, +static void *vimc_scaler_process_frame(struct vimc_ent_device *ved, const void *sink_frame) { - struct vimc_sca_device *vsca = container_of(ved, struct vimc_sca_device, + struct vimc_scaler_device *vscaler = container_of(ved, struct vimc_scaler_device, ved); /* If the stream in this node is not active, just return */ - if (!vsca->src_frame) + if (!vscaler->src_frame) return ERR_PTR(-EINVAL); - vimc_sca_fill_src_frame(vsca, sink_frame); + vimc_scaler_fill_src_frame(vscaler, sink_frame); - return vsca->src_frame; + return vscaler->src_frame; }; -static void vimc_sca_release(struct vimc_ent_device *ved) +static void vimc_scaler_release(struct vimc_ent_device *ved) { - struct vimc_sca_device *vsca = - container_of(ved, struct vimc_sca_device, ved); + struct vimc_scaler_device *vscaler = + container_of(ved, struct vimc_scaler_device, ved); - media_entity_cleanup(vsca->ved.ent); - kfree(vsca); + media_entity_cleanup(vscaler->ved.ent); + kfree(vscaler); } -static struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc, +static struct vimc_ent_device *vimc_scaler_add(struct vimc_device *vimc, const char *vcfg_name) { struct v4l2_device *v4l2_dev = &vimc->v4l2_dev; - struct vimc_sca_device *vsca; + struct vimc_scaler_device *vscaler; int ret; - /* Allocate the vsca struct */ - vsca = kzalloc(sizeof(*vsca), GFP_KERNEL); - if (!vsca) + /* Allocate the vscaler struct */ + vscaler = kzalloc(sizeof(*vscaler), GFP_KERNEL); + if (!vscaler) return ERR_PTR(-ENOMEM); /* Initialize ved and sd */ - vsca->pads[VIMC_SCA_SINK].flags = MEDIA_PAD_FL_SINK; - vsca->pads[VIMC_SCA_SRC].flags = MEDIA_PAD_FL_SOURCE; + vscaler->pads[VIMC_SCALER_SINK].flags = MEDIA_PAD_FL_SINK; + vscaler->pads[VIMC_SCALER_SRC].flags = MEDIA_PAD_FL_SOURCE; - ret = vimc_ent_sd_register(&vsca->ved, &vsca->sd, v4l2_dev, + ret = vimc_ent_sd_register(&vscaler->ved, &vscaler->sd, v4l2_dev, vcfg_name, MEDIA_ENT_F_PROC_VIDEO_SCALER, 2, - vsca->pads, &vimc_sca_ops); + vscaler->pads, &vimc_scaler_ops); if (ret) { - kfree(vsca); + kfree(vscaler); return ERR_PTR(ret); } - vsca->ved.process_frame = vimc_sca_process_frame; - vsca->ved.dev = vimc->mdev.dev; + vscaler->ved.process_frame = vimc_scaler_process_frame; + vscaler->ved.dev = vimc->mdev.dev; /* Initialize the frame format */ - vsca->fmt[VIMC_SCA_SINK] = fmt_default; - vsca->fmt[VIMC_SCA_SRC] = fmt_default; + vscaler->fmt[VIMC_SCALER_SINK] = fmt_default; + vscaler->fmt[VIMC_SCALER_SRC] = fmt_default; /* Initialize the crop selection */ - vsca->crop_rect = crop_rect_default; + vscaler->crop_rect = crop_rect_default; - return &vsca->ved; + return &vscaler->ved; } -struct vimc_ent_type vimc_sca_type = { - .add = vimc_sca_add, - .release = vimc_sca_release +struct vimc_ent_type vimc_scaler_type = { + .add = vimc_scaler_add, + .release = vimc_scaler_release }; diff --git a/drivers/media/test-drivers/vimc/vimc-sensor.c b/drivers/media/test-drivers/vimc/vimc-sensor.c index 74ab79cadb5d..41a3dce2d714 100644 --- a/drivers/media/test-drivers/vimc/vimc-sensor.c +++ b/drivers/media/test-drivers/vimc/vimc-sensor.c @@ -14,18 +14,18 @@ #include "vimc-common.h" -enum vimc_sen_osd_mode { - VIMC_SEN_OSD_SHOW_ALL = 0, - VIMC_SEN_OSD_SHOW_COUNTERS = 1, - VIMC_SEN_OSD_SHOW_NONE = 2 +enum vimc_sensor_osd_mode { + VIMC_SENSOR_OSD_SHOW_ALL = 0, + VIMC_SENSOR_OSD_SHOW_COUNTERS = 1, + VIMC_SENSOR_OSD_SHOW_NONE = 2 }; -struct vimc_sen_device { +struct vimc_sensor_device { struct vimc_ent_device ved; struct v4l2_subdev sd; struct tpg_data tpg; u8 *frame; - enum vimc_sen_osd_mode osd_value; + enum vimc_sensor_osd_mode osd_value; u64 start_stream_ts; /* The active format */ struct v4l2_mbus_framefmt mbus_format; @@ -41,8 +41,8 @@ static const struct v4l2_mbus_framefmt fmt_default = { .colorspace = V4L2_COLORSPACE_SRGB, }; -static int vimc_sen_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int vimc_sensor_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { unsigned int i; @@ -56,9 +56,9 @@ static int vimc_sen_init_cfg(struct v4l2_subdev *sd, return 0; } -static int vimc_sen_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_mbus_code_enum *code) +static int vimc_sensor_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) { u32 mbus_code = vimc_mbus_code_by_index(code->index); @@ -70,9 +70,9 @@ static int vimc_sen_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -static int vimc_sen_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_size_enum *fse) +static int vimc_sensor_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) { const struct vimc_pix_map *vpix; @@ -92,39 +92,39 @@ static int vimc_sen_enum_frame_size(struct v4l2_subdev *sd, return 0; } -static int vimc_sen_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) +static int vimc_sensor_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) { - struct vimc_sen_device *vsen = - container_of(sd, struct vimc_sen_device, sd); + struct vimc_sensor_device *vsensor = + container_of(sd, struct vimc_sensor_device, sd); fmt->format = fmt->which == V4L2_SUBDEV_FORMAT_TRY ? *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) : - vsen->mbus_format; + vsensor->mbus_format; return 0; } -static void vimc_sen_tpg_s_format(struct vimc_sen_device *vsen) +static void vimc_sensor_tpg_s_format(struct vimc_sensor_device *vsensor) { const struct vimc_pix_map *vpix = - vimc_pix_map_by_code(vsen->mbus_format.code); + vimc_pix_map_by_code(vsensor->mbus_format.code); - tpg_reset_source(&vsen->tpg, vsen->mbus_format.width, - vsen->mbus_format.height, vsen->mbus_format.field); - tpg_s_bytesperline(&vsen->tpg, 0, vsen->mbus_format.width * vpix->bpp); - tpg_s_buf_height(&vsen->tpg, vsen->mbus_format.height); - tpg_s_fourcc(&vsen->tpg, vpix->pixelformat); + tpg_reset_source(&vsensor->tpg, vsensor->mbus_format.width, + vsensor->mbus_format.height, vsensor->mbus_format.field); + tpg_s_bytesperline(&vsensor->tpg, 0, vsensor->mbus_format.width * vpix->bpp); + tpg_s_buf_height(&vsensor->tpg, vsensor->mbus_format.height); + tpg_s_fourcc(&vsensor->tpg, vpix->pixelformat); /* TODO: add support for V4L2_FIELD_ALTERNATE */ - tpg_s_field(&vsen->tpg, vsen->mbus_format.field, false); - tpg_s_colorspace(&vsen->tpg, vsen->mbus_format.colorspace); - tpg_s_ycbcr_enc(&vsen->tpg, vsen->mbus_format.ycbcr_enc); - tpg_s_quantization(&vsen->tpg, vsen->mbus_format.quantization); - tpg_s_xfer_func(&vsen->tpg, vsen->mbus_format.xfer_func); + tpg_s_field(&vsensor->tpg, vsensor->mbus_format.field, false); + tpg_s_colorspace(&vsensor->tpg, vsensor->mbus_format.colorspace); + tpg_s_ycbcr_enc(&vsensor->tpg, vsensor->mbus_format.ycbcr_enc); + tpg_s_quantization(&vsensor->tpg, vsensor->mbus_format.quantization); + tpg_s_xfer_func(&vsensor->tpg, vsensor->mbus_format.xfer_func); } -static void vimc_sen_adjust_fmt(struct v4l2_mbus_framefmt *fmt) +static void vimc_sensor_adjust_fmt(struct v4l2_mbus_framefmt *fmt) { const struct vimc_pix_map *vpix; @@ -145,29 +145,29 @@ static void vimc_sen_adjust_fmt(struct v4l2_mbus_framefmt *fmt) vimc_colorimetry_clamp(fmt); } -static int vimc_sen_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) +static int vimc_sensor_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) { - struct vimc_sen_device *vsen = v4l2_get_subdevdata(sd); + struct vimc_sensor_device *vsensor = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *mf; if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { /* Do not change the format while stream is on */ - if (vsen->frame) + if (vsensor->frame) return -EBUSY; - mf = &vsen->mbus_format; + mf = &vsensor->mbus_format; } else { mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); } /* Set the new format */ - vimc_sen_adjust_fmt(&fmt->format); + vimc_sensor_adjust_fmt(&fmt->format); - dev_dbg(vsen->ved.dev, "%s: format update: " + dev_dbg(vsensor->ved.dev, "%s: format update: " "old:%dx%d (0x%x, %d, %d, %d, %d) " - "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vsen->sd.name, + "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vsensor->sd.name, /* old */ mf->width, mf->height, mf->code, mf->colorspace, mf->quantization, @@ -182,146 +182,147 @@ static int vimc_sen_set_fmt(struct v4l2_subdev *sd, return 0; } -static const struct v4l2_subdev_pad_ops vimc_sen_pad_ops = { - .init_cfg = vimc_sen_init_cfg, - .enum_mbus_code = vimc_sen_enum_mbus_code, - .enum_frame_size = vimc_sen_enum_frame_size, - .get_fmt = vimc_sen_get_fmt, - .set_fmt = vimc_sen_set_fmt, +static const struct v4l2_subdev_pad_ops vimc_sensor_pad_ops = { + .init_cfg = vimc_sensor_init_cfg, + .enum_mbus_code = vimc_sensor_enum_mbus_code, + .enum_frame_size = vimc_sensor_enum_frame_size, + .get_fmt = vimc_sensor_get_fmt, + .set_fmt = vimc_sensor_set_fmt, }; -static void *vimc_sen_process_frame(struct vimc_ent_device *ved, - const void *sink_frame) +static void *vimc_sensor_process_frame(struct vimc_ent_device *ved, + const void *sink_frame) { - struct vimc_sen_device *vsen = container_of(ved, struct vimc_sen_device, - ved); + struct vimc_sensor_device *vsensor = + container_of(ved, struct vimc_sensor_device, ved); + const unsigned int line_height = 16; u8 *basep[TPG_MAX_PLANES][2]; unsigned int line = 1; char str[100]; - tpg_fill_plane_buffer(&vsen->tpg, 0, 0, vsen->frame); - tpg_calc_text_basep(&vsen->tpg, basep, 0, vsen->frame); - switch (vsen->osd_value) { - case VIMC_SEN_OSD_SHOW_ALL: { - const char *order = tpg_g_color_order(&vsen->tpg); + tpg_fill_plane_buffer(&vsensor->tpg, 0, 0, vsensor->frame); + tpg_calc_text_basep(&vsensor->tpg, basep, 0, vsensor->frame); + switch (vsensor->osd_value) { + case VIMC_SENSOR_OSD_SHOW_ALL: { + const char *order = tpg_g_color_order(&vsensor->tpg); - tpg_gen_text(&vsen->tpg, basep, line++ * line_height, + tpg_gen_text(&vsensor->tpg, basep, line++ * line_height, 16, order); snprintf(str, sizeof(str), "brightness %3d, contrast %3d, saturation %3d, hue %d ", - vsen->tpg.brightness, - vsen->tpg.contrast, - vsen->tpg.saturation, - vsen->tpg.hue); - tpg_gen_text(&vsen->tpg, basep, line++ * line_height, 16, str); + vsensor->tpg.brightness, + vsensor->tpg.contrast, + vsensor->tpg.saturation, + vsensor->tpg.hue); + tpg_gen_text(&vsensor->tpg, basep, line++ * line_height, 16, str); snprintf(str, sizeof(str), "sensor size: %dx%d", - vsen->mbus_format.width, - vsen->mbus_format.height); - tpg_gen_text(&vsen->tpg, basep, line++ * line_height, 16, str); + vsensor->mbus_format.width, + vsensor->mbus_format.height); + tpg_gen_text(&vsensor->tpg, basep, line++ * line_height, 16, str); fallthrough; } - case VIMC_SEN_OSD_SHOW_COUNTERS: { + case VIMC_SENSOR_OSD_SHOW_COUNTERS: { unsigned int ms; - ms = div_u64(ktime_get_ns() - vsen->start_stream_ts, 1000000); + ms = div_u64(ktime_get_ns() - vsensor->start_stream_ts, 1000000); snprintf(str, sizeof(str), "%02d:%02d:%02d:%03d", (ms / (60 * 60 * 1000)) % 24, (ms / (60 * 1000)) % 60, (ms / 1000) % 60, ms % 1000); - tpg_gen_text(&vsen->tpg, basep, line++ * line_height, 16, str); + tpg_gen_text(&vsensor->tpg, basep, line++ * line_height, 16, str); break; } - case VIMC_SEN_OSD_SHOW_NONE: + case VIMC_SENSOR_OSD_SHOW_NONE: default: break; } - return vsen->frame; + return vsensor->frame; } -static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable) +static int vimc_sensor_s_stream(struct v4l2_subdev *sd, int enable) { - struct vimc_sen_device *vsen = - container_of(sd, struct vimc_sen_device, sd); + struct vimc_sensor_device *vsensor = + container_of(sd, struct vimc_sensor_device, sd); if (enable) { const struct vimc_pix_map *vpix; unsigned int frame_size; - vsen->start_stream_ts = ktime_get_ns(); + vsensor->start_stream_ts = ktime_get_ns(); /* Calculate the frame size */ - vpix = vimc_pix_map_by_code(vsen->mbus_format.code); - frame_size = vsen->mbus_format.width * vpix->bpp * - vsen->mbus_format.height; + vpix = vimc_pix_map_by_code(vsensor->mbus_format.code); + frame_size = vsensor->mbus_format.width * vpix->bpp * + vsensor->mbus_format.height; /* * Allocate the frame buffer. Use vmalloc to be able to * allocate a large amount of memory */ - vsen->frame = vmalloc(frame_size); - if (!vsen->frame) + vsensor->frame = vmalloc(frame_size); + if (!vsensor->frame) return -ENOMEM; /* configure the test pattern generator */ - vimc_sen_tpg_s_format(vsen); + vimc_sensor_tpg_s_format(vsensor); } else { - vfree(vsen->frame); - vsen->frame = NULL; + vfree(vsensor->frame); + vsensor->frame = NULL; } return 0; } -static const struct v4l2_subdev_core_ops vimc_sen_core_ops = { +static const struct v4l2_subdev_core_ops vimc_sensor_core_ops = { .log_status = v4l2_ctrl_subdev_log_status, .subscribe_event = v4l2_ctrl_subdev_subscribe_event, .unsubscribe_event = v4l2_event_subdev_unsubscribe, }; -static const struct v4l2_subdev_video_ops vimc_sen_video_ops = { - .s_stream = vimc_sen_s_stream, +static const struct v4l2_subdev_video_ops vimc_sensor_video_ops = { + .s_stream = vimc_sensor_s_stream, }; -static const struct v4l2_subdev_ops vimc_sen_ops = { - .core = &vimc_sen_core_ops, - .pad = &vimc_sen_pad_ops, - .video = &vimc_sen_video_ops, +static const struct v4l2_subdev_ops vimc_sensor_ops = { + .core = &vimc_sensor_core_ops, + .pad = &vimc_sensor_pad_ops, + .video = &vimc_sensor_video_ops, }; -static int vimc_sen_s_ctrl(struct v4l2_ctrl *ctrl) +static int vimc_sensor_s_ctrl(struct v4l2_ctrl *ctrl) { - struct vimc_sen_device *vsen = - container_of(ctrl->handler, struct vimc_sen_device, hdl); + struct vimc_sensor_device *vsensor = + container_of(ctrl->handler, struct vimc_sensor_device, hdl); switch (ctrl->id) { case VIMC_CID_TEST_PATTERN: - tpg_s_pattern(&vsen->tpg, ctrl->val); + tpg_s_pattern(&vsensor->tpg, ctrl->val); break; case V4L2_CID_HFLIP: - tpg_s_hflip(&vsen->tpg, ctrl->val); + tpg_s_hflip(&vsensor->tpg, ctrl->val); break; case V4L2_CID_VFLIP: - tpg_s_vflip(&vsen->tpg, ctrl->val); + tpg_s_vflip(&vsensor->tpg, ctrl->val); break; case V4L2_CID_BRIGHTNESS: - tpg_s_brightness(&vsen->tpg, ctrl->val); + tpg_s_brightness(&vsensor->tpg, ctrl->val); break; case V4L2_CID_CONTRAST: - tpg_s_contrast(&vsen->tpg, ctrl->val); + tpg_s_contrast(&vsensor->tpg, ctrl->val); break; case V4L2_CID_HUE: - tpg_s_hue(&vsen->tpg, ctrl->val); + tpg_s_hue(&vsensor->tpg, ctrl->val); break; case V4L2_CID_SATURATION: - tpg_s_saturation(&vsen->tpg, ctrl->val); + tpg_s_saturation(&vsensor->tpg, ctrl->val); break; case VIMC_CID_OSD_TEXT_MODE: - vsen->osd_value = ctrl->val; + vsensor->osd_value = ctrl->val; break; default: return -EINVAL; @@ -329,31 +330,31 @@ static int vimc_sen_s_ctrl(struct v4l2_ctrl *ctrl) return 0; } -static const struct v4l2_ctrl_ops vimc_sen_ctrl_ops = { - .s_ctrl = vimc_sen_s_ctrl, +static const struct v4l2_ctrl_ops vimc_sensor_ctrl_ops = { + .s_ctrl = vimc_sensor_s_ctrl, }; -static void vimc_sen_release(struct vimc_ent_device *ved) +static void vimc_sensor_release(struct vimc_ent_device *ved) { - struct vimc_sen_device *vsen = - container_of(ved, struct vimc_sen_device, ved); + struct vimc_sensor_device *vsensor = + container_of(ved, struct vimc_sensor_device, ved); - v4l2_ctrl_handler_free(&vsen->hdl); - tpg_free(&vsen->tpg); - media_entity_cleanup(vsen->ved.ent); - kfree(vsen); + v4l2_ctrl_handler_free(&vsensor->hdl); + tpg_free(&vsensor->tpg); + media_entity_cleanup(vsensor->ved.ent); + kfree(vsensor); } /* Image Processing Controls */ -static const struct v4l2_ctrl_config vimc_sen_ctrl_class = { +static const struct v4l2_ctrl_config vimc_sensor_ctrl_class = { .flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY, .id = VIMC_CID_VIMC_CLASS, .name = "VIMC Controls", .type = V4L2_CTRL_TYPE_CTRL_CLASS, }; -static const struct v4l2_ctrl_config vimc_sen_ctrl_test_pattern = { - .ops = &vimc_sen_ctrl_ops, +static const struct v4l2_ctrl_config vimc_sensor_ctrl_test_pattern = { + .ops = &vimc_sensor_ctrl_ops, .id = VIMC_CID_TEST_PATTERN, .name = "Test Pattern", .type = V4L2_CTRL_TYPE_MENU, @@ -368,8 +369,8 @@ static const char * const vimc_ctrl_osd_mode_strings[] = { NULL, }; -static const struct v4l2_ctrl_config vimc_sen_ctrl_osd_mode = { - .ops = &vimc_sen_ctrl_ops, +static const struct v4l2_ctrl_config vimc_sensor_ctrl_osd_mode = { + .ops = &vimc_sensor_ctrl_ops, .id = VIMC_CID_OSD_TEXT_MODE, .name = "Show Information", .type = V4L2_CTRL_TYPE_MENU, @@ -377,76 +378,76 @@ static const struct v4l2_ctrl_config vimc_sen_ctrl_osd_mode = { .qmenu = vimc_ctrl_osd_mode_strings, }; -static struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc, - const char *vcfg_name) +static struct vimc_ent_device *vimc_sensor_add(struct vimc_device *vimc, + const char *vcfg_name) { struct v4l2_device *v4l2_dev = &vimc->v4l2_dev; - struct vimc_sen_device *vsen; + struct vimc_sensor_device *vsensor; int ret; - /* Allocate the vsen struct */ - vsen = kzalloc(sizeof(*vsen), GFP_KERNEL); - if (!vsen) + /* Allocate the vsensor struct */ + vsensor = kzalloc(sizeof(*vsensor), GFP_KERNEL); + if (!vsensor) return ERR_PTR(-ENOMEM); - v4l2_ctrl_handler_init(&vsen->hdl, 4); + v4l2_ctrl_handler_init(&vsensor->hdl, 4); - v4l2_ctrl_new_custom(&vsen->hdl, &vimc_sen_ctrl_class, NULL); - v4l2_ctrl_new_custom(&vsen->hdl, &vimc_sen_ctrl_test_pattern, NULL); - v4l2_ctrl_new_custom(&vsen->hdl, &vimc_sen_ctrl_osd_mode, NULL); - v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops, + v4l2_ctrl_new_custom(&vsensor->hdl, &vimc_sensor_ctrl_class, NULL); + v4l2_ctrl_new_custom(&vsensor->hdl, &vimc_sensor_ctrl_test_pattern, NULL); + v4l2_ctrl_new_custom(&vsensor->hdl, &vimc_sensor_ctrl_osd_mode, NULL); + v4l2_ctrl_new_std(&vsensor->hdl, &vimc_sensor_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops, + v4l2_ctrl_new_std(&vsensor->hdl, &vimc_sensor_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops, + v4l2_ctrl_new_std(&vsensor->hdl, &vimc_sensor_ctrl_ops, V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); - v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops, + v4l2_ctrl_new_std(&vsensor->hdl, &vimc_sensor_ctrl_ops, V4L2_CID_CONTRAST, 0, 255, 1, 128); - v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops, + v4l2_ctrl_new_std(&vsensor->hdl, &vimc_sensor_ctrl_ops, V4L2_CID_HUE, -128, 127, 1, 0); - v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops, + v4l2_ctrl_new_std(&vsensor->hdl, &vimc_sensor_ctrl_ops, V4L2_CID_SATURATION, 0, 255, 1, 128); - vsen->sd.ctrl_handler = &vsen->hdl; - if (vsen->hdl.error) { - ret = vsen->hdl.error; - goto err_free_vsen; + vsensor->sd.ctrl_handler = &vsensor->hdl; + if (vsensor->hdl.error) { + ret = vsensor->hdl.error; + goto err_free_vsensor; } /* Initialize the test pattern generator */ - tpg_init(&vsen->tpg, vsen->mbus_format.width, - vsen->mbus_format.height); - ret = tpg_alloc(&vsen->tpg, VIMC_FRAME_MAX_WIDTH); + tpg_init(&vsensor->tpg, vsensor->mbus_format.width, + vsensor->mbus_format.height); + ret = tpg_alloc(&vsensor->tpg, VIMC_FRAME_MAX_WIDTH); if (ret) goto err_free_hdl; /* Initialize ved and sd */ - vsen->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = vimc_ent_sd_register(&vsen->ved, &vsen->sd, v4l2_dev, + vsensor->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = vimc_ent_sd_register(&vsensor->ved, &vsensor->sd, v4l2_dev, vcfg_name, - MEDIA_ENT_F_CAM_SENSOR, 1, &vsen->pad, - &vimc_sen_ops); + MEDIA_ENT_F_CAM_SENSOR, 1, &vsensor->pad, + &vimc_sensor_ops); if (ret) goto err_free_tpg; - vsen->ved.process_frame = vimc_sen_process_frame; - vsen->ved.dev = vimc->mdev.dev; + vsensor->ved.process_frame = vimc_sensor_process_frame; + vsensor->ved.dev = vimc->mdev.dev; /* Initialize the frame format */ - vsen->mbus_format = fmt_default; + vsensor->mbus_format = fmt_default; - return &vsen->ved; + return &vsensor->ved; err_free_tpg: - tpg_free(&vsen->tpg); + tpg_free(&vsensor->tpg); err_free_hdl: - v4l2_ctrl_handler_free(&vsen->hdl); -err_free_vsen: - kfree(vsen); + v4l2_ctrl_handler_free(&vsensor->hdl); +err_free_vsensor: + kfree(vsensor); return ERR_PTR(ret); } -struct vimc_ent_type vimc_sen_type = { - .add = vimc_sen_add, - .release = vimc_sen_release +struct vimc_ent_type vimc_sensor_type = { + .add = vimc_sensor_add, + .release = vimc_sensor_release }; diff --git a/drivers/media/test-drivers/vimc/vimc-streamer.c b/drivers/media/test-drivers/vimc/vimc-streamer.c index 65feb3c596db..807551a5143b 100644 --- a/drivers/media/test-drivers/vimc/vimc-streamer.c +++ b/drivers/media/test-drivers/vimc/vimc-streamer.c @@ -30,7 +30,7 @@ static struct media_entity *vimc_get_source_entity(struct media_entity *ent) for (i = 0; i < ent->num_pads; i++) { if (ent->pads[i].flags & MEDIA_PAD_FL_SOURCE) continue; - pad = media_entity_remote_pad(&ent->pads[i]); + pad = media_pad_remote_pad_first(&ent->pads[i]); return pad ? pad->entity : NULL; } return NULL; diff --git a/drivers/media/test-drivers/vivid/vivid-ctrls.c b/drivers/media/test-drivers/vivid/vivid-ctrls.c index e7516dc1227b..a78d676575bc 100644 --- a/drivers/media/test-drivers/vivid/vivid-ctrls.c +++ b/drivers/media/test-drivers/vivid/vivid-ctrls.c @@ -34,6 +34,7 @@ #define VIVID_CID_U8_4D_ARRAY (VIVID_CID_CUSTOM_BASE + 10) #define VIVID_CID_AREA (VIVID_CID_CUSTOM_BASE + 11) #define VIVID_CID_RO_INTEGER (VIVID_CID_CUSTOM_BASE + 12) +#define VIVID_CID_U32_DYN_ARRAY (VIVID_CID_CUSTOM_BASE + 13) #define VIVID_CID_VIVID_BASE (0x00f00000 | 0xf000) #define VIVID_CID_VIVID_CLASS (0x00f00000 | 1) @@ -46,6 +47,7 @@ #define VIVID_CID_INSERT_SAV (VIVID_CID_VIVID_BASE + 6) #define VIVID_CID_INSERT_EAV (VIVID_CID_VIVID_BASE + 7) #define VIVID_CID_VBI_CAP_INTERLACED (VIVID_CID_VIVID_BASE + 8) +#define VIVID_CID_INSERT_HDMI_VIDEO_GUARD_BAND (VIVID_CID_VIVID_BASE + 9) #define VIVID_CID_HFLIP (VIVID_CID_VIVID_BASE + 20) #define VIVID_CID_VFLIP (VIVID_CID_VIVID_BASE + 21) @@ -189,6 +191,19 @@ static const struct v4l2_ctrl_config vivid_ctrl_u32_array = { .dims = { 1 }, }; +static const struct v4l2_ctrl_config vivid_ctrl_u32_dyn_array = { + .ops = &vivid_user_gen_ctrl_ops, + .id = VIVID_CID_U32_DYN_ARRAY, + .name = "U32 Dynamic Array", + .type = V4L2_CTRL_TYPE_U32, + .flags = V4L2_CTRL_FLAG_DYNAMIC_ARRAY, + .def = 50, + .min = 10, + .max = 90, + .step = 1, + .dims = { 100 }, +}; + static const struct v4l2_ctrl_config vivid_ctrl_u16_matrix = { .ops = &vivid_user_gen_ctrl_ops, .id = VIVID_CID_U16_MATRIX, @@ -474,6 +489,9 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl) case VIVID_CID_INSERT_EAV: tpg_s_insert_eav(&dev->tpg, ctrl->val); break; + case VIVID_CID_INSERT_HDMI_VIDEO_GUARD_BAND: + tpg_s_insert_hdmi_video_guard_band(&dev->tpg, ctrl->val); + break; case VIVID_CID_HFLIP: dev->sensor_hflip = ctrl->val; tpg_s_hflip(&dev->tpg, dev->sensor_hflip ^ dev->hflip); @@ -660,6 +678,15 @@ static const struct v4l2_ctrl_config vivid_ctrl_insert_eav = { .step = 1, }; +static const struct v4l2_ctrl_config vivid_ctrl_insert_hdmi_video_guard_band = { + .ops = &vivid_vid_cap_ctrl_ops, + .id = VIVID_CID_INSERT_HDMI_VIDEO_GUARD_BAND, + .name = "Insert Video Guard Band", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .step = 1, +}; + static const struct v4l2_ctrl_config vivid_ctrl_hflip = { .ops = &vivid_vid_cap_ctrl_ops, .id = VIVID_CID_HFLIP, @@ -1612,6 +1639,7 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, dev->ro_int32 = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_ro_int32, NULL); v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_area, NULL); v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_u32_array, NULL); + v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_u32_dyn_array, NULL); v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_u16_matrix, NULL); v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_u8_4d_array, NULL); @@ -1638,6 +1666,7 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_vflip, NULL); v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_insert_sav, NULL); v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_insert_eav, NULL); + v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_insert_hdmi_video_guard_band, NULL); v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_reduced_fps, NULL); if (show_ccs_cap) { dev->ctrl_has_crop_cap = v4l2_ctrl_new_custom(hdl_vid_cap, diff --git a/drivers/media/test-drivers/vivid/vivid-vid-common.c b/drivers/media/test-drivers/vivid/vivid-vid-common.c index 19701fe72030..38d788b5cf19 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-common.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-common.c @@ -199,6 +199,21 @@ struct vivid_fmt vivid_formats[] = { .buffers = 1, }, { + .fourcc = V4L2_PIX_FMT_YUVA32, + .vdownsampling = { 1 }, + .bit_depth = { 32 }, + .planes = 1, + .buffers = 1, + .alpha_mask = 0xff000000, + }, + { + .fourcc = V4L2_PIX_FMT_YUVX32, + .vdownsampling = { 1 }, + .bit_depth = { 32 }, + .planes = 1, + .buffers = 1, + }, + { .fourcc = V4L2_PIX_FMT_GREY, .vdownsampling = { 1 }, .bit_depth = { 8 }, diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig index 8de08704f8e4..af88e0766388 100644 --- a/drivers/media/usb/Kconfig +++ b/drivers/media/usb/Kconfig @@ -17,7 +17,6 @@ source "drivers/media/usb/cpia2/Kconfig" source "drivers/media/usb/gspca/Kconfig" source "drivers/media/usb/pwc/Kconfig" source "drivers/media/usb/s2255/Kconfig" -source "drivers/media/usb/stkwebcam/Kconfig" source "drivers/media/usb/usbtv/Kconfig" source "drivers/media/usb/uvc/Kconfig" source "drivers/media/usb/zr364xx/Kconfig" diff --git a/drivers/media/usb/Makefile b/drivers/media/usb/Makefile index 044bd46c799c..25fa2015b179 100644 --- a/drivers/media/usb/Makefile +++ b/drivers/media/usb/Makefile @@ -10,7 +10,6 @@ obj-y += dvb-usb/ obj-y += dvb-usb-v2/ obj-y += s2255/ obj-y += siano/ -obj-y += stkwebcam/ obj-y += ttusb-budget/ obj-y += ttusb-dec/ obj-y += zr364xx/ diff --git a/drivers/media/usb/airspy/airspy.c b/drivers/media/usb/airspy/airspy.c index d568452618d1..240a7cc56777 100644 --- a/drivers/media/usb/airspy/airspy.c +++ b/drivers/media/usb/airspy/airspy.c @@ -123,7 +123,7 @@ struct airspy { /* USB control message buffer */ #define BUF_SIZE 128 - u8 buf[BUF_SIZE]; + u8 *buf; /* Current configuration */ unsigned int f_adc; @@ -856,6 +856,7 @@ static void airspy_video_release(struct v4l2_device *v) v4l2_ctrl_handler_free(&s->hdl); v4l2_device_unregister(&s->v4l2_dev); + kfree(s->buf); kfree(s); } @@ -963,7 +964,10 @@ static int airspy_probe(struct usb_interface *intf, { struct airspy *s; int ret; - u8 u8tmp, buf[BUF_SIZE]; + u8 u8tmp, *buf; + + buf = NULL; + ret = -ENOMEM; s = kzalloc(sizeof(struct airspy), GFP_KERNEL); if (s == NULL) { @@ -971,6 +975,13 @@ static int airspy_probe(struct usb_interface *intf, return -ENOMEM; } + s->buf = kzalloc(BUF_SIZE, GFP_KERNEL); + if (!s->buf) + goto err_free_mem; + buf = kzalloc(BUF_SIZE, GFP_KERNEL); + if (!buf) + goto err_free_mem; + mutex_init(&s->v4l2_lock); mutex_init(&s->vb_queue_lock); spin_lock_init(&s->queued_bufs_lock); @@ -1068,6 +1079,8 @@ err_free_controls: v4l2_ctrl_handler_free(&s->hdl); v4l2_device_unregister(&s->v4l2_dev); err_free_mem: + kfree(buf); + kfree(s->buf); kfree(s); return ret; } diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index 4d5ab1433b44..ce1b0d9e0741 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -10,16 +10,6 @@ // // This driver is based on my previous au600 usb pstn audio driver // and inherits all the copyrights -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. #include "em28xx.h" diff --git a/drivers/media/usb/em28xx/em28xx-camera.c b/drivers/media/usb/em28xx/em28xx-camera.c index d1e66b503f4d..b5f58dc6dd0f 100644 --- a/drivers/media/usb/em28xx/em28xx-camera.c +++ b/drivers/media/usb/em28xx/em28xx-camera.c @@ -4,16 +4,6 @@ // // Copyright (C) 2009 Mauro Carvalho Chehab <mchehab@kernel.org> // Copyright (C) 2013 Frank Schäfer <fschaefer.oss@googlemail.com> -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. #include "em28xx.h" diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index ae25d2cbfdfe..4d037c92af7c 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -8,16 +8,6 @@ // Mauro Carvalho Chehab <mchehab@kernel.org> // Sascha Sommer <saschasommer@freenet.de> // Copyright (C) 2012 Frank Schäfer <fschaefer.oss@googlemail.com> -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. #include "em28xx.h" diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index c837cc528a33..61d7bf701d57 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -7,16 +7,6 @@ // Mauro Carvalho Chehab <mchehab@kernel.org> // Sascha Sommer <saschasommer@freenet.de> // Copyright (C) 2012 Frank Schäfer <fschaefer.oss@googlemail.com> -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. #include "em28xx.h" diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 471bd74667e3..185e89c18d68 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -16,10 +16,6 @@ // Based on cx88-dvb, saa7134-dvb and videobuf-dvb originally written by: // (c) 2004, 2005 Chris Pascoe <c.pascoe@itee.uq.edu.au> // (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation version 2 of the License. #include "em28xx.h" diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index b9a8d3fbad1a..a7eb11f7fb34 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -7,16 +7,6 @@ // Mauro Carvalho Chehab <mchehab@kernel.org> // Sascha Sommer <saschasommer@freenet.de> // Copyright (C) 2013 Frank Schäfer <fschaefer.oss@googlemail.com> -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. #include "em28xx.h" diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 0b6d77c3bec8..5f3b00869bdb 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -6,16 +6,6 @@ // Markus Rechberger <mrechberger@gmail.com> // Mauro Carvalho Chehab <mchehab@kernel.org> // Sascha Sommer <saschasommer@freenet.de> -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. #include "em28xx.h" diff --git a/drivers/media/usb/em28xx/em28xx-v4l.h b/drivers/media/usb/em28xx/em28xx-v4l.h index 6216cdd182f3..8455dcfdaf81 100644 --- a/drivers/media/usb/em28xx/em28xx-v4l.h +++ b/drivers/media/usb/em28xx/em28xx-v4l.h @@ -4,15 +4,6 @@ * video capture devices * * Copyright (C) 2013-2014 Mauro Carvalho Chehab <mchehab+samsung@kernel.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count); diff --git a/drivers/media/usb/em28xx/em28xx-vbi.c b/drivers/media/usb/em28xx/em28xx-vbi.c index 63c48361d3f2..b253c44c9724 100644 --- a/drivers/media/usb/em28xx/em28xx-vbi.c +++ b/drivers/media/usb/em28xx/em28xx-vbi.c @@ -5,16 +5,6 @@ // Copyright (C) 2009 Devin Heitmueller <dheitmueller@kernellabs.com> // // This work was sponsored by EyeMagnet Limited. -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. #include "em28xx.h" diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 6b84c3413e83..8181c0e6a25b 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -11,16 +11,6 @@ // // Some parts based on SN9C10x PC Camera Controllers GPL driver made // by Luca Risolia <luca.risolia@studio.unibo.it> -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. #include "em28xx.h" diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 7fc0b68a4a22..db18dd814a67 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -8,16 +8,6 @@ * Copyright (C) 2012 Frank Schäfer <fschaefer.oss@googlemail.com> * * Based on the em2800 driver from Sascha Sommer <saschasommer@freenet.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef _EM28XX_H diff --git a/drivers/media/usb/gspca/spca501.c b/drivers/media/usb/gspca/spca501.c index ecc97f807cfa..f7c75d7535c4 100644 --- a/drivers/media/usb/gspca/spca501.c +++ b/drivers/media/usb/gspca/spca501.c @@ -488,7 +488,7 @@ static const __u16 spca501_init_data[][3] = { /* Data for video camera init before capture. * Capture and decoding by Colin Peart. - * This is is for the 3com HomeConnect Lite which is spca501a based. + * This is for the 3com HomeConnect Lite which is spca501a based. */ static const __u16 spca501_3com_open_data[][3] = { /* bmRequest,value,index */ diff --git a/drivers/media/usb/gspca/xirlink_cit.c b/drivers/media/usb/gspca/xirlink_cit.c index cc87c24dd24c..acfb9a195106 100644 --- a/drivers/media/usb/gspca/xirlink_cit.c +++ b/drivers/media/usb/gspca/xirlink_cit.c @@ -817,7 +817,7 @@ static void cit_model2_Packet1(struct gspca_dev *gspca_dev, u16 v1, u16 v2) * 00_d141_0124 * 00_0096_0127 * 00_fea8_0124 -*/ + */ static void cit_model3_Packet1(struct gspca_dev *gspca_dev, u16 v1, u16 v2) { cit_write_reg(gspca_dev, 0x0078, 0x012d); diff --git a/drivers/media/usb/hdpvr/hdpvr-video.c b/drivers/media/usb/hdpvr/hdpvr-video.c index 60e57e0f1927..fd7d2a9d0449 100644 --- a/drivers/media/usb/hdpvr/hdpvr-video.c +++ b/drivers/media/usb/hdpvr/hdpvr-video.c @@ -409,7 +409,7 @@ static ssize_t hdpvr_read(struct file *file, char __user *buffer, size_t count, struct hdpvr_device *dev = video_drvdata(file); struct hdpvr_buffer *buf = NULL; struct urb *urb; - unsigned int ret = 0; + int ret = 0; int rem, cnt; if (*pos) diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c index a9666373af6b..62ff1fa1c753 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c @@ -2610,6 +2610,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, del_timer_sync(&hdw->encoder_run_timer); del_timer_sync(&hdw->encoder_wait_timer); flush_work(&hdw->workpoll); + v4l2_device_unregister(&hdw->v4l2_dev); usb_free_urb(hdw->ctl_read_urb); usb_free_urb(hdw->ctl_write_urb); kfree(hdw->ctl_read_buffer); @@ -5040,7 +5041,7 @@ void pvr2_hdw_status_poll(struct pvr2_hdw *hdw) /* Note: There apparently is no replacement for VIDIOC_CROPCAP using v4l2-subdev - therefore we can't support that AT ALL right now. (Of course, no sub-drivers seem to implement it either. - But now it's a a chicken and egg problem...) */ + But now it's a chicken and egg problem...) */ v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner, g_tuner, vtp); pvr2_trace(PVR2_TRACE_CHIPS, "subdev status poll type=%u strength=%u audio=0x%x cap=0x%x low=%u hi=%u", vtp->type, diff --git a/drivers/media/usb/stkwebcam/Kconfig b/drivers/media/usb/stkwebcam/Kconfig deleted file mode 100644 index d94d023f1aa0..000000000000 --- a/drivers/media/usb/stkwebcam/Kconfig +++ /dev/null @@ -1,14 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config USB_STKWEBCAM - tristate "USB Syntek DC1125 Camera support" - depends on VIDEO_DEV - help - Say Y here if you want to use this type of camera. - Supported devices are typically found in some Asus laptops, - with USB id 174f:a311 and 05e1:0501. Other Syntek cameras - may be supported by the stk11xx driver, from which this is - derived, see <http://sourceforge.net/projects/syntekdriver/> - - To compile this driver as a module, choose M here: the - module will be called stkwebcam. - diff --git a/drivers/media/usb/stkwebcam/Makefile b/drivers/media/usb/stkwebcam/Makefile deleted file mode 100644 index daa9ae6d48c2..000000000000 --- a/drivers/media/usb/stkwebcam/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -stkwebcam-objs := stk-webcam.o stk-sensor.o - -obj-$(CONFIG_USB_STKWEBCAM) += stkwebcam.o - diff --git a/drivers/media/usb/stkwebcam/stk-sensor.c b/drivers/media/usb/stkwebcam/stk-sensor.c deleted file mode 100644 index 94aa6a27f934..000000000000 --- a/drivers/media/usb/stkwebcam/stk-sensor.c +++ /dev/null @@ -1,587 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* stk-sensor.c: Driver for ov96xx sensor (used in some Syntek webcams) - * - * Copyright 2007-2008 Jaime Velasco Juan <jsagarribay@gmail.com> - * - * Some parts derived from ov7670.c: - * Copyright 2006 One Laptop Per Child Association, Inc. Written - * by Jonathan Corbet with substantial inspiration from Mark - * McClelland's ovcamchip code. - * - * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net> - * - * This file may be distributed under the terms of the GNU General - */ - -/* Controlling the sensor via the STK1125 vendor specific control interface: - * The camera uses an OmniVision sensor and the stk1125 provides an - * SCCB(i2c)-USB bridge which let us program the sensor. - * In my case the sensor id is 0x9652, it can be read from sensor's register - * 0x0A and 0x0B as follows: - * - read register #R: - * output #R to index 0x0208 - * output 0x0070 to index 0x0200 - * input 1 byte from index 0x0201 (some kind of status register) - * until its value is 0x01 - * input 1 byte from index 0x0209. This is the value of #R - * - write value V to register #R - * output #R to index 0x0204 - * output V to index 0x0205 - * output 0x0005 to index 0x0200 - * input 1 byte from index 0x0201 until its value becomes 0x04 - */ - -/* It seems the i2c bus is controlled with these registers */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include "stk-webcam.h" - -#define STK_IIC_BASE (0x0200) -# define STK_IIC_OP (STK_IIC_BASE) -# define STK_IIC_OP_TX (0x05) -# define STK_IIC_OP_RX (0x70) -# define STK_IIC_STAT (STK_IIC_BASE+1) -# define STK_IIC_STAT_TX_OK (0x04) -# define STK_IIC_STAT_RX_OK (0x01) -/* I don't know what does this register. - * when it is 0x00 or 0x01, we cannot talk to the sensor, - * other values work */ -# define STK_IIC_ENABLE (STK_IIC_BASE+2) -# define STK_IIC_ENABLE_NO (0x00) -/* This is what the driver writes in windows */ -# define STK_IIC_ENABLE_YES (0x1e) -/* - * Address of the slave. Seems like the binary driver look for the - * sensor in multiple places, attempting a reset sequence. - * We only know about the ov9650 - */ -# define STK_IIC_ADDR (STK_IIC_BASE+3) -# define STK_IIC_TX_INDEX (STK_IIC_BASE+4) -# define STK_IIC_TX_VALUE (STK_IIC_BASE+5) -# define STK_IIC_RX_INDEX (STK_IIC_BASE+8) -# define STK_IIC_RX_VALUE (STK_IIC_BASE+9) - -#define MAX_RETRIES (50) - -#define SENSOR_ADDRESS (0x60) - -/* From ov7670.c (These registers aren't fully accurate) */ - -/* Registers */ -#define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */ -#define REG_BLUE 0x01 /* blue gain */ -#define REG_RED 0x02 /* red gain */ -#define REG_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */ -#define REG_COM1 0x04 /* Control 1 */ -#define COM1_CCIR656 0x40 /* CCIR656 enable */ -#define COM1_QFMT 0x20 /* QVGA/QCIF format */ -#define COM1_SKIP_0 0x00 /* Do not skip any row */ -#define COM1_SKIP_2 0x04 /* Skip 2 rows of 4 */ -#define COM1_SKIP_3 0x08 /* Skip 3 rows of 4 */ -#define REG_BAVE 0x05 /* U/B Average level */ -#define REG_GbAVE 0x06 /* Y/Gb Average level */ -#define REG_AECHH 0x07 /* AEC MS 5 bits */ -#define REG_RAVE 0x08 /* V/R Average level */ -#define REG_COM2 0x09 /* Control 2 */ -#define COM2_SSLEEP 0x10 /* Soft sleep mode */ -#define REG_PID 0x0a /* Product ID MSB */ -#define REG_VER 0x0b /* Product ID LSB */ -#define REG_COM3 0x0c /* Control 3 */ -#define COM3_SWAP 0x40 /* Byte swap */ -#define COM3_SCALEEN 0x08 /* Enable scaling */ -#define COM3_DCWEN 0x04 /* Enable downsamp/crop/window */ -#define REG_COM4 0x0d /* Control 4 */ -#define REG_COM5 0x0e /* All "reserved" */ -#define REG_COM6 0x0f /* Control 6 */ -#define REG_AECH 0x10 /* More bits of AEC value */ -#define REG_CLKRC 0x11 /* Clock control */ -#define CLK_PLL 0x80 /* Enable internal PLL */ -#define CLK_EXT 0x40 /* Use external clock directly */ -#define CLK_SCALE 0x3f /* Mask for internal clock scale */ -#define REG_COM7 0x12 /* Control 7 */ -#define COM7_RESET 0x80 /* Register reset */ -#define COM7_FMT_MASK 0x38 -#define COM7_FMT_SXGA 0x00 -#define COM7_FMT_VGA 0x40 -#define COM7_FMT_CIF 0x20 /* CIF format */ -#define COM7_FMT_QVGA 0x10 /* QVGA format */ -#define COM7_FMT_QCIF 0x08 /* QCIF format */ -#define COM7_RGB 0x04 /* bits 0 and 2 - RGB format */ -#define COM7_YUV 0x00 /* YUV */ -#define COM7_BAYER 0x01 /* Bayer format */ -#define COM7_PBAYER 0x05 /* "Processed bayer" */ -#define REG_COM8 0x13 /* Control 8 */ -#define COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */ -#define COM8_AECSTEP 0x40 /* Unlimited AEC step size */ -#define COM8_BFILT 0x20 /* Band filter enable */ -#define COM8_AGC 0x04 /* Auto gain enable */ -#define COM8_AWB 0x02 /* White balance enable */ -#define COM8_AEC 0x01 /* Auto exposure enable */ -#define REG_COM9 0x14 /* Control 9 - gain ceiling */ -#define REG_COM10 0x15 /* Control 10 */ -#define COM10_HSYNC 0x40 /* HSYNC instead of HREF */ -#define COM10_PCLK_HB 0x20 /* Suppress PCLK on horiz blank */ -#define COM10_HREF_REV 0x08 /* Reverse HREF */ -#define COM10_VS_LEAD 0x04 /* VSYNC on clock leading edge */ -#define COM10_VS_NEG 0x02 /* VSYNC negative */ -#define COM10_HS_NEG 0x01 /* HSYNC negative */ -#define REG_HSTART 0x17 /* Horiz start high bits */ -#define REG_HSTOP 0x18 /* Horiz stop high bits */ -#define REG_VSTART 0x19 /* Vert start high bits */ -#define REG_VSTOP 0x1a /* Vert stop high bits */ -#define REG_PSHFT 0x1b /* Pixel delay after HREF */ -#define REG_MIDH 0x1c /* Manuf. ID high */ -#define REG_MIDL 0x1d /* Manuf. ID low */ -#define REG_MVFP 0x1e /* Mirror / vflip */ -#define MVFP_MIRROR 0x20 /* Mirror image */ -#define MVFP_FLIP 0x10 /* Vertical flip */ - -#define REG_AEW 0x24 /* AGC upper limit */ -#define REG_AEB 0x25 /* AGC lower limit */ -#define REG_VPT 0x26 /* AGC/AEC fast mode op region */ -#define REG_ADVFL 0x2d /* Insert dummy lines (LSB) */ -#define REG_ADVFH 0x2e /* Insert dummy lines (MSB) */ -#define REG_HSYST 0x30 /* HSYNC rising edge delay */ -#define REG_HSYEN 0x31 /* HSYNC falling edge delay */ -#define REG_HREF 0x32 /* HREF pieces */ -#define REG_TSLB 0x3a /* lots of stuff */ -#define TSLB_YLAST 0x04 /* UYVY or VYUY - see com13 */ -#define TSLB_BYTEORD 0x08 /* swap bytes in 16bit mode? */ -#define REG_COM11 0x3b /* Control 11 */ -#define COM11_NIGHT 0x80 /* NIght mode enable */ -#define COM11_NMFR 0x60 /* Two bit NM frame rate */ -#define COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */ -#define COM11_50HZ 0x08 /* Manual 50Hz select */ -#define COM11_EXP 0x02 -#define REG_COM12 0x3c /* Control 12 */ -#define COM12_HREF 0x80 /* HREF always */ -#define REG_COM13 0x3d /* Control 13 */ -#define COM13_GAMMA 0x80 /* Gamma enable */ -#define COM13_UVSAT 0x40 /* UV saturation auto adjustment */ -#define COM13_CMATRIX 0x10 /* Enable color matrix for RGB or YUV */ -#define COM13_UVSWAP 0x01 /* V before U - w/TSLB */ -#define REG_COM14 0x3e /* Control 14 */ -#define COM14_DCWEN 0x10 /* DCW/PCLK-scale enable */ -#define REG_EDGE 0x3f /* Edge enhancement factor */ -#define REG_COM15 0x40 /* Control 15 */ -#define COM15_R10F0 0x00 /* Data range 10 to F0 */ -#define COM15_R01FE 0x80 /* 01 to FE */ -#define COM15_R00FF 0xc0 /* 00 to FF */ -#define COM15_RGB565 0x10 /* RGB565 output */ -#define COM15_RGBFIXME 0x20 /* FIXME */ -#define COM15_RGB555 0x30 /* RGB555 output */ -#define REG_COM16 0x41 /* Control 16 */ -#define COM16_AWBGAIN 0x08 /* AWB gain enable */ -#define REG_COM17 0x42 /* Control 17 */ -#define COM17_AECWIN 0xc0 /* AEC window - must match COM4 */ -#define COM17_CBAR 0x08 /* DSP Color bar */ - -/* - * This matrix defines how the colors are generated, must be - * tweaked to adjust hue and saturation. - * - * Order: v-red, v-green, v-blue, u-red, u-green, u-blue - * - * They are nine-bit signed quantities, with the sign bit - * stored in 0x58. Sign for v-red is bit 0, and up from there. - */ -#define REG_CMATRIX_BASE 0x4f -#define CMATRIX_LEN 6 -#define REG_CMATRIX_SIGN 0x58 - - -#define REG_BRIGHT 0x55 /* Brightness */ -#define REG_CONTRAS 0x56 /* Contrast control */ - -#define REG_GFIX 0x69 /* Fix gain control */ - -#define REG_RGB444 0x8c /* RGB 444 control */ -#define R444_ENABLE 0x02 /* Turn on RGB444, overrides 5x5 */ -#define R444_RGBX 0x01 /* Empty nibble at end */ - -#define REG_HAECC1 0x9f /* Hist AEC/AGC control 1 */ -#define REG_HAECC2 0xa0 /* Hist AEC/AGC control 2 */ - -#define REG_BD50MAX 0xa5 /* 50hz banding step limit */ -#define REG_HAECC3 0xa6 /* Hist AEC/AGC control 3 */ -#define REG_HAECC4 0xa7 /* Hist AEC/AGC control 4 */ -#define REG_HAECC5 0xa8 /* Hist AEC/AGC control 5 */ -#define REG_HAECC6 0xa9 /* Hist AEC/AGC control 6 */ -#define REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */ -#define REG_BD60MAX 0xab /* 60hz banding step limit */ - - - - -/* Returns 0 if OK */ -static int stk_sensor_outb(struct stk_camera *dev, u8 reg, u8 val) -{ - int i = 0; - u8 tmpval = 0; - - if (stk_camera_write_reg(dev, STK_IIC_TX_INDEX, reg)) - return 1; - if (stk_camera_write_reg(dev, STK_IIC_TX_VALUE, val)) - return 1; - if (stk_camera_write_reg(dev, STK_IIC_OP, STK_IIC_OP_TX)) - return 1; - do { - if (stk_camera_read_reg(dev, STK_IIC_STAT, &tmpval)) - return 1; - i++; - } while (tmpval == 0 && i < MAX_RETRIES); - if (tmpval != STK_IIC_STAT_TX_OK) { - if (tmpval) - pr_err("stk_sensor_outb failed, status=0x%02x\n", - tmpval); - return 1; - } else - return 0; -} - -static int stk_sensor_inb(struct stk_camera *dev, u8 reg, u8 *val) -{ - int i = 0; - u8 tmpval = 0; - - if (stk_camera_write_reg(dev, STK_IIC_RX_INDEX, reg)) - return 1; - if (stk_camera_write_reg(dev, STK_IIC_OP, STK_IIC_OP_RX)) - return 1; - do { - if (stk_camera_read_reg(dev, STK_IIC_STAT, &tmpval)) - return 1; - i++; - } while (tmpval == 0 && i < MAX_RETRIES); - if (tmpval != STK_IIC_STAT_RX_OK) { - if (tmpval) - pr_err("stk_sensor_inb failed, status=0x%02x\n", - tmpval); - return 1; - } - - if (stk_camera_read_reg(dev, STK_IIC_RX_VALUE, &tmpval)) - return 1; - - *val = tmpval; - return 0; -} - -static int stk_sensor_write_regvals(struct stk_camera *dev, - struct regval *rv) -{ - int ret; - if (rv == NULL) - return 0; - while (rv->reg != 0xff || rv->val != 0xff) { - ret = stk_sensor_outb(dev, rv->reg, rv->val); - if (ret != 0) - return ret; - rv++; - } - return 0; -} - -int stk_sensor_sleep(struct stk_camera *dev) -{ - u8 tmp; - return stk_sensor_inb(dev, REG_COM2, &tmp) - || stk_sensor_outb(dev, REG_COM2, tmp|COM2_SSLEEP); -} - -int stk_sensor_wakeup(struct stk_camera *dev) -{ - u8 tmp; - return stk_sensor_inb(dev, REG_COM2, &tmp) - || stk_sensor_outb(dev, REG_COM2, tmp&~COM2_SSLEEP); -} - -static struct regval ov_initvals[] = { - {REG_CLKRC, CLK_PLL}, - {REG_COM11, 0x01}, - {0x6a, 0x7d}, - {REG_AECH, 0x40}, - {REG_GAIN, 0x00}, - {REG_BLUE, 0x80}, - {REG_RED, 0x80}, - /* Do not enable fast AEC for now */ - /*{REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC},*/ - {REG_COM8, COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC}, - {0x39, 0x50}, {0x38, 0x93}, - {0x37, 0x00}, {0x35, 0x81}, - {REG_COM5, 0x20}, - {REG_COM1, 0x00}, - {REG_COM3, 0x00}, - {REG_COM4, 0x00}, - {REG_PSHFT, 0x00}, - {0x16, 0x07}, - {0x33, 0xe2}, {0x34, 0xbf}, - {REG_COM16, 0x00}, - {0x96, 0x04}, - /* Gamma curve values */ -/* { 0x7a, 0x20 }, { 0x7b, 0x10 }, - { 0x7c, 0x1e }, { 0x7d, 0x35 }, - { 0x7e, 0x5a }, { 0x7f, 0x69 }, - { 0x80, 0x76 }, { 0x81, 0x80 }, - { 0x82, 0x88 }, { 0x83, 0x8f }, - { 0x84, 0x96 }, { 0x85, 0xa3 }, - { 0x86, 0xaf }, { 0x87, 0xc4 }, - { 0x88, 0xd7 }, { 0x89, 0xe8 }, -*/ - {REG_GFIX, 0x40}, - {0x8e, 0x00}, - {REG_COM12, 0x73}, - {0x8f, 0xdf}, {0x8b, 0x06}, - {0x8c, 0x20}, - {0x94, 0x88}, {0x95, 0x88}, -/* {REG_COM15, 0xc1}, TODO */ - {0x29, 0x3f}, - {REG_COM6, 0x42}, - {REG_BD50MAX, 0x80}, - {REG_HAECC6, 0xb8}, {REG_HAECC7, 0x92}, - {REG_BD60MAX, 0x0a}, - {0x90, 0x00}, {0x91, 0x00}, - {REG_HAECC1, 0x00}, {REG_HAECC2, 0x00}, - {REG_AEW, 0x68}, {REG_AEB, 0x5c}, - {REG_VPT, 0xc3}, - {REG_COM9, 0x2e}, - {0x2a, 0x00}, {0x2b, 0x00}, - - {0xff, 0xff}, /* END MARKER */ -}; - -/* Probe the I2C bus and initialise the sensor chip */ -int stk_sensor_init(struct stk_camera *dev) -{ - u8 idl = 0; - u8 idh = 0; - - if (stk_camera_write_reg(dev, STK_IIC_ENABLE, STK_IIC_ENABLE_YES) - || stk_camera_write_reg(dev, STK_IIC_ADDR, SENSOR_ADDRESS) - || stk_sensor_outb(dev, REG_COM7, COM7_RESET)) { - pr_err("Sensor resetting failed\n"); - return -ENODEV; - } - msleep(10); - /* Read the manufacturer ID: ov = 0x7FA2 */ - if (stk_sensor_inb(dev, REG_MIDH, &idh) - || stk_sensor_inb(dev, REG_MIDL, &idl)) { - pr_err("Strange error reading sensor ID\n"); - return -ENODEV; - } - if (idh != 0x7f || idl != 0xa2) { - pr_err("Huh? you don't have a sensor from ovt\n"); - return -ENODEV; - } - if (stk_sensor_inb(dev, REG_PID, &idh) - || stk_sensor_inb(dev, REG_VER, &idl)) { - pr_err("Could not read sensor model\n"); - return -ENODEV; - } - stk_sensor_write_regvals(dev, ov_initvals); - msleep(10); - pr_info("OmniVision sensor detected, id %02X%02X at address %x\n", - idh, idl, SENSOR_ADDRESS); - return 0; -} - -/* V4L2_PIX_FMT_UYVY */ -static struct regval ov_fmt_uyvy[] = { - {REG_TSLB, TSLB_YLAST|0x08 }, - { 0x4f, 0x80 }, /* "matrix coefficient 1" */ - { 0x50, 0x80 }, /* "matrix coefficient 2" */ - { 0x51, 0 }, /* vb */ - { 0x52, 0x22 }, /* "matrix coefficient 4" */ - { 0x53, 0x5e }, /* "matrix coefficient 5" */ - { 0x54, 0x80 }, /* "matrix coefficient 6" */ - {REG_COM13, COM13_UVSAT|COM13_CMATRIX}, - {REG_COM15, COM15_R00FF }, - {0xff, 0xff}, /* END MARKER */ -}; -/* V4L2_PIX_FMT_YUYV */ -static struct regval ov_fmt_yuyv[] = { - {REG_TSLB, 0 }, - { 0x4f, 0x80 }, /* "matrix coefficient 1" */ - { 0x50, 0x80 }, /* "matrix coefficient 2" */ - { 0x51, 0 }, /* vb */ - { 0x52, 0x22 }, /* "matrix coefficient 4" */ - { 0x53, 0x5e }, /* "matrix coefficient 5" */ - { 0x54, 0x80 }, /* "matrix coefficient 6" */ - {REG_COM13, COM13_UVSAT|COM13_CMATRIX}, - {REG_COM15, COM15_R00FF }, - {0xff, 0xff}, /* END MARKER */ -}; - -/* V4L2_PIX_FMT_RGB565X rrrrrggg gggbbbbb */ -static struct regval ov_fmt_rgbr[] = { - { REG_RGB444, 0 }, /* No RGB444 please */ - {REG_TSLB, 0x00}, - { REG_COM1, 0x0 }, - { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ - { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ - { 0x50, 0xb3 }, /* "matrix coefficient 2" */ - { 0x51, 0 }, /* vb */ - { 0x52, 0x3d }, /* "matrix coefficient 4" */ - { 0x53, 0xa7 }, /* "matrix coefficient 5" */ - { 0x54, 0xe4 }, /* "matrix coefficient 6" */ - { REG_COM13, COM13_GAMMA }, - { REG_COM15, COM15_RGB565|COM15_R00FF }, - { 0xff, 0xff }, -}; - -/* V4L2_PIX_FMT_RGB565 gggbbbbb rrrrrggg */ -static struct regval ov_fmt_rgbp[] = { - { REG_RGB444, 0 }, /* No RGB444 please */ - {REG_TSLB, TSLB_BYTEORD }, - { REG_COM1, 0x0 }, - { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ - { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ - { 0x50, 0xb3 }, /* "matrix coefficient 2" */ - { 0x51, 0 }, /* vb */ - { 0x52, 0x3d }, /* "matrix coefficient 4" */ - { 0x53, 0xa7 }, /* "matrix coefficient 5" */ - { 0x54, 0xe4 }, /* "matrix coefficient 6" */ - { REG_COM13, COM13_GAMMA }, - { REG_COM15, COM15_RGB565|COM15_R00FF }, - { 0xff, 0xff }, -}; - -/* V4L2_PIX_FMT_SRGGB8 */ -static struct regval ov_fmt_bayer[] = { - /* This changes color order */ - {REG_TSLB, 0x40}, /* BGGR */ - /* {REG_TSLB, 0x08}, */ /* BGGR with vertical image flipping */ - {REG_COM15, COM15_R00FF }, - {0xff, 0xff}, /* END MARKER */ -}; -/* - * Store a set of start/stop values into the camera. - */ -static int stk_sensor_set_hw(struct stk_camera *dev, - int hstart, int hstop, int vstart, int vstop) -{ - int ret; - unsigned char v; -/* - * Horizontal: 11 bits, top 8 live in hstart and hstop. Bottom 3 of - * hstart are in href[2:0], bottom 3 of hstop in href[5:3]. There is - * a mystery "edge offset" value in the top two bits of href. - */ - ret = stk_sensor_outb(dev, REG_HSTART, (hstart >> 3) & 0xff); - ret += stk_sensor_outb(dev, REG_HSTOP, (hstop >> 3) & 0xff); - ret += stk_sensor_inb(dev, REG_HREF, &v); - v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7); - msleep(10); - ret += stk_sensor_outb(dev, REG_HREF, v); -/* - * Vertical: similar arrangement (note: this is different from ov7670.c) - */ - ret += stk_sensor_outb(dev, REG_VSTART, (vstart >> 3) & 0xff); - ret += stk_sensor_outb(dev, REG_VSTOP, (vstop >> 3) & 0xff); - ret += stk_sensor_inb(dev, REG_VREF, &v); - v = (v & 0xc0) | ((vstop & 0x7) << 3) | (vstart & 0x7); - msleep(10); - ret += stk_sensor_outb(dev, REG_VREF, v); - return ret; -} - - -int stk_sensor_configure(struct stk_camera *dev) -{ - int com7; - /* - * We setup the sensor to output dummy lines in low-res modes, - * so we don't get absurdly hight framerates. - */ - unsigned dummylines; - int flip; - struct regval *rv; - - switch (dev->vsettings.mode) { - case MODE_QCIF: com7 = COM7_FMT_QCIF; - dummylines = 604; - break; - case MODE_QVGA: com7 = COM7_FMT_QVGA; - dummylines = 267; - break; - case MODE_CIF: com7 = COM7_FMT_CIF; - dummylines = 412; - break; - case MODE_VGA: com7 = COM7_FMT_VGA; - dummylines = 11; - break; - case MODE_SXGA: com7 = COM7_FMT_SXGA; - dummylines = 0; - break; - default: - pr_err("Unsupported mode %d\n", dev->vsettings.mode); - return -EFAULT; - } - switch (dev->vsettings.palette) { - case V4L2_PIX_FMT_UYVY: - com7 |= COM7_YUV; - rv = ov_fmt_uyvy; - break; - case V4L2_PIX_FMT_YUYV: - com7 |= COM7_YUV; - rv = ov_fmt_yuyv; - break; - case V4L2_PIX_FMT_RGB565: - com7 |= COM7_RGB; - rv = ov_fmt_rgbp; - break; - case V4L2_PIX_FMT_RGB565X: - com7 |= COM7_RGB; - rv = ov_fmt_rgbr; - break; - case V4L2_PIX_FMT_SBGGR8: - com7 |= COM7_PBAYER; - rv = ov_fmt_bayer; - break; - default: - pr_err("Unsupported colorspace\n"); - return -EFAULT; - } - /*FIXME sometimes the sensor go to a bad state - stk_sensor_write_regvals(dev, ov_initvals); */ - stk_sensor_outb(dev, REG_COM7, com7); - msleep(50); - stk_sensor_write_regvals(dev, rv); - flip = (dev->vsettings.vflip?MVFP_FLIP:0) - | (dev->vsettings.hflip?MVFP_MIRROR:0); - stk_sensor_outb(dev, REG_MVFP, flip); - if (dev->vsettings.palette == V4L2_PIX_FMT_SBGGR8 - && !dev->vsettings.vflip) - stk_sensor_outb(dev, REG_TSLB, 0x08); - stk_sensor_outb(dev, REG_ADVFH, dummylines >> 8); - stk_sensor_outb(dev, REG_ADVFL, dummylines & 0xff); - msleep(50); - switch (dev->vsettings.mode) { - case MODE_VGA: - if (stk_sensor_set_hw(dev, 302, 1582, 6, 486)) - pr_err("stk_sensor_set_hw failed (VGA)\n"); - break; - case MODE_SXGA: - case MODE_CIF: - case MODE_QVGA: - case MODE_QCIF: - /*FIXME These settings seem ignored by the sensor - if (stk_sensor_set_hw(dev, 220, 1500, 10, 1034)) - pr_err("stk_sensor_set_hw failed (SXGA)\n"); - */ - break; - } - msleep(10); - return 0; -} - -int stk_sensor_set_brightness(struct stk_camera *dev, int br) -{ - if (br < 0 || br > 0xff) - return -EINVAL; - stk_sensor_outb(dev, REG_AEB, max(0x00, br - 6)); - stk_sensor_outb(dev, REG_AEW, min(0xff, br + 6)); - return 0; -} - diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c deleted file mode 100644 index 787edb3d47c2..000000000000 --- a/drivers/media/usb/stkwebcam/stk-webcam.c +++ /dev/null @@ -1,1434 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * stk-webcam.c : Driver for Syntek 1125 USB webcam controller - * - * Copyright (C) 2006 Nicolas VIVIEN - * Copyright 2007-2008 Jaime Velasco Juan <jsagarribay@gmail.com> - * - * Some parts are inspired from cafe_ccic.c - * Copyright 2006-2007 Jonathan Corbet - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/slab.h> - -#include <linux/dmi.h> -#include <linux/usb.h> -#include <linux/mm.h> -#include <linux/vmalloc.h> -#include <linux/videodev2.h> -#include <media/v4l2-common.h> -#include <media/v4l2-ioctl.h> -#include <media/v4l2-event.h> - -#include "stk-webcam.h" - - -static int hflip = -1; -module_param(hflip, int, 0444); -MODULE_PARM_DESC(hflip, "Horizontal image flip (mirror). Defaults to 0"); - -static int vflip = -1; -module_param(vflip, int, 0444); -MODULE_PARM_DESC(vflip, "Vertical image flip. Defaults to 0"); - -static int debug; -module_param(debug, int, 0444); -MODULE_PARM_DESC(debug, "Debug v4l ioctls. Defaults to 0"); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jaime Velasco Juan <jsagarribay@gmail.com> and Nicolas VIVIEN"); -MODULE_DESCRIPTION("Syntek DC1125 webcam driver"); - -/* Some cameras have audio interfaces, we aren't interested in those */ -static const struct usb_device_id stkwebcam_table[] = { - { USB_DEVICE_AND_INTERFACE_INFO(0x174f, 0xa311, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(0x05e1, 0x0501, 0xff, 0xff, 0xff) }, - { } -}; -MODULE_DEVICE_TABLE(usb, stkwebcam_table); - -/* - * The stk webcam laptop module is mounted upside down in some laptops :( - * - * Some background information (thanks to Hans de Goede for providing this): - * - * 1) Once upon a time the stkwebcam driver was written - * - * 2) The webcam in question was used mostly in Asus laptop models, including - * the laptop of the original author of the driver, and in these models, in - * typical Asus fashion (see the long long list for uvc cams inside v4l-utils), - * they mounted the webcam-module the wrong way up. So the hflip and vflip - * module options were given a default value of 1 (the correct value for - * upside down mounted models) - * - * 3) Years later I got a bug report from a user with a laptop with stkwebcam, - * where the module was actually mounted the right way up, and thus showed - * upside down under Linux. So now I was facing the choice of 2 options: - * - * a) Add a not-upside-down list to stkwebcam, which overrules the default. - * - * b) Do it like all the other drivers do, and make the default right for - * cams mounted the proper way and add an upside-down model list, with - * models where we need to flip-by-default. - * - * Despite knowing that going b) would cause a period of pain where we were - * building the table I opted to go for option b), since a) is just too ugly, - * and worse different from how every other driver does it leading to - * confusion in the long run. This change was made in kernel 3.6. - * - * So for any user report about upside-down images since kernel 3.6 ask them - * to provide the output of 'sudo dmidecode' so the laptop can be added in - * the table below. - */ -static const struct dmi_system_id stk_upside_down_dmi_table[] = { - { - .ident = "ASUS G1", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "G1") - } - }, { - .ident = "ASUS F3JC", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "F3JC") - } - }, - { - .ident = "T12Rg-H", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "HCL Infosystems Limited"), - DMI_MATCH(DMI_PRODUCT_NAME, "T12Rg-H") - } - }, - { - .ident = "ASUS A6VM", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "A6VM") - } - }, - { - .ident = "ASUS A6JC", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "A6JC") - } - }, - {} -}; - - -/* - * Basic stuff - */ -int stk_camera_write_reg(struct stk_camera *dev, u16 index, u8 value) -{ - struct usb_device *udev = dev->udev; - int ret; - - ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - 0x01, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, - index, - NULL, - 0, - 500); - if (ret < 0) - return ret; - else - return 0; -} - -int stk_camera_read_reg(struct stk_camera *dev, u16 index, u8 *value) -{ - struct usb_device *udev = dev->udev; - int ret; - - ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), - 0x00, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x00, - index, - &dev->read_reg_scratch, - sizeof(u8), - 500); - if (ret >= 0) - *value = dev->read_reg_scratch; - - if (ret < 0) - return ret; - else - return 0; -} - -static int stk_start_stream(struct stk_camera *dev) -{ - u8 value; - int i, ret; - u8 value_116, value_117; - - - if (!is_present(dev)) - return -ENODEV; - if (!is_memallocd(dev) || !is_initialised(dev)) { - pr_err("FIXME: Buffers are not allocated\n"); - return -EFAULT; - } - ret = usb_set_interface(dev->udev, 0, 5); - - if (ret < 0) - pr_err("usb_set_interface failed !\n"); - if (stk_sensor_wakeup(dev)) - pr_err("error awaking the sensor\n"); - - stk_camera_read_reg(dev, 0x0116, &value_116); - stk_camera_read_reg(dev, 0x0117, &value_117); - - stk_camera_write_reg(dev, 0x0116, 0x0000); - stk_camera_write_reg(dev, 0x0117, 0x0000); - - stk_camera_read_reg(dev, 0x0100, &value); - stk_camera_write_reg(dev, 0x0100, value | 0x80); - - stk_camera_write_reg(dev, 0x0116, value_116); - stk_camera_write_reg(dev, 0x0117, value_117); - for (i = 0; i < MAX_ISO_BUFS; i++) { - if (dev->isobufs[i].urb) { - ret = usb_submit_urb(dev->isobufs[i].urb, GFP_KERNEL); - atomic_inc(&dev->urbs_used); - if (ret) - return ret; - } - } - set_streaming(dev); - return 0; -} - -static int stk_stop_stream(struct stk_camera *dev) -{ - u8 value; - int i; - if (is_present(dev)) { - stk_camera_read_reg(dev, 0x0100, &value); - stk_camera_write_reg(dev, 0x0100, value & ~0x80); - if (dev->isobufs != NULL) { - for (i = 0; i < MAX_ISO_BUFS; i++) { - if (dev->isobufs[i].urb) - usb_kill_urb(dev->isobufs[i].urb); - } - } - unset_streaming(dev); - - if (usb_set_interface(dev->udev, 0, 0)) - pr_err("usb_set_interface failed !\n"); - if (stk_sensor_sleep(dev)) - pr_err("error suspending the sensor\n"); - } - return 0; -} - -/* - * This seems to be the shortest init sequence we - * must do in order to find the sensor - * Bit 5 of reg. 0x0000 here is important, when reset to 0 the sensor - * is also reset. Maybe powers down it? - * Rest of values don't make a difference - */ - -static struct regval stk1125_initvals[] = { - /*TODO: What means this sequence? */ - {0x0000, 0x24}, - {0x0100, 0x21}, - {0x0002, 0x68}, - {0x0003, 0x80}, - {0x0005, 0x00}, - {0x0007, 0x03}, - {0x000d, 0x00}, - {0x000f, 0x02}, - {0x0300, 0x12}, - {0x0350, 0x41}, - {0x0351, 0x00}, - {0x0352, 0x00}, - {0x0353, 0x00}, - {0x0018, 0x10}, - {0x0019, 0x00}, - {0x001b, 0x0e}, - {0x001c, 0x46}, - {0x0300, 0x80}, - {0x001a, 0x04}, - {0x0110, 0x00}, - {0x0111, 0x00}, - {0x0112, 0x00}, - {0x0113, 0x00}, - - {0xffff, 0xff}, -}; - - -static int stk_initialise(struct stk_camera *dev) -{ - struct regval *rv; - int ret; - if (!is_present(dev)) - return -ENODEV; - if (is_initialised(dev)) - return 0; - rv = stk1125_initvals; - while (rv->reg != 0xffff) { - ret = stk_camera_write_reg(dev, rv->reg, rv->val); - if (ret) - return ret; - rv++; - } - if (stk_sensor_init(dev) == 0) { - set_initialised(dev); - return 0; - } else - return -1; -} - -/* *********************************************** */ -/* - * This function is called as an URB transfert is complete (Isochronous pipe). - * So, the traitement is done in interrupt time, so it has be fast, not crash, - * and not stall. Neat. - */ -static void stk_isoc_handler(struct urb *urb) -{ - int i; - int ret; - int framelen; - unsigned long flags; - - unsigned char *fill = NULL; - unsigned char *iso_buf = NULL; - - struct stk_camera *dev; - struct stk_sio_buffer *fb; - - dev = (struct stk_camera *) urb->context; - - if (dev == NULL) { - pr_err("isoc_handler called with NULL device !\n"); - return; - } - - if (urb->status == -ENOENT || urb->status == -ECONNRESET - || urb->status == -ESHUTDOWN) { - atomic_dec(&dev->urbs_used); - return; - } - - spin_lock_irqsave(&dev->spinlock, flags); - - if (urb->status != -EINPROGRESS && urb->status != 0) { - pr_err("isoc_handler: urb->status == %d\n", urb->status); - goto resubmit; - } - - if (list_empty(&dev->sio_avail)) { - /*FIXME Stop streaming after a while */ - pr_err_ratelimited("isoc_handler without available buffer!\n"); - goto resubmit; - } - fb = list_first_entry(&dev->sio_avail, - struct stk_sio_buffer, list); - fill = fb->buffer + fb->v4lbuf.bytesused; - - for (i = 0; i < urb->number_of_packets; i++) { - if (urb->iso_frame_desc[i].status != 0) { - if (urb->iso_frame_desc[i].status != -EXDEV) - pr_err("Frame %d has error %d\n", - i, urb->iso_frame_desc[i].status); - continue; - } - framelen = urb->iso_frame_desc[i].actual_length; - iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - - if (framelen <= 4) - continue; /* no data */ - - /* - * we found something informational from there - * the isoc frames have to type of headers - * type1: 00 xx 00 00 or 20 xx 00 00 - * type2: 80 xx 00 00 00 00 00 00 or a0 xx 00 00 00 00 00 00 - * xx is a sequencer which has never been seen over 0x3f - * imho data written down looks like bayer, i see similarities - * after every 640 bytes - */ - if (*iso_buf & 0x80) { - framelen -= 8; - iso_buf += 8; - /* This marks a new frame */ - if (fb->v4lbuf.bytesused != 0 - && fb->v4lbuf.bytesused != dev->frame_size) { - pr_err_ratelimited("frame %d, bytesused=%d, skipping\n", - i, fb->v4lbuf.bytesused); - fb->v4lbuf.bytesused = 0; - fill = fb->buffer; - } else if (fb->v4lbuf.bytesused == dev->frame_size) { - if (list_is_singular(&dev->sio_avail)) { - /* Always reuse the last buffer */ - fb->v4lbuf.bytesused = 0; - fill = fb->buffer; - } else { - list_move_tail(dev->sio_avail.next, - &dev->sio_full); - wake_up(&dev->wait_frame); - fb = list_first_entry(&dev->sio_avail, - struct stk_sio_buffer, list); - fb->v4lbuf.bytesused = 0; - fill = fb->buffer; - } - } - } else { - framelen -= 4; - iso_buf += 4; - } - - /* Our buffer is full !!! */ - if (framelen + fb->v4lbuf.bytesused > dev->frame_size) { - pr_err_ratelimited("Frame buffer overflow, lost sync\n"); - /*FIXME Do something here? */ - continue; - } - spin_unlock_irqrestore(&dev->spinlock, flags); - memcpy(fill, iso_buf, framelen); - spin_lock_irqsave(&dev->spinlock, flags); - fill += framelen; - - /* New size of our buffer */ - fb->v4lbuf.bytesused += framelen; - } - -resubmit: - spin_unlock_irqrestore(&dev->spinlock, flags); - urb->dev = dev->udev; - ret = usb_submit_urb(urb, GFP_ATOMIC); - if (ret != 0) { - pr_err("Error (%d) re-submitting urb in stk_isoc_handler\n", - ret); - } -} - -/* -------------------------------------------- */ - -static int stk_prepare_iso(struct stk_camera *dev) -{ - void *kbuf; - int i, j; - struct urb *urb; - struct usb_device *udev; - - if (dev == NULL) - return -ENXIO; - udev = dev->udev; - - if (dev->isobufs) - pr_err("isobufs already allocated. Bad\n"); - else - dev->isobufs = kcalloc(MAX_ISO_BUFS, sizeof(*dev->isobufs), - GFP_KERNEL); - if (dev->isobufs == NULL) { - pr_err("Unable to allocate iso buffers\n"); - return -ENOMEM; - } - for (i = 0; i < MAX_ISO_BUFS; i++) { - if (dev->isobufs[i].data == NULL) { - kbuf = kzalloc(ISO_BUFFER_SIZE, GFP_KERNEL); - if (kbuf == NULL) { - pr_err("Failed to allocate iso buffer %d\n", i); - goto isobufs_out; - } - dev->isobufs[i].data = kbuf; - } else - pr_err("isobuf data already allocated\n"); - if (dev->isobufs[i].urb == NULL) { - urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL); - if (urb == NULL) - goto isobufs_out; - dev->isobufs[i].urb = urb; - } else { - pr_err("Killing URB\n"); - usb_kill_urb(dev->isobufs[i].urb); - urb = dev->isobufs[i].urb; - } - urb->interval = 1; - urb->dev = udev; - urb->pipe = usb_rcvisocpipe(udev, dev->isoc_ep); - urb->transfer_flags = URB_ISO_ASAP; - urb->transfer_buffer = dev->isobufs[i].data; - urb->transfer_buffer_length = ISO_BUFFER_SIZE; - urb->complete = stk_isoc_handler; - urb->context = dev; - urb->start_frame = 0; - urb->number_of_packets = ISO_FRAMES_PER_DESC; - - for (j = 0; j < ISO_FRAMES_PER_DESC; j++) { - urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE; - urb->iso_frame_desc[j].length = ISO_MAX_FRAME_SIZE; - } - } - set_memallocd(dev); - return 0; - -isobufs_out: - for (i = 0; i < MAX_ISO_BUFS && dev->isobufs[i].data; i++) - kfree(dev->isobufs[i].data); - for (i = 0; i < MAX_ISO_BUFS && dev->isobufs[i].urb; i++) - usb_free_urb(dev->isobufs[i].urb); - kfree(dev->isobufs); - dev->isobufs = NULL; - return -ENOMEM; -} - -static void stk_clean_iso(struct stk_camera *dev) -{ - int i; - - if (dev == NULL || dev->isobufs == NULL) - return; - - for (i = 0; i < MAX_ISO_BUFS; i++) { - struct urb *urb; - - urb = dev->isobufs[i].urb; - if (urb) { - if (atomic_read(&dev->urbs_used) && is_present(dev)) - usb_kill_urb(urb); - usb_free_urb(urb); - } - kfree(dev->isobufs[i].data); - } - kfree(dev->isobufs); - dev->isobufs = NULL; - unset_memallocd(dev); -} - -static int stk_setup_siobuf(struct stk_camera *dev, int index) -{ - struct stk_sio_buffer *buf = dev->sio_bufs + index; - INIT_LIST_HEAD(&buf->list); - buf->v4lbuf.length = PAGE_ALIGN(dev->frame_size); - buf->buffer = vmalloc_user(buf->v4lbuf.length); - if (buf->buffer == NULL) - return -ENOMEM; - buf->mapcount = 0; - buf->dev = dev; - buf->v4lbuf.index = index; - buf->v4lbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf->v4lbuf.flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - buf->v4lbuf.field = V4L2_FIELD_NONE; - buf->v4lbuf.memory = V4L2_MEMORY_MMAP; - buf->v4lbuf.m.offset = 2*index*buf->v4lbuf.length; - return 0; -} - -static int stk_free_sio_buffers(struct stk_camera *dev) -{ - int i; - int nbufs; - unsigned long flags; - if (dev->n_sbufs == 0 || dev->sio_bufs == NULL) - return 0; - /* - * If any buffers are mapped, we cannot free them at all. - */ - for (i = 0; i < dev->n_sbufs; i++) { - if (dev->sio_bufs[i].mapcount > 0) - return -EBUSY; - } - /* - * OK, let's do it. - */ - spin_lock_irqsave(&dev->spinlock, flags); - INIT_LIST_HEAD(&dev->sio_avail); - INIT_LIST_HEAD(&dev->sio_full); - nbufs = dev->n_sbufs; - dev->n_sbufs = 0; - spin_unlock_irqrestore(&dev->spinlock, flags); - for (i = 0; i < nbufs; i++) - vfree(dev->sio_bufs[i].buffer); - kfree(dev->sio_bufs); - dev->sio_bufs = NULL; - return 0; -} - -static int stk_prepare_sio_buffers(struct stk_camera *dev, unsigned n_sbufs) -{ - int i; - if (dev->sio_bufs != NULL) - pr_err("sio_bufs already allocated\n"); - else { - dev->sio_bufs = kcalloc(n_sbufs, - sizeof(struct stk_sio_buffer), - GFP_KERNEL); - if (dev->sio_bufs == NULL) - return -ENOMEM; - for (i = 0; i < n_sbufs; i++) { - if (stk_setup_siobuf(dev, i)) - return (dev->n_sbufs > 1 ? 0 : -ENOMEM); - dev->n_sbufs = i+1; - } - } - return 0; -} - -static int stk_allocate_buffers(struct stk_camera *dev, unsigned n_sbufs) -{ - int err; - err = stk_prepare_iso(dev); - if (err) { - stk_clean_iso(dev); - return err; - } - err = stk_prepare_sio_buffers(dev, n_sbufs); - if (err) { - stk_free_sio_buffers(dev); - return err; - } - return 0; -} - -static void stk_free_buffers(struct stk_camera *dev) -{ - stk_clean_iso(dev); - stk_free_sio_buffers(dev); -} -/* -------------------------------------------- */ - -/* v4l file operations */ - -static int v4l_stk_open(struct file *fp) -{ - struct stk_camera *dev = video_drvdata(fp); - int err; - - if (dev == NULL || !is_present(dev)) - return -ENXIO; - - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - if (!dev->first_init) - stk_camera_write_reg(dev, 0x0, 0x24); - else - dev->first_init = 0; - - err = v4l2_fh_open(fp); - if (!err) - usb_autopm_get_interface(dev->interface); - mutex_unlock(&dev->lock); - return err; -} - -static int v4l_stk_release(struct file *fp) -{ - struct stk_camera *dev = video_drvdata(fp); - - mutex_lock(&dev->lock); - if (dev->owner == fp) { - stk_stop_stream(dev); - stk_free_buffers(dev); - stk_camera_write_reg(dev, 0x0, 0x49); /* turn off the LED */ - unset_initialised(dev); - dev->owner = NULL; - } - - usb_autopm_put_interface(dev->interface); - mutex_unlock(&dev->lock); - return v4l2_fh_release(fp); -} - -static ssize_t stk_read(struct file *fp, char __user *buf, - size_t count, loff_t *f_pos) -{ - int i; - int ret; - unsigned long flags; - struct stk_sio_buffer *sbuf; - struct stk_camera *dev = video_drvdata(fp); - - if (!is_present(dev)) - return -EIO; - if (dev->owner && (!dev->reading || dev->owner != fp)) - return -EBUSY; - dev->owner = fp; - if (!is_streaming(dev)) { - if (stk_initialise(dev) - || stk_allocate_buffers(dev, 3) - || stk_start_stream(dev)) - return -ENOMEM; - dev->reading = 1; - spin_lock_irqsave(&dev->spinlock, flags); - for (i = 0; i < dev->n_sbufs; i++) { - list_add_tail(&dev->sio_bufs[i].list, &dev->sio_avail); - dev->sio_bufs[i].v4lbuf.flags = V4L2_BUF_FLAG_QUEUED; - } - spin_unlock_irqrestore(&dev->spinlock, flags); - } - if (*f_pos == 0) { - if (fp->f_flags & O_NONBLOCK && list_empty(&dev->sio_full)) - return -EWOULDBLOCK; - ret = wait_event_interruptible(dev->wait_frame, - !list_empty(&dev->sio_full) || !is_present(dev)); - if (ret) - return ret; - if (!is_present(dev)) - return -EIO; - } - if (count + *f_pos > dev->frame_size) - count = dev->frame_size - *f_pos; - spin_lock_irqsave(&dev->spinlock, flags); - if (list_empty(&dev->sio_full)) { - spin_unlock_irqrestore(&dev->spinlock, flags); - pr_err("BUG: No siobufs ready\n"); - return 0; - } - sbuf = list_first_entry(&dev->sio_full, struct stk_sio_buffer, list); - spin_unlock_irqrestore(&dev->spinlock, flags); - - if (copy_to_user(buf, sbuf->buffer + *f_pos, count)) - return -EFAULT; - - *f_pos += count; - - if (*f_pos >= dev->frame_size) { - *f_pos = 0; - spin_lock_irqsave(&dev->spinlock, flags); - list_move_tail(&sbuf->list, &dev->sio_avail); - spin_unlock_irqrestore(&dev->spinlock, flags); - } - return count; -} - -static ssize_t v4l_stk_read(struct file *fp, char __user *buf, - size_t count, loff_t *f_pos) -{ - struct stk_camera *dev = video_drvdata(fp); - int ret; - - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - ret = stk_read(fp, buf, count, f_pos); - mutex_unlock(&dev->lock); - return ret; -} - -static __poll_t v4l_stk_poll(struct file *fp, poll_table *wait) -{ - struct stk_camera *dev = video_drvdata(fp); - __poll_t res = v4l2_ctrl_poll(fp, wait); - - poll_wait(fp, &dev->wait_frame, wait); - - if (!is_present(dev)) - return EPOLLERR; - - if (!list_empty(&dev->sio_full)) - return res | EPOLLIN | EPOLLRDNORM; - - return res; -} - - -static void stk_v4l_vm_open(struct vm_area_struct *vma) -{ - struct stk_sio_buffer *sbuf = vma->vm_private_data; - sbuf->mapcount++; -} -static void stk_v4l_vm_close(struct vm_area_struct *vma) -{ - struct stk_sio_buffer *sbuf = vma->vm_private_data; - sbuf->mapcount--; - if (sbuf->mapcount == 0) - sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_MAPPED; -} -static const struct vm_operations_struct stk_v4l_vm_ops = { - .open = stk_v4l_vm_open, - .close = stk_v4l_vm_close -}; - -static int v4l_stk_mmap(struct file *fp, struct vm_area_struct *vma) -{ - unsigned int i; - int ret; - unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; - struct stk_camera *dev = video_drvdata(fp); - struct stk_sio_buffer *sbuf = NULL; - - if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED)) - return -EINVAL; - - for (i = 0; i < dev->n_sbufs; i++) { - if (dev->sio_bufs[i].v4lbuf.m.offset == offset) { - sbuf = dev->sio_bufs + i; - break; - } - } - if (sbuf == NULL) - return -EINVAL; - ret = remap_vmalloc_range(vma, sbuf->buffer, 0); - if (ret) - return ret; - vma->vm_flags |= VM_DONTEXPAND; - vma->vm_private_data = sbuf; - vma->vm_ops = &stk_v4l_vm_ops; - sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_MAPPED; - stk_v4l_vm_open(vma); - return 0; -} - -/* v4l ioctl handlers */ - -static int stk_vidioc_querycap(struct file *filp, - void *priv, struct v4l2_capability *cap) -{ - struct stk_camera *dev = video_drvdata(filp); - - strscpy(cap->driver, "stk", sizeof(cap->driver)); - strscpy(cap->card, "stk", sizeof(cap->card)); - usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - return 0; -} - -static int stk_vidioc_enum_input(struct file *filp, - void *priv, struct v4l2_input *input) -{ - if (input->index != 0) - return -EINVAL; - - strscpy(input->name, "Syntek USB Camera", sizeof(input->name)); - input->type = V4L2_INPUT_TYPE_CAMERA; - return 0; -} - - -static int stk_vidioc_g_input(struct file *filp, void *priv, unsigned int *i) -{ - *i = 0; - return 0; -} - -static int stk_vidioc_s_input(struct file *filp, void *priv, unsigned int i) -{ - return i ? -EINVAL : 0; -} - -static int stk_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct stk_camera *dev = - container_of(ctrl->handler, struct stk_camera, hdl); - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - return stk_sensor_set_brightness(dev, ctrl->val); - case V4L2_CID_HFLIP: - if (dmi_check_system(stk_upside_down_dmi_table)) - dev->vsettings.hflip = !ctrl->val; - else - dev->vsettings.hflip = ctrl->val; - return 0; - case V4L2_CID_VFLIP: - if (dmi_check_system(stk_upside_down_dmi_table)) - dev->vsettings.vflip = !ctrl->val; - else - dev->vsettings.vflip = ctrl->val; - return 0; - default: - return -EINVAL; - } - return 0; -} - - -static int stk_vidioc_enum_fmt_vid_cap(struct file *filp, - void *priv, struct v4l2_fmtdesc *fmtd) -{ - switch (fmtd->index) { - case 0: - fmtd->pixelformat = V4L2_PIX_FMT_RGB565; - break; - case 1: - fmtd->pixelformat = V4L2_PIX_FMT_RGB565X; - break; - case 2: - fmtd->pixelformat = V4L2_PIX_FMT_UYVY; - break; - case 3: - fmtd->pixelformat = V4L2_PIX_FMT_SBGGR8; - break; - case 4: - fmtd->pixelformat = V4L2_PIX_FMT_YUYV; - break; - default: - return -EINVAL; - } - return 0; -} - -static struct stk_size { - unsigned w; - unsigned h; - enum stk_mode m; -} stk_sizes[] = { - { .w = 1280, .h = 1024, .m = MODE_SXGA, }, - { .w = 640, .h = 480, .m = MODE_VGA, }, - { .w = 352, .h = 288, .m = MODE_CIF, }, - { .w = 320, .h = 240, .m = MODE_QVGA, }, - { .w = 176, .h = 144, .m = MODE_QCIF, }, -}; - -static int stk_vidioc_g_fmt_vid_cap(struct file *filp, - void *priv, struct v4l2_format *f) -{ - struct v4l2_pix_format *pix_format = &f->fmt.pix; - struct stk_camera *dev = video_drvdata(filp); - int i; - - for (i = 0; i < ARRAY_SIZE(stk_sizes) && - stk_sizes[i].m != dev->vsettings.mode; i++) - ; - if (i == ARRAY_SIZE(stk_sizes)) { - pr_err("ERROR: mode invalid\n"); - return -EINVAL; - } - pix_format->width = stk_sizes[i].w; - pix_format->height = stk_sizes[i].h; - pix_format->field = V4L2_FIELD_NONE; - pix_format->colorspace = V4L2_COLORSPACE_SRGB; - pix_format->pixelformat = dev->vsettings.palette; - if (dev->vsettings.palette == V4L2_PIX_FMT_SBGGR8) - pix_format->bytesperline = pix_format->width; - else - pix_format->bytesperline = 2 * pix_format->width; - pix_format->sizeimage = pix_format->bytesperline - * pix_format->height; - return 0; -} - -static int stk_try_fmt_vid_cap(struct file *filp, - struct v4l2_format *fmtd, int *idx) -{ - int i; - switch (fmtd->fmt.pix.pixelformat) { - case V4L2_PIX_FMT_RGB565: - case V4L2_PIX_FMT_RGB565X: - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_YUYV: - case V4L2_PIX_FMT_SBGGR8: - break; - default: - return -EINVAL; - } - for (i = 1; i < ARRAY_SIZE(stk_sizes); i++) { - if (fmtd->fmt.pix.width > stk_sizes[i].w) - break; - } - if (i == ARRAY_SIZE(stk_sizes) - || (abs(fmtd->fmt.pix.width - stk_sizes[i-1].w) - < abs(fmtd->fmt.pix.width - stk_sizes[i].w))) { - fmtd->fmt.pix.height = stk_sizes[i-1].h; - fmtd->fmt.pix.width = stk_sizes[i-1].w; - if (idx) - *idx = i - 1; - } else { - fmtd->fmt.pix.height = stk_sizes[i].h; - fmtd->fmt.pix.width = stk_sizes[i].w; - if (idx) - *idx = i; - } - - fmtd->fmt.pix.field = V4L2_FIELD_NONE; - fmtd->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; - if (fmtd->fmt.pix.pixelformat == V4L2_PIX_FMT_SBGGR8) - fmtd->fmt.pix.bytesperline = fmtd->fmt.pix.width; - else - fmtd->fmt.pix.bytesperline = 2 * fmtd->fmt.pix.width; - fmtd->fmt.pix.sizeimage = fmtd->fmt.pix.bytesperline - * fmtd->fmt.pix.height; - return 0; -} - -static int stk_vidioc_try_fmt_vid_cap(struct file *filp, - void *priv, struct v4l2_format *fmtd) -{ - return stk_try_fmt_vid_cap(filp, fmtd, NULL); -} - -static int stk_setup_format(struct stk_camera *dev) -{ - int i = 0; - int depth; - if (dev->vsettings.palette == V4L2_PIX_FMT_SBGGR8) - depth = 1; - else - depth = 2; - while (i < ARRAY_SIZE(stk_sizes) && - stk_sizes[i].m != dev->vsettings.mode) - i++; - if (i == ARRAY_SIZE(stk_sizes)) { - pr_err("Something is broken in %s\n", __func__); - return -EFAULT; - } - /* This registers controls some timings, not sure of what. */ - stk_camera_write_reg(dev, 0x001b, 0x0e); - if (dev->vsettings.mode == MODE_SXGA) - stk_camera_write_reg(dev, 0x001c, 0x0e); - else - stk_camera_write_reg(dev, 0x001c, 0x46); - /* - * Registers 0x0115 0x0114 are the size of each line (bytes), - * regs 0x0117 0x0116 are the height of the image. - */ - stk_camera_write_reg(dev, 0x0115, - ((stk_sizes[i].w * depth) >> 8) & 0xff); - stk_camera_write_reg(dev, 0x0114, - (stk_sizes[i].w * depth) & 0xff); - stk_camera_write_reg(dev, 0x0117, - (stk_sizes[i].h >> 8) & 0xff); - stk_camera_write_reg(dev, 0x0116, - stk_sizes[i].h & 0xff); - return stk_sensor_configure(dev); -} - -static int stk_vidioc_s_fmt_vid_cap(struct file *filp, - void *priv, struct v4l2_format *fmtd) -{ - int ret; - int idx; - struct stk_camera *dev = video_drvdata(filp); - - if (dev == NULL) - return -ENODEV; - if (!is_present(dev)) - return -ENODEV; - if (is_streaming(dev)) - return -EBUSY; - if (dev->owner) - return -EBUSY; - ret = stk_try_fmt_vid_cap(filp, fmtd, &idx); - if (ret) - return ret; - - dev->vsettings.palette = fmtd->fmt.pix.pixelformat; - stk_free_buffers(dev); - dev->frame_size = fmtd->fmt.pix.sizeimage; - dev->vsettings.mode = stk_sizes[idx].m; - - stk_initialise(dev); - return stk_setup_format(dev); -} - -static int stk_vidioc_reqbufs(struct file *filp, - void *priv, struct v4l2_requestbuffers *rb) -{ - struct stk_camera *dev = video_drvdata(filp); - - if (dev == NULL) - return -ENODEV; - if (rb->memory != V4L2_MEMORY_MMAP) - return -EINVAL; - if (is_streaming(dev) - || (dev->owner && dev->owner != filp)) - return -EBUSY; - stk_free_buffers(dev); - if (rb->count == 0) { - stk_camera_write_reg(dev, 0x0, 0x49); /* turn off the LED */ - unset_initialised(dev); - dev->owner = NULL; - return 0; - } - dev->owner = filp; - - /*FIXME If they ask for zero, we must stop streaming and free */ - if (rb->count < 3) - rb->count = 3; - /* Arbitrary limit */ - else if (rb->count > 5) - rb->count = 5; - - stk_allocate_buffers(dev, rb->count); - rb->count = dev->n_sbufs; - return 0; -} - -static int stk_vidioc_querybuf(struct file *filp, - void *priv, struct v4l2_buffer *buf) -{ - struct stk_camera *dev = video_drvdata(filp); - struct stk_sio_buffer *sbuf; - - if (buf->index >= dev->n_sbufs) - return -EINVAL; - sbuf = dev->sio_bufs + buf->index; - *buf = sbuf->v4lbuf; - return 0; -} - -static int stk_vidioc_qbuf(struct file *filp, - void *priv, struct v4l2_buffer *buf) -{ - struct stk_camera *dev = video_drvdata(filp); - struct stk_sio_buffer *sbuf; - unsigned long flags; - - if (buf->memory != V4L2_MEMORY_MMAP) - return -EINVAL; - - if (buf->index >= dev->n_sbufs) - return -EINVAL; - sbuf = dev->sio_bufs + buf->index; - if (sbuf->v4lbuf.flags & V4L2_BUF_FLAG_QUEUED) - return 0; - sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_QUEUED; - sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_DONE; - spin_lock_irqsave(&dev->spinlock, flags); - list_add_tail(&sbuf->list, &dev->sio_avail); - *buf = sbuf->v4lbuf; - spin_unlock_irqrestore(&dev->spinlock, flags); - return 0; -} - -static int stk_vidioc_dqbuf(struct file *filp, - void *priv, struct v4l2_buffer *buf) -{ - struct stk_camera *dev = video_drvdata(filp); - struct stk_sio_buffer *sbuf; - unsigned long flags; - int ret; - - if (!is_streaming(dev)) - return -EINVAL; - - if (filp->f_flags & O_NONBLOCK && list_empty(&dev->sio_full)) - return -EWOULDBLOCK; - ret = wait_event_interruptible(dev->wait_frame, - !list_empty(&dev->sio_full) || !is_present(dev)); - if (ret) - return ret; - if (!is_present(dev)) - return -EIO; - - spin_lock_irqsave(&dev->spinlock, flags); - sbuf = list_first_entry(&dev->sio_full, struct stk_sio_buffer, list); - list_del_init(&sbuf->list); - spin_unlock_irqrestore(&dev->spinlock, flags); - sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_QUEUED; - sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_DONE; - sbuf->v4lbuf.sequence = ++dev->sequence; - v4l2_buffer_set_timestamp(&sbuf->v4lbuf, ktime_get_ns()); - - *buf = sbuf->v4lbuf; - return 0; -} - -static int stk_vidioc_streamon(struct file *filp, - void *priv, enum v4l2_buf_type type) -{ - struct stk_camera *dev = video_drvdata(filp); - if (is_streaming(dev)) - return 0; - if (dev->sio_bufs == NULL) - return -EINVAL; - dev->sequence = 0; - return stk_start_stream(dev); -} - -static int stk_vidioc_streamoff(struct file *filp, - void *priv, enum v4l2_buf_type type) -{ - struct stk_camera *dev = video_drvdata(filp); - unsigned long flags; - int i; - stk_stop_stream(dev); - spin_lock_irqsave(&dev->spinlock, flags); - INIT_LIST_HEAD(&dev->sio_avail); - INIT_LIST_HEAD(&dev->sio_full); - for (i = 0; i < dev->n_sbufs; i++) { - INIT_LIST_HEAD(&dev->sio_bufs[i].list); - dev->sio_bufs[i].v4lbuf.flags = 0; - } - spin_unlock_irqrestore(&dev->spinlock, flags); - return 0; -} - - -static int stk_vidioc_g_parm(struct file *filp, - void *priv, struct v4l2_streamparm *sp) -{ - /*FIXME This is not correct */ - sp->parm.capture.timeperframe.numerator = 1; - sp->parm.capture.timeperframe.denominator = 30; - sp->parm.capture.readbuffers = 2; - return 0; -} - -static int stk_vidioc_enum_framesizes(struct file *filp, - void *priv, struct v4l2_frmsizeenum *frms) -{ - if (frms->index >= ARRAY_SIZE(stk_sizes)) - return -EINVAL; - switch (frms->pixel_format) { - case V4L2_PIX_FMT_RGB565: - case V4L2_PIX_FMT_RGB565X: - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_YUYV: - case V4L2_PIX_FMT_SBGGR8: - frms->type = V4L2_FRMSIZE_TYPE_DISCRETE; - frms->discrete.width = stk_sizes[frms->index].w; - frms->discrete.height = stk_sizes[frms->index].h; - return 0; - default: return -EINVAL; - } -} - -static const struct v4l2_ctrl_ops stk_ctrl_ops = { - .s_ctrl = stk_s_ctrl, -}; - -static const struct v4l2_file_operations v4l_stk_fops = { - .owner = THIS_MODULE, - .open = v4l_stk_open, - .release = v4l_stk_release, - .read = v4l_stk_read, - .poll = v4l_stk_poll, - .mmap = v4l_stk_mmap, - .unlocked_ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops v4l_stk_ioctl_ops = { - .vidioc_querycap = stk_vidioc_querycap, - .vidioc_enum_fmt_vid_cap = stk_vidioc_enum_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = stk_vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = stk_vidioc_s_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = stk_vidioc_g_fmt_vid_cap, - .vidioc_enum_input = stk_vidioc_enum_input, - .vidioc_s_input = stk_vidioc_s_input, - .vidioc_g_input = stk_vidioc_g_input, - .vidioc_reqbufs = stk_vidioc_reqbufs, - .vidioc_querybuf = stk_vidioc_querybuf, - .vidioc_qbuf = stk_vidioc_qbuf, - .vidioc_dqbuf = stk_vidioc_dqbuf, - .vidioc_streamon = stk_vidioc_streamon, - .vidioc_streamoff = stk_vidioc_streamoff, - .vidioc_g_parm = stk_vidioc_g_parm, - .vidioc_enum_framesizes = stk_vidioc_enum_framesizes, - .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static void stk_v4l_dev_release(struct video_device *vd) -{ - struct stk_camera *dev = vdev_to_camera(vd); - - if (dev->sio_bufs != NULL || dev->isobufs != NULL) - pr_err("We are leaking memory\n"); - usb_put_intf(dev->interface); - usb_put_dev(dev->udev); - - v4l2_ctrl_handler_free(&dev->hdl); - v4l2_device_unregister(&dev->v4l2_dev); - kfree(dev); -} - -static const struct video_device stk_v4l_data = { - .name = "stkwebcam", - .fops = &v4l_stk_fops, - .ioctl_ops = &v4l_stk_ioctl_ops, - .release = stk_v4l_dev_release, -}; - - -static int stk_register_video_device(struct stk_camera *dev) -{ - int err; - - dev->vdev = stk_v4l_data; - dev->vdev.lock = &dev->lock; - dev->vdev.v4l2_dev = &dev->v4l2_dev; - dev->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING; - video_set_drvdata(&dev->vdev, dev); - err = video_register_device(&dev->vdev, VFL_TYPE_VIDEO, -1); - if (err) - pr_err("v4l registration failed\n"); - else - pr_info("Syntek USB2.0 Camera is now controlling device %s\n", - video_device_node_name(&dev->vdev)); - return err; -} - - -/* USB Stuff */ - -static int stk_camera_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - struct v4l2_ctrl_handler *hdl; - int err = 0; - int i; - - struct stk_camera *dev = NULL; - struct usb_device *udev = interface_to_usbdev(interface); - struct usb_host_interface *iface_desc; - struct usb_endpoint_descriptor *endpoint; - - dev = kzalloc(sizeof(struct stk_camera), GFP_KERNEL); - if (dev == NULL) { - pr_err("Out of memory !\n"); - return -ENOMEM; - } - err = v4l2_device_register(&interface->dev, &dev->v4l2_dev); - if (err < 0) { - dev_err(&udev->dev, "couldn't register v4l2_device\n"); - kfree(dev); - return err; - } - hdl = &dev->hdl; - v4l2_ctrl_handler_init(hdl, 3); - v4l2_ctrl_new_std(hdl, &stk_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 0xff, 0x1, 0x60); - v4l2_ctrl_new_std(hdl, &stk_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 1); - v4l2_ctrl_new_std(hdl, &stk_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 1); - if (hdl->error) { - err = hdl->error; - dev_err(&udev->dev, "couldn't register control\n"); - goto error; - } - dev->v4l2_dev.ctrl_handler = hdl; - - spin_lock_init(&dev->spinlock); - mutex_init(&dev->lock); - init_waitqueue_head(&dev->wait_frame); - dev->first_init = 1; /* webcam LED management */ - - dev->udev = usb_get_dev(udev); - dev->interface = interface; - usb_get_intf(interface); - - if (hflip != -1) - dev->vsettings.hflip = hflip; - else if (dmi_check_system(stk_upside_down_dmi_table)) - dev->vsettings.hflip = 1; - else - dev->vsettings.hflip = 0; - if (vflip != -1) - dev->vsettings.vflip = vflip; - else if (dmi_check_system(stk_upside_down_dmi_table)) - dev->vsettings.vflip = 1; - else - dev->vsettings.vflip = 0; - dev->n_sbufs = 0; - set_present(dev); - - /* Set up the endpoint information - * use only the first isoc-in endpoint - * for the current alternate setting */ - iface_desc = interface->cur_altsetting; - - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - endpoint = &iface_desc->endpoint[i].desc; - - if (!dev->isoc_ep - && usb_endpoint_is_isoc_in(endpoint)) { - /* we found an isoc in endpoint */ - dev->isoc_ep = usb_endpoint_num(endpoint); - break; - } - } - if (!dev->isoc_ep) { - pr_err("Could not find isoc-in endpoint\n"); - err = -ENODEV; - goto error_put; - } - dev->vsettings.palette = V4L2_PIX_FMT_RGB565; - dev->vsettings.mode = MODE_VGA; - dev->frame_size = 640 * 480 * 2; - - INIT_LIST_HEAD(&dev->sio_avail); - INIT_LIST_HEAD(&dev->sio_full); - - usb_set_intfdata(interface, dev); - - err = stk_register_video_device(dev); - if (err) - goto error_put; - - return 0; - -error_put: - usb_put_intf(interface); - usb_put_dev(dev->udev); -error: - v4l2_ctrl_handler_free(hdl); - v4l2_device_unregister(&dev->v4l2_dev); - kfree(dev); - return err; -} - -static void stk_camera_disconnect(struct usb_interface *interface) -{ - struct stk_camera *dev = usb_get_intfdata(interface); - - usb_set_intfdata(interface, NULL); - unset_present(dev); - - wake_up_interruptible(&dev->wait_frame); - - pr_info("Syntek USB2.0 Camera release resources device %s\n", - video_device_node_name(&dev->vdev)); - - video_unregister_device(&dev->vdev); -} - -#ifdef CONFIG_PM -static int stk_camera_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct stk_camera *dev = usb_get_intfdata(intf); - if (is_streaming(dev)) { - stk_stop_stream(dev); - /* yes, this is ugly */ - set_streaming(dev); - } - return 0; -} - -static int stk_camera_resume(struct usb_interface *intf) -{ - struct stk_camera *dev = usb_get_intfdata(intf); - if (!is_initialised(dev)) - return 0; - unset_initialised(dev); - stk_initialise(dev); - stk_camera_write_reg(dev, 0x0, 0x49); - stk_setup_format(dev); - if (is_streaming(dev)) - stk_start_stream(dev); - return 0; -} -#endif - -static struct usb_driver stk_camera_driver = { - .name = "stkwebcam", - .probe = stk_camera_probe, - .disconnect = stk_camera_disconnect, - .id_table = stkwebcam_table, -#ifdef CONFIG_PM - .suspend = stk_camera_suspend, - .resume = stk_camera_resume, -#endif -}; - -module_usb_driver(stk_camera_driver); diff --git a/drivers/media/usb/stkwebcam/stk-webcam.h b/drivers/media/usb/stkwebcam/stk-webcam.h deleted file mode 100644 index 136decffe9ce..000000000000 --- a/drivers/media/usb/stkwebcam/stk-webcam.h +++ /dev/null @@ -1,123 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * stk-webcam.h : Driver for Syntek 1125 USB webcam controller - * - * Copyright (C) 2006 Nicolas VIVIEN - * Copyright 2007-2008 Jaime Velasco Juan <jsagarribay@gmail.com> - */ - -#ifndef STKWEBCAM_H -#define STKWEBCAM_H - -#include <linux/usb.h> -#include <media/v4l2-device.h> -#include <media/v4l2-ctrls.h> -#include <media/v4l2-common.h> - -#define DRIVER_VERSION "v0.0.1" -#define DRIVER_VERSION_NUM 0x000001 - -#define MAX_ISO_BUFS 3 -#define ISO_FRAMES_PER_DESC 16 -#define ISO_MAX_FRAME_SIZE 3 * 1024 -#define ISO_BUFFER_SIZE (ISO_FRAMES_PER_DESC * ISO_MAX_FRAME_SIZE) - -struct stk_iso_buf { - void *data; - int length; - int read; - struct urb *urb; -}; - -/* Streaming IO buffers */ -struct stk_sio_buffer { - struct v4l2_buffer v4lbuf; - char *buffer; - int mapcount; - struct stk_camera *dev; - struct list_head list; -}; - -enum stk_mode {MODE_VGA, MODE_SXGA, MODE_CIF, MODE_QVGA, MODE_QCIF}; - -struct stk_video { - enum stk_mode mode; - __u32 palette; - int hflip; - int vflip; -}; - -enum stk_status { - S_PRESENT = 1, - S_INITIALISED = 2, - S_MEMALLOCD = 4, - S_STREAMING = 8, -}; -#define is_present(dev) ((dev)->status & S_PRESENT) -#define is_initialised(dev) ((dev)->status & S_INITIALISED) -#define is_streaming(dev) ((dev)->status & S_STREAMING) -#define is_memallocd(dev) ((dev)->status & S_MEMALLOCD) -#define set_present(dev) ((dev)->status = S_PRESENT) -#define unset_present(dev) ((dev)->status &= \ - ~(S_PRESENT|S_INITIALISED|S_STREAMING)) -#define set_initialised(dev) ((dev)->status |= S_INITIALISED) -#define unset_initialised(dev) ((dev)->status &= ~S_INITIALISED) -#define set_memallocd(dev) ((dev)->status |= S_MEMALLOCD) -#define unset_memallocd(dev) ((dev)->status &= ~S_MEMALLOCD) -#define set_streaming(dev) ((dev)->status |= S_STREAMING) -#define unset_streaming(dev) ((dev)->status &= ~S_STREAMING) - -struct regval { - unsigned reg; - unsigned val; -}; - -struct stk_camera { - struct v4l2_device v4l2_dev; - struct v4l2_ctrl_handler hdl; - struct video_device vdev; - struct usb_device *udev; - struct usb_interface *interface; - int webcam_model; - struct file *owner; - struct mutex lock; - int first_init; - - u8 isoc_ep; - - /* Not sure if this is right */ - atomic_t urbs_used; - - struct stk_video vsettings; - - enum stk_status status; - - spinlock_t spinlock; - wait_queue_head_t wait_frame; - - struct stk_iso_buf *isobufs; - - int frame_size; - /* Streaming buffers */ - int reading; - unsigned int n_sbufs; - struct stk_sio_buffer *sio_bufs; - struct list_head sio_avail; - struct list_head sio_full; - unsigned sequence; - - u8 read_reg_scratch; -}; - -#define vdev_to_camera(d) container_of(d, struct stk_camera, vdev) - -int stk_camera_write_reg(struct stk_camera *, u16, u8); -int stk_camera_read_reg(struct stk_camera *, u16, u8 *); - -int stk_sensor_init(struct stk_camera *); -int stk_sensor_configure(struct stk_camera *); -int stk_sensor_sleep(struct stk_camera *dev); -int stk_sensor_wakeup(struct stk_camera *dev); -int stk_sensor_set_brightness(struct stk_camera *dev, int br); - -#endif diff --git a/drivers/media/usb/usbtv/usbtv-video.c b/drivers/media/usb/usbtv/usbtv-video.c index a714ad77ca8e..1e30e05953dc 100644 --- a/drivers/media/usb/usbtv/usbtv-video.c +++ b/drivers/media/usb/usbtv/usbtv-video.c @@ -136,6 +136,8 @@ static uint16_t usbtv_norm_to_16f_reg(v4l2_std_id norm) return 0x00a8; if (norm & (V4L2_STD_PAL_M | V4L2_STD_PAL_60)) return 0x00bc; + if (norm & V4L2_STD_PAL_Nc) + return 0x00fe; /* Fallback to automatic detection for other standards */ return 0x0000; } @@ -241,7 +243,8 @@ static int usbtv_select_norm(struct usbtv *usbtv, v4l2_std_id norm) static const v4l2_std_id ntsc_mask = V4L2_STD_NTSC | V4L2_STD_NTSC_443; static const v4l2_std_id pal_mask = - V4L2_STD_PAL | V4L2_STD_PAL_60 | V4L2_STD_PAL_M; + V4L2_STD_PAL | V4L2_STD_PAL_60 | V4L2_STD_PAL_M | + V4L2_STD_PAL_Nc; if (norm & ntsc_mask) ret = usbtv_set_regs(usbtv, ntsc, ARRAY_SIZE(ntsc)); diff --git a/drivers/media/usb/usbtv/usbtv.h b/drivers/media/usb/usbtv/usbtv.h index 77a368e90fd0..b9fa7c0088c9 100644 --- a/drivers/media/usb/usbtv/usbtv.h +++ b/drivers/media/usb/usbtv/usbtv.h @@ -68,7 +68,8 @@ #define USBTV_ODD(chunk) ((be32_to_cpu(chunk[0]) & 0x0000f000) >> 15) #define USBTV_CHUNK_NO(chunk) (be32_to_cpu(chunk[0]) & 0x00000fff) -#define USBTV_TV_STD (V4L2_STD_525_60 | V4L2_STD_PAL | V4L2_STD_SECAM) +#define USBTV_TV_STD (V4L2_STD_525_60 | V4L2_STD_PAL | \ + V4L2_STD_PAL_Nc | V4L2_STD_SECAM) /* parameters for supported TV norms */ struct usbtv_norm_params { diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index 0e78233fc8a0..8c208db9600b 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -366,6 +366,7 @@ static const struct uvc_menu_info power_line_frequency_controls[] = { { 0, "Disabled" }, { 1, "50 Hz" }, { 2, "60 Hz" }, + { 3, "Auto" }, }; static const struct uvc_menu_info exposure_auto_controls[] = { @@ -505,17 +506,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = { .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, }, { - .id = V4L2_CID_POWER_LINE_FREQUENCY, - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL, - .size = 2, - .offset = 0, - .v4l2_type = V4L2_CTRL_TYPE_MENU, - .data_type = UVC_CTRL_DATA_TYPE_ENUM, - .menu_info = power_line_frequency_controls, - .menu_count = ARRAY_SIZE(power_line_frequency_controls), - }, - { .id = V4L2_CID_HUE_AUTO, .entity = UVC_GUID_UVC_PROCESSING, .selector = UVC_PU_HUE_AUTO_CONTROL, @@ -730,6 +720,34 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = { }, }; +static const struct uvc_control_mapping uvc_ctrl_mappings_uvc11[] = { + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL, + .size = 2, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_MENU, + .data_type = UVC_CTRL_DATA_TYPE_ENUM, + .menu_info = power_line_frequency_controls, + .menu_count = ARRAY_SIZE(power_line_frequency_controls) - 1, + }, +}; + +static const struct uvc_control_mapping uvc_ctrl_mappings_uvc15[] = { + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL, + .size = 2, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_MENU, + .data_type = UVC_CTRL_DATA_TYPE_ENUM, + .menu_info = power_line_frequency_controls, + .menu_count = ARRAY_SIZE(power_line_frequency_controls), + }, +}; + /* ------------------------------------------------------------------------ * Utility functions */ @@ -749,7 +767,8 @@ static inline void uvc_clear_bit(u8 *data, int bit) data[bit >> 3] &= ~(1 << (bit & 7)); } -/* Extract the bit string specified by mapping->offset and mapping->size +/* + * Extract the bit string specified by mapping->offset and mapping->size * from the little-endian data stored at 'data' and return the result as * a signed 32bit integer. Sign extension will be performed if the mapping * references a signed data type. @@ -785,7 +804,8 @@ static s32 uvc_get_le_value(struct uvc_control_mapping *mapping, return value; } -/* Set the bit string specified by mapping->offset and mapping->size +/* + * Set the bit string specified by mapping->offset and mapping->size * in the little-endian data stored at 'data' to the value 'value'. */ static void uvc_set_le_value(struct uvc_control_mapping *mapping, @@ -795,7 +815,8 @@ static void uvc_set_le_value(struct uvc_control_mapping *mapping, int offset = mapping->offset; u8 mask; - /* According to the v4l2 spec, writing any value to a button control + /* + * According to the v4l2 spec, writing any value to a button control * should result in the action belonging to the button control being * triggered. UVC devices however want to see a 1 written -> override * value. @@ -927,7 +948,8 @@ static int uvc_ctrl_populate_cache(struct uvc_video_chain *chain, UVC_VC_EXTENSION_UNIT) return ret; - /* GET_RES is mandatory for XU controls, but some + /* + * GET_RES is mandatory for XU controls, but some * cameras still choke on it. Ignore errors and set the * resolution value to zero. */ @@ -1522,8 +1544,10 @@ static int uvc_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems) uvc_ctrl_fill_event(handle->chain, &ev, ctrl, mapping, val, changes); - /* Mark the queue as active, allowing this initial - event to be accepted. */ + /* + * Mark the queue as active, allowing this initial event to be + * accepted. + */ sev->elems = elems; v4l2_event_queue_fh(sev->fh, &ev); } @@ -1596,7 +1620,8 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev, if (!ctrl->initialized) continue; - /* Reset the loaded flag for auto-update controls that were + /* + * Reset the loaded flag for auto-update controls that were * marked as loaded in uvc_ctrl_get/uvc_ctrl_set to prevent * uvc_ctrl_get from using the cached value, and for write-only * controls to prevent uvc_ctrl_set from setting bits not @@ -1755,7 +1780,8 @@ int uvc_ctrl_set(struct uvc_fh *handle, return -ERANGE; value = mapping->menu_info[xctrl->value].value; - /* Valid menu indices are reported by the GET_RES request for + /* + * Valid menu indices are reported by the GET_RES request for * UVC controls that support it. */ if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK && @@ -1779,7 +1805,8 @@ int uvc_ctrl_set(struct uvc_fh *handle, break; } - /* If the mapping doesn't span the whole UVC control, the current value + /* + * If the mapping doesn't span the whole UVC control, the current value * needs to be loaded from the device to perform the read-modify-write * operation. */ @@ -2180,7 +2207,8 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain, unsigned int size; unsigned int i; - /* Most mappings come from static kernel data and need to be duplicated. + /* + * Most mappings come from static kernel data and need to be duplicated. * Mappings that come from userspace will be unnecessarily duplicated, * this could be optimized. */ @@ -2385,11 +2413,11 @@ static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain, { const struct uvc_control_info *info = uvc_ctrls; const struct uvc_control_info *iend = info + ARRAY_SIZE(uvc_ctrls); - const struct uvc_control_mapping *mapping = uvc_ctrl_mappings; - const struct uvc_control_mapping *mend = - mapping + ARRAY_SIZE(uvc_ctrl_mappings); + const struct uvc_control_mapping *mapping; + const struct uvc_control_mapping *mend; - /* XU controls initialization requires querying the device for control + /* + * XU controls initialization requires querying the device for control * information. As some buggy UVC devices will crash when queried * repeatedly in a tight loop, delay XU controls initialization until * first use. @@ -2415,6 +2443,48 @@ static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain, if (!ctrl->initialized) return; + /* + * First check if the device provides a custom mapping for this control, + * used to override standard mappings for non-conformant devices. Don't + * process standard mappings if a custom mapping is found. This + * mechanism doesn't support combining standard and custom mappings for + * a single control. + */ + if (chain->dev->info->mappings) { + bool custom = false; + unsigned int i; + + for (i = 0; (mapping = chain->dev->info->mappings[i]); ++i) { + if (uvc_entity_match_guid(ctrl->entity, mapping->entity) && + ctrl->info.selector == mapping->selector) { + __uvc_ctrl_add_mapping(chain, ctrl, mapping); + custom = true; + } + } + + if (custom) + return; + } + + /* Process common mappings next. */ + mapping = uvc_ctrl_mappings; + mend = mapping + ARRAY_SIZE(uvc_ctrl_mappings); + + for (; mapping < mend; ++mapping) { + if (uvc_entity_match_guid(ctrl->entity, mapping->entity) && + ctrl->info.selector == mapping->selector) + __uvc_ctrl_add_mapping(chain, ctrl, mapping); + } + + /* Finally process version-specific mappings. */ + if (chain->dev->uvc_version < 0x0150) { + mapping = uvc_ctrl_mappings_uvc11; + mend = mapping + ARRAY_SIZE(uvc_ctrl_mappings_uvc11); + } else { + mapping = uvc_ctrl_mappings_uvc15; + mend = mapping + ARRAY_SIZE(uvc_ctrl_mappings_uvc15); + } + for (; mapping < mend; ++mapping) { if (uvc_entity_match_guid(ctrl->entity, mapping->entity) && ctrl->info.selector == mapping->selector) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 6c86faecbea2..9c05776f11d1 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -329,7 +329,8 @@ static enum v4l2_ycbcr_encoding uvc_ycbcr_enc(const u8 matrix_coefficients) return V4L2_YCBCR_ENC_DEFAULT; /* Reserved */ } -/* Simplify a fraction using a simple continued fraction decomposition. The +/* + * Simplify a fraction using a simple continued fraction decomposition. The * idea here is to convert fractions such as 333333/10000000 to 1/30 using * 32 bit arithmetic only. The algorithm is not perfect and relies upon two * arbitrary parameters to remove non-significative terms from the simple @@ -347,8 +348,9 @@ void uvc_simplify_fraction(u32 *numerator, u32 *denominator, if (an == NULL) return; - /* Convert the fraction to a simple continued fraction. See - * https://mathforum.org/dr.math/faq/faq.fractions.html + /* + * Convert the fraction to a simple continued fraction. See + * https://en.wikipedia.org/wiki/Continued_fraction * Stop if the current term is bigger than or equal to the given * threshold. */ @@ -383,7 +385,8 @@ void uvc_simplify_fraction(u32 *numerator, u32 *denominator, kfree(an); } -/* Convert a fraction to a frame interval in 100ns multiples. The idea here is +/* + * Convert a fraction to a frame interval in 100ns multiples. The idea here is * to compute numerator / denominator * 10000000 using 32 bit fixed point * arithmetic only. */ @@ -396,7 +399,8 @@ u32 uvc_fraction_to_interval(u32 numerator, u32 denominator) numerator/denominator >= ((u32)-1)/10000000) return (u32)-1; - /* Divide both the denominator and the multiplier by two until + /* + * Divide both the denominator and the multiplier by two until * numerator * multiplier doesn't overflow. If anyone knows a better * algorithm please let me know. */ @@ -548,7 +552,8 @@ static int uvc_parse_format(struct uvc_device *dev, format->bpp = buffer[21]; - /* Some devices report a format that doesn't match what they + /* + * Some devices report a format that doesn't match what they * really send. */ if (dev->quirks & UVC_QUIRK_FORCE_Y8) { @@ -663,7 +668,8 @@ static int uvc_parse_format(struct uvc_device *dev, buflen -= buffer[0]; buffer += buffer[0]; - /* Parse the frame descriptors. Only uncompressed, MJPEG and frame + /* + * Parse the frame descriptors. Only uncompressed, MJPEG and frame * based formats have frame descriptors. */ while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && @@ -705,7 +711,8 @@ static int uvc_parse_format(struct uvc_device *dev, } frame->dwFrameInterval = *intervals; - /* Several UVC chipsets screw up dwMaxVideoFrameBufferSize + /* + * Several UVC chipsets screw up dwMaxVideoFrameBufferSize * completely. Observed behaviours range from setting the * value to 1.1x the actual frame size to hardwiring the * 16 low bits to 0. This results in a higher than necessary @@ -717,7 +724,8 @@ static int uvc_parse_format(struct uvc_device *dev, frame->dwMaxVideoFrameBufferSize = format->bpp * frame->wWidth * frame->wHeight / 8; - /* Some bogus devices report dwMinFrameInterval equal to + /* + * Some bogus devices report dwMinFrameInterval equal to * dwMaxFrameInterval and have dwFrameIntervalStep set to * zero. Setting all null intervals to 1 fixes the problem and * some other divisions by zero that could happen. @@ -727,7 +735,8 @@ static int uvc_parse_format(struct uvc_device *dev, *(*intervals)++ = interval ? interval : 1; } - /* Make sure that the default frame interval stays between + /* + * Make sure that the default frame interval stays between * the boundaries. */ n -= frame->bFrameIntervalType ? 1 : 2; @@ -819,7 +828,8 @@ static int uvc_parse_streaming(struct uvc_device *dev, return -ENOMEM; } - /* The Pico iMage webcam has its class-specific interface descriptors + /* + * The Pico iMage webcam has its class-specific interface descriptors * after the endpoint descriptors. */ if (buflen == 0) { @@ -918,7 +928,8 @@ static int uvc_parse_streaming(struct uvc_device *dev, break; case UVC_VS_FORMAT_DV: - /* DV format has no frame descriptor. We will create a + /* + * DV format has no frame descriptor. We will create a * dummy frame descriptor with a dummy frame interval. */ nformats++; @@ -1105,7 +1116,8 @@ static int uvc_parse_vendor_control(struct uvc_device *dev, if (buffer[1] != 0x41 || buffer[2] != 0x01) break; - /* Logitech implements several vendor specific functions + /* + * Logitech implements several vendor specific functions * through vendor specific extension units (LXU). * * The LXU descriptors are similar to XU descriptors @@ -1303,7 +1315,8 @@ static int uvc_parse_standard_control(struct uvc_device *dev, return -EINVAL; } - /* Make sure the terminal type MSB is not null, otherwise it + /* + * Make sure the terminal type MSB is not null, otherwise it * could be confused with a unit. */ type = get_unaligned_le16(&buffer[4]); @@ -1437,7 +1450,8 @@ static int uvc_parse_control(struct uvc_device *dev) int buflen = alts->extralen; int ret; - /* Parse the default alternate setting only, as the UVC specification + /* + * Parse the default alternate setting only, as the UVC specification * defines a single alternate setting, the default alternate setting * zero. */ @@ -1455,7 +1469,8 @@ next_descriptor: buffer += buffer[0]; } - /* Check if the optional status endpoint is present. Built-in iSight + /* + * Check if the optional status endpoint is present. Built-in iSight * webcams have an interrupt endpoint but spit proprietary data that * don't conform to the UVC status endpoint messages. Don't try to * handle the interrupt endpoint for those cameras. @@ -2057,7 +2072,8 @@ static int uvc_scan_device(struct uvc_device *dev) if (!UVC_ENTITY_IS_OTERM(term)) continue; - /* If the terminal is already included in a chain, skip it. + /* + * If the terminal is already included in a chain, skip it. * This can happen for chains that have multiple output * terminals, where all output terminals beside the first one * will be inserted in the chain in forward scans. @@ -2309,7 +2325,8 @@ static int uvc_register_terms(struct uvc_device *dev, if (ret < 0) return ret; - /* Register a metadata node, but ignore a possible failure, + /* + * Register a metadata node, but ignore a possible failure, * complete registration of video nodes anyway. */ uvc_meta_register(stream); @@ -2507,7 +2524,8 @@ static void uvc_disconnect(struct usb_interface *intf) { struct uvc_device *dev = usb_get_intfdata(intf); - /* Set the USB interface data to NULL. This can be done outside the + /* + * Set the USB interface data to NULL. This can be done outside the * lock, as there's no other reader. */ usb_set_intfdata(intf, NULL); @@ -2643,6 +2661,30 @@ MODULE_PARM_DESC(timeout, "Streaming control requests timeout"); * Driver initialization and cleanup */ +static const struct uvc_menu_info power_line_frequency_controls_limited[] = { + { 1, "50 Hz" }, + { 2, "60 Hz" }, +}; + +static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited = { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL, + .size = 2, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_MENU, + .data_type = UVC_CTRL_DATA_TYPE_ENUM, + .menu_info = power_line_frequency_controls_limited, + .menu_count = ARRAY_SIZE(power_line_frequency_controls_limited), +}; + +static const struct uvc_device_info uvc_ctrl_power_line_limited = { + .mappings = (const struct uvc_control_mapping *[]) { + &uvc_ctrl_power_line_mapping_limited, + NULL, /* Sentinel */ + }, +}; + static const struct uvc_device_info uvc_quirk_probe_minmax = { .quirks = UVC_QUIRK_PROBE_MINMAX, }; @@ -2673,6 +2715,33 @@ static const struct uvc_device_info uvc_quirk_force_y8 = { * though they are compliant. */ static const struct usb_device_id uvc_ids[] = { + /* Quanta USB2.0 HD UVC Webcam */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0408, + .idProduct = 0x3090, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, + /* Quanta USB2.0 HD UVC Webcam */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0408, + .idProduct = 0x4030, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, + /* Quanta USB2.0 HD UVC Webcam */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0408, + .idProduct = 0x4034, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, /* LogiLink Wireless Webcam */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -2784,6 +2853,33 @@ static const struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_RESTRICT_FRAME_RATE) }, + /* Chicony EasyCamera */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x04f2, + .idProduct = 0xb5eb, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, + /* Chicony EasyCamera */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x04f2, + .idProduct = 0xb6ba, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, + /* Chicony EasyCamera */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x04f2, + .idProduct = 0xb746, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, /* Alcor Micro AU3820 (Future Boy PC USB Webcam) */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -3168,6 +3264,15 @@ static const struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_FORCE_BPP) }, + /* Acer EasyCamera */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x5986, + .idProduct = 0x1172, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, /* Intel RealSense D4M */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, diff --git a/drivers/media/usb/uvc/uvc_isight.c b/drivers/media/usb/uvc/uvc_isight.c index 2578d6ee4829..43cda5e760a3 100644 --- a/drivers/media/usb/uvc/uvc_isight.c +++ b/drivers/media/usb/uvc/uvc_isight.c @@ -14,7 +14,8 @@ #include "uvcvideo.h" -/* Built-in iSight webcams implements most of UVC 1.0 except a +/* + * Built-in iSight webcams implements most of UVC 1.0 except a * different packet format. Instead of sending a header at the * beginning of each isochronous transfer payload, the webcam sends a * single header per image (on its own in a packet), followed by @@ -65,7 +66,8 @@ static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf, buf->state = UVC_BUF_STATE_ACTIVE; } - /* Mark the buffer as done if we're at the beginning of a new frame. + /* + * Mark the buffer as done if we're at the beginning of a new frame. * * Empty buffers (bytesused == 0) don't trigger end of frame detection * as it doesn't make sense to return an empty buffer. @@ -75,7 +77,8 @@ static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf, return -EAGAIN; } - /* Copy the video data to the buffer. Skip header packets, as they + /* + * Copy the video data to the buffer. Skip header packets, as they * contain no data. */ if (!is_header) { @@ -109,7 +112,9 @@ void uvc_video_decode_isight(struct uvc_urb *uvc_urb, struct uvc_buffer *buf, urb->iso_frame_desc[i].status); } - /* Decode the payload packet. + /* + * Decode the payload packet. + * * uvc_video_decode is entered twice when a frame transition * has been detected because the end of frame can only be * reliably detected when the first packet of the new frame diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c index 21a907d32bb7..16fa17bbd15e 100644 --- a/drivers/media/usb/uvc/uvc_queue.c +++ b/drivers/media/usb/uvc/uvc_queue.c @@ -135,7 +135,8 @@ static void uvc_buffer_queue(struct vb2_buffer *vb) kref_init(&buf->ref); list_add_tail(&buf->queue, &queue->irqqueue); } else { - /* If the device is disconnected return the buffer to userspace + /* + * If the device is disconnected return the buffer to userspace * directly. The next QBUF call will fail with -ENODEV. */ buf->state = UVC_BUF_STATE_ERROR; @@ -412,7 +413,8 @@ void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect) spin_lock_irqsave(&queue->irqlock, flags); uvc_queue_return_buffers(queue, UVC_BUF_STATE_ERROR); - /* This must be protected by the irqlock spinlock to avoid race + /* + * This must be protected by the irqlock spinlock to avoid race * conditions between uvc_buffer_queue and the disconnection event that * could result in an interruptible wait in uvc_dequeue_buffer. Do not * blindly replace this logic by checking for the UVC_QUEUE_DISCONNECTED diff --git a/drivers/media/usb/uvc/uvc_status.c b/drivers/media/usb/uvc/uvc_status.c index 753c8226db70..7518ffce22ed 100644 --- a/drivers/media/usb/uvc/uvc_status.c +++ b/drivers/media/usb/uvc/uvc_status.c @@ -202,8 +202,7 @@ static void uvc_status_complete(struct urb *urb) case -ENOENT: /* usb_kill_urb() called. */ case -ECONNRESET: /* usb_unlink_urb() called. */ case -ESHUTDOWN: /* The endpoint is being disabled. */ - case -EPROTO: /* Device is disconnected (reported by some - * host controller). */ + case -EPROTO: /* Device is disconnected (reported by some host controllers). */ return; default: @@ -272,7 +271,8 @@ int uvc_status_init(struct uvc_device *dev) pipe = usb_rcvintpipe(dev->udev, ep->desc.bEndpointAddress); - /* For high-speed interrupt endpoints, the bInterval value is used as + /* + * For high-speed interrupt endpoints, the bInterval value is used as * an exponent of two. Some developers forgot about it. */ interval = ep->desc.bInterval; diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index 648dcd579e81..4cc3fa6b8c98 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -63,7 +63,8 @@ static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain, break; case V4L2_CTRL_TYPE_MENU: - /* Prevent excessive memory consumption, as well as integer + /* + * Prevent excessive memory consumption, as well as integer * overflows. */ if (xmap->menu_count == 0 || @@ -177,7 +178,8 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream, fcc[0], fcc[1], fcc[2], fcc[3], fmt->fmt.pix.width, fmt->fmt.pix.height); - /* Check if the hardware supports the requested format, use the default + /* + * Check if the hardware supports the requested format, use the default * format otherwise. */ for (i = 0; i < stream->nformats; ++i) { @@ -191,7 +193,8 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream, fmt->fmt.pix.pixelformat = format->fcc; } - /* Find the closest image size. The distance between image sizes is + /* + * Find the closest image size. The distance between image sizes is * the size in pixels of the non-overlapping regions between the * requested size and the frame-specified size. */ @@ -233,7 +236,8 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream, probe->bFormatIndex = format->index; probe->bFrameIndex = frame->bFrameIndex; probe->dwFrameInterval = uvc_try_frame_interval(frame, interval); - /* Some webcams stall the probe control set request when the + /* + * Some webcams stall the probe control set request when the * dwMaxVideoFrameSize field is set to zero. The UVC specification * clearly states that the field is read-only from the host, so this * is a webcam bug. Set dwMaxVideoFrameSize to the value reported by @@ -254,9 +258,10 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream, ret = uvc_probe_video(stream, probe); mutex_unlock(&stream->mutex); if (ret < 0) - goto done; + return ret; - /* After the probe, update fmt with the values returned from + /* + * After the probe, update fmt with the values returned from * negotiation with the device. Some devices return invalid bFormatIndex * and bFrameIndex values, in which case we can only assume they have * accepted the requested format as-is. @@ -300,7 +305,6 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream, if (uvc_frame != NULL) *uvc_frame = frame; -done: return ret; } diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 6d3dfa4e0bb2..170a008f4006 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -189,7 +189,8 @@ static void uvc_fixup_video_ctrl(struct uvc_streaming *stream, ctrl->dwMaxVideoFrameSize = frame->dwMaxVideoFrameBufferSize; - /* The "TOSHIBA Web Camera - 5M" Chicony device (04f2:b50b) seems to + /* + * The "TOSHIBA Web Camera - 5M" Chicony device (04f2:b50b) seems to * compute the bandwidth on 16 bits and erroneously sign-extend it to * 32 bits, resulting in a huge bandwidth value. Detect and fix that * condition by setting the 16 MSBs to 0 when they're all equal to 1. @@ -207,7 +208,8 @@ static void uvc_fixup_video_ctrl(struct uvc_streaming *stream, ? ctrl->dwFrameInterval : frame->dwFrameInterval[0]; - /* Compute a bandwidth estimation by multiplying the frame + /* + * Compute a bandwidth estimation by multiplying the frame * size by the number of video frames per second, divide the * result by the number of USB frames (or micro-frames for * high-speed devices) per second and add the UVC header size @@ -220,7 +222,8 @@ static void uvc_fixup_video_ctrl(struct uvc_streaming *stream, bandwidth /= 8; bandwidth += 12; - /* The bandwidth estimate is too low for many cameras. Don't use + /* + * The bandwidth estimate is too low for many cameras. Don't use * maximum packet sizes lower than 1024 bytes to try and work * around the problem. According to measurements done on two * different camera models, the value is high enough to get most @@ -267,7 +270,8 @@ static int uvc_get_video_ctrl(struct uvc_streaming *stream, size, uvc_timeout_param); if ((query == UVC_GET_MIN || query == UVC_GET_MAX) && ret == 2) { - /* Some cameras, mostly based on Bison Electronics chipsets, + /* + * Some cameras, mostly based on Bison Electronics chipsets, * answer a GET_MIN or GET_MAX request with the wCompQuality * field only. */ @@ -279,7 +283,8 @@ static int uvc_get_video_ctrl(struct uvc_streaming *stream, ret = 0; goto out; } else if (query == UVC_GET_DEF && probe == 1 && ret != size) { - /* Many cameras don't support the GET_DEF request on their + /* + * Many cameras don't support the GET_DEF request on their * video probe control. Warn once and return, the caller will * fall back to GET_CUR. */ @@ -322,7 +327,8 @@ static int uvc_get_video_ctrl(struct uvc_streaming *stream, ctrl->bMaxVersion = 0; } - /* Some broken devices return null or wrong dwMaxVideoFrameSize and + /* + * Some broken devices return null or wrong dwMaxVideoFrameSize and * dwMaxPayloadTransferSize fields. Try to get the value from the * format and frame descriptors. */ @@ -386,7 +392,8 @@ int uvc_probe_video(struct uvc_streaming *stream, unsigned int i; int ret; - /* Perform probing. The device should adjust the requested values + /* + * Perform probing. The device should adjust the requested values * according to its capabilities. However, some devices, namely the * first generation UVC Logitech webcams, don't implement the Video * Probe control properly, and just return the needed bandwidth. For @@ -493,7 +500,8 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, if (len < header_size) return; - /* Extract the timestamps: + /* + * Extract the timestamps: * * - store the frame PTS in the buffer structure * - if the SCR field is present, retrieve the host SOF counter and @@ -506,7 +514,8 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, if (!has_scr) return; - /* To limit the amount of data, drop SCRs with an SOF identical to the + /* + * To limit the amount of data, drop SCRs with an SOF identical to the * previous one. */ dev_sof = get_unaligned_le16(&data[header_size - 2]); @@ -518,7 +527,8 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, host_sof = usb_get_current_frame_number(stream->dev->udev); time = uvc_video_get_time(); - /* The UVC specification allows device implementations that can't obtain + /* + * The UVC specification allows device implementations that can't obtain * the USB frame number to keep their own frame counters as long as they * match the size and frequency of the frame number associated with USB * SOF tokens. The SOF values sent by such devices differ from the USB @@ -756,7 +766,8 @@ void uvc_video_clock_update(struct uvc_streaming *stream, y1 = NSEC_PER_SEC; y2 = (u32)ktime_to_ns(ktime_sub(last->host_time, first->host_time)) + y1; - /* Interpolated and host SOF timestamps can wrap around at slightly + /* + * Interpolated and host SOF timestamps can wrap around at slightly * different times. Handle this by adding or removing 2048 to or from * the computed SOF value to keep it close to the SOF samples mean * value. @@ -854,7 +865,8 @@ static void uvc_video_stats_decode(struct uvc_streaming *stream, stream->stats.frame.pts = pts; } - /* Do all frames have a PTS in their first non-empty packet, or before + /* + * Do all frames have a PTS in their first non-empty packet, or before * their first empty packet ? */ if (stream->stats.frame.size == 0) { @@ -945,7 +957,8 @@ size_t uvc_video_stats_dump(struct uvc_streaming *stream, char *buf, unsigned int duration; size_t count = 0; - /* Compute the SCR.SOF frequency estimate. At the nominal 1kHz SOF + /* + * Compute the SCR.SOF frequency estimate. At the nominal 1kHz SOF * frequency this will not overflow before more than 1h. */ duration = ktime_ms_delta(stream->stats.stream.stop_ts, @@ -997,7 +1010,8 @@ static void uvc_video_stats_stop(struct uvc_streaming *stream) * Video codecs */ -/* Video payload decoding is handled by uvc_video_decode_start(), +/* + * Video payload decoding is handled by uvc_video_decode_start(), * uvc_video_decode_data() and uvc_video_decode_end(). * * uvc_video_decode_start is called with URB data at the start of a bulk or @@ -1037,7 +1051,8 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, { u8 fid; - /* Sanity checks: + /* + * Sanity checks: * - packet must be at least 2 bytes long * - bHeaderLength value must be at least 2 bytes (see above) * - bHeaderLength value can't be larger than the packet size. @@ -1049,7 +1064,8 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, fid = data[1] & UVC_STREAM_FID; - /* Increase the sequence number regardless of any buffer states, so + /* + * Increase the sequence number regardless of any buffer states, so * that discontinuous sequence numbers always indicate lost frames. */ if (stream->last_fid != fid) { @@ -1061,7 +1077,8 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, uvc_video_clock_decode(stream, buf, data, len); uvc_video_stats_decode(stream, data, len); - /* Store the payload FID bit and return immediately when the buffer is + /* + * Store the payload FID bit and return immediately when the buffer is * NULL. */ if (buf == NULL) { @@ -1076,7 +1093,8 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, buf->error = 1; } - /* Synchronize to the input stream by waiting for the FID bit to be + /* + * Synchronize to the input stream by waiting for the FID bit to be * toggled when the the buffer state is not UVC_BUF_STATE_ACTIVE. * stream->last_fid is initialized to -1, so the first isochronous * frame will always be in sync. @@ -1102,7 +1120,8 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, buf->state = UVC_BUF_STATE_ACTIVE; } - /* Mark the buffer as done if we're at the beginning of a new frame. + /* + * Mark the buffer as done if we're at the beginning of a new frame. * End of frame detection is better implemented by checking the EOF * bit (FID bit toggling is delayed by one frame compared to the EOF * bit), but some devices don't set the bit at end of frame (and the @@ -1226,7 +1245,8 @@ static void uvc_video_decode_end(struct uvc_streaming *stream, } } -/* Video payload encoding is handled by uvc_video_encode_header() and +/* + * Video payload encoding is handled by uvc_video_encode_header() and * uvc_video_encode_data(). Only bulk transfers are currently supported. * * uvc_video_encode_header is called at the start of a payload. It adds header @@ -1450,7 +1470,8 @@ static void uvc_video_decode_bulk(struct uvc_urb *uvc_urb, len = urb->actual_length; stream->bulk.payload_size += len; - /* If the URB is the first of its payload, decode and save the + /* + * If the URB is the first of its payload, decode and save the * header. */ if (stream->bulk.header_size == 0 && !stream->bulk.skip_payload) { @@ -1474,7 +1495,8 @@ static void uvc_video_decode_bulk(struct uvc_urb *uvc_urb, } } - /* The buffer queue might have been cancelled while a bulk transfer + /* + * The buffer queue might have been cancelled while a bulk transfer * was in progress, so we can reach here with buf equal to NULL. Make * sure buf is never dereferenced if NULL. */ @@ -1483,7 +1505,8 @@ static void uvc_video_decode_bulk(struct uvc_urb *uvc_urb, if (!stream->bulk.skip_payload && buf != NULL) uvc_video_decode_data(uvc_urb, buf, mem, len); - /* Detect the payload end by a URB smaller than the maximum size (or + /* + * Detect the payload end by a URB smaller than the maximum size (or * a payload size equal to the maximum) and process the header again. */ if (urb->actual_length < urb->transfer_buffer_length || @@ -1686,7 +1709,8 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming *stream, if (stream->urb_size) return stream->urb_size / psize; - /* Compute the number of packets. Bulk endpoints might transfer UVC + /* + * Compute the number of packets. Bulk endpoints might transfer UVC * payloads across multiple URBs. */ npackets = DIV_ROUND_UP(size, psize); @@ -1975,7 +1999,8 @@ static int uvc_video_start_transfer(struct uvc_streaming *stream, } } - /* The Logitech C920 temporarily forgets that it should not be adjusting + /* + * The Logitech C920 temporarily forgets that it should not be adjusting * Exposure Absolute during init so restore controls to stored values. */ if (stream->dev->quirks & UVC_QUIRK_RESTORE_CTRLS_ON_INIT) @@ -2018,7 +2043,8 @@ int uvc_video_resume(struct uvc_streaming *stream, int reset) { int ret; - /* If the bus has been reset on resume, set the alternate setting to 0. + /* + * If the bus has been reset on resume, set the alternate setting to 0. * This should be the default value, but some devices crash or otherwise * misbehave if they don't receive a SET_INTERFACE request before any * other video control request. @@ -2071,14 +2097,16 @@ int uvc_video_init(struct uvc_streaming *stream) atomic_set(&stream->active, 0); - /* Alternate setting 0 should be the default, yet the XBox Live Vision + /* + * Alternate setting 0 should be the default, yet the XBox Live Vision * Cam (and possibly other devices) crash or otherwise misbehave if * they don't receive a SET_INTERFACE request before any other video * control request. */ usb_set_interface(stream->dev->udev, stream->intfnum, 0); - /* Set the streaming probe control with default streaming parameters + /* + * Set the streaming probe control with default streaming parameters * retrieved from the device. Webcams that don't support GET_DEF * requests on the probe control will just keep their current streaming * parameters. @@ -2086,7 +2114,8 @@ int uvc_video_init(struct uvc_streaming *stream) if (uvc_get_video_ctrl(stream, probe, 1, UVC_GET_DEF) == 0) uvc_set_video_ctrl(stream, probe, 1); - /* Initialize the streaming parameters with the probe control current + /* + * Initialize the streaming parameters with the probe control current * value. This makes sure SET_CUR requests on the streaming commit * control will always use values retrieved from a successful GET_CUR * request on the probe control, as required by the UVC specification. @@ -2095,7 +2124,8 @@ int uvc_video_init(struct uvc_streaming *stream) if (ret < 0) return ret; - /* Check if the default format descriptor exists. Use the first + /* + * Check if the default format descriptor exists. Use the first * available format otherwise. */ for (i = stream->nformats; i > 0; --i) { @@ -2110,7 +2140,8 @@ int uvc_video_init(struct uvc_streaming *stream) return -EINVAL; } - /* Zero bFrameIndex might be correct. Stream-based formats (including + /* + * Zero bFrameIndex might be correct. Stream-based formats (including * MPEG-2 TS and DV) do not support frames but have a dummy frame * descriptor with bFrameIndex set to zero. If the default frame * descriptor is not found, use the first available frame. @@ -2187,7 +2218,8 @@ void uvc_video_stop_streaming(struct uvc_streaming *stream) if (stream->intf->num_altsetting > 1) { usb_set_interface(stream->dev->udev, stream->intfnum, 0); } else { - /* UVC doesn't specify how to inform a bulk-based device + /* + * UVC doesn't specify how to inform a bulk-based device * when the video stream is stopped. Windows sends a * CLEAR_FEATURE(HALT) request to the video streaming * bulk endpoint, mimic the same behaviour. diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index c5b4febd2d94..24c911aeebce 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -225,7 +225,8 @@ struct gpio_desc; struct sg_table; struct uvc_device; -/* TODO: Put the most frequently accessed fields at the beginning of +/* + * TODO: Put the most frequently accessed fields at the beginning of * structures to maximize cache efficiency. */ struct uvc_control_info { @@ -270,8 +271,7 @@ struct uvc_control { struct uvc_entity *entity; struct uvc_control_info info; - u8 index; /* Used to match the uvc_control entry with a - uvc_control_info. */ + u8 index; /* Used to match the uvc_control entry with a uvc_control_info. */ u8 dirty:1, loaded:1, modified:1, @@ -289,7 +289,8 @@ struct uvc_format_desc { u32 fcc; }; -/* The term 'entity' refers to both UVC units and UVC terminals. +/* + * The term 'entity' refers to both UVC units and UVC terminals. * * The type field is either the terminal type (wTerminalType in the terminal * descriptor), or the unit type (bDescriptorSubtype in the unit descriptor). @@ -308,8 +309,7 @@ struct uvc_format_desc { struct uvc_entity { struct list_head list; /* Entity as part of a UVC device. */ - struct list_head chain; /* Entity as part of a video device - * chain. */ + struct list_head chain; /* Entity as part of a video device chain. */ unsigned int flags; /* @@ -591,7 +591,8 @@ struct uvc_streaming { struct uvc_format *cur_format; struct uvc_frame *cur_frame; - /* Protect access to ctrl, cur_format, cur_frame and hardware video + /* + * Protect access to ctrl, cur_format, cur_frame and hardware video * probe control. */ struct mutex mutex; @@ -667,6 +668,7 @@ struct uvc_device_info { u32 quirks; u32 meta_format; u16 uvc_version; + const struct uvc_control_mapping **mappings; }; struct uvc_device { diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig index 1be9a2cc947a..348559bc2468 100644 --- a/drivers/media/v4l2-core/Kconfig +++ b/drivers/media/v4l2-core/Kconfig @@ -9,7 +9,7 @@ config VIDEO_V4L2_I2C default y config VIDEO_V4L2_SUBDEV_API - bool "V4L2 sub-device userspace API" + bool depends on VIDEO_DEV && MEDIA_CONTROLLER help Enables the V4L2 sub-device pad-level userspace API used to configure @@ -56,9 +56,11 @@ config V4L2_MEM2MEM_DEV # Used by LED subsystem flash drivers config V4L2_FLASH_LED_CLASS tristate "V4L2 flash API for LED flash class devices" - depends on VIDEO_DEV && VIDEO_V4L2_SUBDEV_API + depends on VIDEO_DEV depends on LEDS_CLASS_FLASH + select MEDIA_CONTROLLER select V4L2_ASYNC + select VIDEO_V4L2_SUBDEV_API help Say Y here to enable V4L2 flash API support for LED flash class drivers. diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index c6995718237a..2f1b718a9189 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -52,6 +52,15 @@ static int v4l2_async_nf_call_complete(struct v4l2_async_notifier *n) return n->ops->complete(n); } +static void v4l2_async_nf_call_destroy(struct v4l2_async_notifier *n, + struct v4l2_async_subdev *asd) +{ + if (!n->ops || !n->ops->destroy) + return; + + n->ops->destroy(asd); +} + static bool match_i2c(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) { @@ -66,8 +75,10 @@ static bool match_i2c(struct v4l2_async_notifier *notifier, #endif } -static bool match_fwnode(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) +static bool +match_fwnode_one(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, struct fwnode_handle *sd_fwnode, + struct v4l2_async_subdev *asd) { struct fwnode_handle *other_fwnode; struct fwnode_handle *dev_fwnode; @@ -80,15 +91,7 @@ static bool match_fwnode(struct v4l2_async_notifier *notifier, * fwnode or a device fwnode. Start with the simple case of direct * fwnode matching. */ - if (sd->fwnode == asd->match.fwnode) - return true; - - /* - * Check the same situation for any possible secondary assigned to the - * subdev's fwnode - */ - if (!IS_ERR_OR_NULL(sd->fwnode->secondary) && - sd->fwnode->secondary == asd->match.fwnode) + if (sd_fwnode == asd->match.fwnode) return true; /* @@ -99,7 +102,7 @@ static bool match_fwnode(struct v4l2_async_notifier *notifier, * ACPI. This won't make a difference, as drivers should not try to * match unconnected endpoints. */ - sd_fwnode_is_ep = fwnode_graph_is_endpoint(sd->fwnode); + sd_fwnode_is_ep = fwnode_graph_is_endpoint(sd_fwnode); asd_fwnode_is_ep = fwnode_graph_is_endpoint(asd->match.fwnode); if (sd_fwnode_is_ep == asd_fwnode_is_ep) @@ -110,11 +113,11 @@ static bool match_fwnode(struct v4l2_async_notifier *notifier, * parent of the endpoint fwnode, and compare it with the other fwnode. */ if (sd_fwnode_is_ep) { - dev_fwnode = fwnode_graph_get_port_parent(sd->fwnode); + dev_fwnode = fwnode_graph_get_port_parent(sd_fwnode); other_fwnode = asd->match.fwnode; } else { dev_fwnode = fwnode_graph_get_port_parent(asd->match.fwnode); - other_fwnode = sd->fwnode; + other_fwnode = sd_fwnode; } fwnode_handle_put(dev_fwnode); @@ -143,6 +146,19 @@ static bool match_fwnode(struct v4l2_async_notifier *notifier, return true; } +static bool match_fwnode(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) +{ + if (match_fwnode_one(notifier, sd, sd->fwnode, asd)) + return true; + + /* Also check the secondary fwnode. */ + if (IS_ERR_OR_NULL(sd->fwnode->secondary)) + return false; + + return match_fwnode_one(notifier, sd, sd->fwnode->secondary, asd); +} + static LIST_HEAD(subdev_list); static LIST_HEAD(notifier_list); static DEFINE_MUTEX(list_lock); @@ -626,6 +642,7 @@ static void __v4l2_async_nf_cleanup(struct v4l2_async_notifier *notifier) } list_del(&asd->asd_list); + v4l2_async_nf_call_destroy(notifier, asd); kfree(asd); } } diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index df34b2a283bc..e0fbe6ba4b6c 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -266,6 +266,7 @@ const struct v4l2_format_info *v4l2_format_info(u32 format) { .format = V4L2_PIX_FMT_NV61, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 }, { .format = V4L2_PIX_FMT_NV24, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_NV42, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_P010, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 2, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 }, { .format = V4L2_PIX_FMT_YUV410, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 4, .vdiv = 4 }, { .format = V4L2_PIX_FMT_YVU410, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 4, .vdiv = 4 }, @@ -277,6 +278,7 @@ const struct v4l2_format_info *v4l2_format_info(u32 format) /* Tiled YUV formats */ { .format = V4L2_PIX_FMT_NV12_4L4, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 }, + { .format = V4L2_PIX_FMT_P010_4L4, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 2, 4, 0, 0 }, .hdiv = 2, .vdiv = 2 }, /* YUV planar formats, non contiguous variant */ { .format = V4L2_PIX_FMT_YUV420M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 }, diff --git a/drivers/media/v4l2-core/v4l2-ctrls-api.c b/drivers/media/v4l2-core/v4l2-ctrls-api.c index db9baa0bd05f..50d012ba3c02 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-api.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c @@ -97,29 +97,47 @@ static int def_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl) return ptr_to_user(c, ctrl, ctrl->p_new); } -/* Helper function: copy the caller-provider value to the given control value */ -static int user_to_ptr(struct v4l2_ext_control *c, - struct v4l2_ctrl *ctrl, - union v4l2_ctrl_ptr ptr) +/* Helper function: copy the caller-provider value as the new control value */ +static int user_to_new(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl) { int ret; u32 size; - ctrl->is_new = 1; + ctrl->is_new = 0; + if (ctrl->is_dyn_array && + c->size > ctrl->p_dyn_alloc_elems * ctrl->elem_size) { + void *old = ctrl->p_dyn; + void *tmp = kvzalloc(2 * c->size, GFP_KERNEL); + + if (!tmp) + return -ENOMEM; + memcpy(tmp, ctrl->p_new.p, ctrl->elems * ctrl->elem_size); + memcpy(tmp + c->size, ctrl->p_cur.p, ctrl->elems * ctrl->elem_size); + ctrl->p_new.p = tmp; + ctrl->p_cur.p = tmp + c->size; + ctrl->p_dyn = tmp; + ctrl->p_dyn_alloc_elems = c->size / ctrl->elem_size; + kvfree(old); + } + if (ctrl->is_ptr && !ctrl->is_string) { + unsigned int elems = c->size / ctrl->elem_size; unsigned int idx; - ret = copy_from_user(ptr.p, c->ptr, c->size) ? -EFAULT : 0; - if (ret || !ctrl->is_array) - return ret; - for (idx = c->size / ctrl->elem_size; idx < ctrl->elems; idx++) - ctrl->type_ops->init(ctrl, idx, ptr); + if (copy_from_user(ctrl->p_new.p, c->ptr, c->size)) + return -EFAULT; + ctrl->is_new = 1; + if (ctrl->is_dyn_array) + ctrl->new_elems = elems; + else if (ctrl->is_array) + for (idx = elems; idx < ctrl->elems; idx++) + ctrl->type_ops->init(ctrl, idx, ctrl->p_new); return 0; } switch (ctrl->type) { case V4L2_CTRL_TYPE_INTEGER64: - *ptr.p_s64 = c->value64; + *ctrl->p_new.p_s64 = c->value64; break; case V4L2_CTRL_TYPE_STRING: size = c->size; @@ -127,32 +145,27 @@ static int user_to_ptr(struct v4l2_ext_control *c, return -ERANGE; if (size > ctrl->maximum + 1) size = ctrl->maximum + 1; - ret = copy_from_user(ptr.p_char, c->string, size) ? -EFAULT : 0; + ret = copy_from_user(ctrl->p_new.p_char, c->string, size) ? -EFAULT : 0; if (!ret) { - char last = ptr.p_char[size - 1]; + char last = ctrl->p_new.p_char[size - 1]; - ptr.p_char[size - 1] = 0; + ctrl->p_new.p_char[size - 1] = 0; /* * If the string was longer than ctrl->maximum, * then return an error. */ - if (strlen(ptr.p_char) == ctrl->maximum && last) + if (strlen(ctrl->p_new.p_char) == ctrl->maximum && last) return -ERANGE; } return ret; default: - *ptr.p_s32 = c->value; + *ctrl->p_new.p_s32 = c->value; break; } + ctrl->is_new = 1; return 0; } -/* Helper function: copy the caller-provider value as the new control value */ -static int user_to_new(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl) -{ - return user_to_ptr(c, ctrl, ctrl->p_new); -} - /* * VIDIOC_G/TRY/S_EXT_CTRLS implementation */ @@ -254,7 +267,31 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl, have_clusters = true; if (ctrl->cluster[0] != ctrl) ref = find_ref_lock(hdl, ctrl->cluster[0]->id); - if (ctrl->is_ptr && !ctrl->is_string) { + if (ctrl->is_dyn_array) { + unsigned int max_size = ctrl->dims[0] * ctrl->elem_size; + unsigned int tot_size = ctrl->elem_size; + + if (cs->which == V4L2_CTRL_WHICH_REQUEST_VAL) + tot_size *= ref->p_req_elems; + else + tot_size *= ctrl->elems; + + c->size = ctrl->elem_size * (c->size / ctrl->elem_size); + if (get) { + if (c->size < tot_size) { + c->size = tot_size; + return -ENOSPC; + } + c->size = tot_size; + } else { + if (c->size > max_size) { + c->size = max_size; + return -ENOSPC; + } + if (!c->size) + return -EFAULT; + } + } else if (ctrl->is_ptr && !ctrl->is_string) { unsigned int tot_size = ctrl->elems * ctrl->elem_size; if (c->size < tot_size) { @@ -346,7 +383,7 @@ static int class_check(struct v4l2_ctrl_handler *hdl, u32 which) * * Note that v4l2_g_ext_ctrls_common() with 'which' set to * V4L2_CTRL_WHICH_REQUEST_VAL is only called if the request was - * completed, and in that case valid_p_req is true for all controls. + * completed, and in that case p_req_valid is true for all controls. */ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs, @@ -430,7 +467,9 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl, if (is_default) ret = def_to_user(cs->controls + idx, ref->ctrl); - else if (is_request && ref->valid_p_req) + else if (is_request && ref->p_req_dyn_enomem) + ret = -ENOMEM; + else if (is_request && ref->p_req_valid) ret = req_to_user(cs->controls + idx, ref); else if (is_volatile) ret = new_to_user(cs->controls + idx, ref->ctrl); @@ -457,6 +496,17 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct video_device *vdev, } EXPORT_SYMBOL(v4l2_g_ext_ctrls); +/* Validate a new control */ +static int validate_new(const struct v4l2_ctrl *ctrl, union v4l2_ctrl_ptr p_new) +{ + unsigned int idx; + int err = 0; + + for (idx = 0; !err && idx < ctrl->new_elems; idx++) + err = ctrl->type_ops->validate(ctrl, idx, p_new); + return err; +} + /* Validate controls. */ static int validate_ctrls(struct v4l2_ext_controls *cs, struct v4l2_ctrl_helper *helpers, @@ -872,6 +922,9 @@ int __v4l2_ctrl_s_ctrl_compound(struct v4l2_ctrl *ctrl, /* It's a driver bug if this happens. */ if (WARN_ON(ctrl->type != type)) return -EINVAL; + /* Setting dynamic arrays is not (yet?) supported. */ + if (WARN_ON(ctrl->is_dyn_array)) + return -EINVAL; memcpy(ctrl->p_new.p, p, ctrl->elems * ctrl->elem_size); return set_ctrl(NULL, ctrl, 0); } diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c index 949c1884d9c1..1f85828d6694 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c @@ -307,6 +307,21 @@ static void std_log(const struct v4l2_ctrl *ctrl) case V4L2_CTRL_TYPE_VP9_FRAME: pr_cont("VP9_FRAME"); break; + case V4L2_CTRL_TYPE_HEVC_SPS: + pr_cont("HEVC_SPS"); + break; + case V4L2_CTRL_TYPE_HEVC_PPS: + pr_cont("HEVC_PPS"); + break; + case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS: + pr_cont("HEVC_SLICE_PARAMS"); + break; + case V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX: + pr_cont("HEVC_SCALING_MATRIX"); + break; + case V4L2_CTRL_TYPE_HEVC_DECODE_PARAMS: + pr_cont("HEVC_DECODE_PARAMS"); + break; default: pr_cont("unknown type %d", ctrl->type); break; @@ -521,7 +536,6 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, struct v4l2_ctrl_h264_decode_params *p_h264_dec_params; struct v4l2_ctrl_hevc_sps *p_hevc_sps; struct v4l2_ctrl_hevc_pps *p_hevc_pps; - struct v4l2_ctrl_hevc_slice_params *p_hevc_slice_params; struct v4l2_ctrl_hdr10_mastering_display *p_hdr10_mastering; struct v4l2_ctrl_hevc_decode_params *p_hevc_decode_params; struct v4l2_area *area; @@ -799,8 +813,6 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, p_hevc_pps->pps_beta_offset_div2 = 0; p_hevc_pps->pps_tc_offset_div2 = 0; } - - zero_padding(*p_hevc_pps); break; case V4L2_CTRL_TYPE_HEVC_DECODE_PARAMS: @@ -809,21 +821,9 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, if (p_hevc_decode_params->num_active_dpb_entries > V4L2_HEVC_DPB_ENTRIES_NUM_MAX) return -EINVAL; - - for (i = 0; i < p_hevc_decode_params->num_active_dpb_entries; - i++) { - struct v4l2_hevc_dpb_entry *dpb_entry = - &p_hevc_decode_params->dpb[i]; - - zero_padding(*dpb_entry); - } break; case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS: - p_hevc_slice_params = p; - - zero_padding(p_hevc_slice_params->pred_weight_table); - zero_padding(*p_hevc_slice_params); break; case V4L2_CTRL_TYPE_HDR10_CLL_INFO: @@ -991,11 +991,12 @@ EXPORT_SYMBOL(v4l2_ctrl_notify); /* Copy the one value to another. */ static void ptr_to_ptr(struct v4l2_ctrl *ctrl, - union v4l2_ctrl_ptr from, union v4l2_ctrl_ptr to) + union v4l2_ctrl_ptr from, union v4l2_ctrl_ptr to, + unsigned int elems) { if (ctrl == NULL) return; - memcpy(to.p, from.p_const, ctrl->elems * ctrl->elem_size); + memcpy(to.p, from.p_const, elems * ctrl->elem_size); } /* Copy the new value to the current value. */ @@ -1008,8 +1009,11 @@ void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 ch_flags) /* has_changed is set by cluster_changed */ changed = ctrl->has_changed; - if (changed) - ptr_to_ptr(ctrl, ctrl->p_new, ctrl->p_cur); + if (changed) { + if (ctrl->is_dyn_array) + ctrl->elems = ctrl->new_elems; + ptr_to_ptr(ctrl, ctrl->p_new, ctrl->p_cur, ctrl->elems); + } if (ch_flags & V4L2_EVENT_CTRL_CH_FLAGS) { /* Note: CH_FLAGS is only set for auto clusters. */ @@ -1039,36 +1043,122 @@ void cur_to_new(struct v4l2_ctrl *ctrl) { if (ctrl == NULL) return; - ptr_to_ptr(ctrl, ctrl->p_cur, ctrl->p_new); + if (ctrl->is_dyn_array) + ctrl->new_elems = ctrl->elems; + ptr_to_ptr(ctrl, ctrl->p_cur, ctrl->p_new, ctrl->new_elems); +} + +static bool req_alloc_dyn_array(struct v4l2_ctrl_ref *ref, u32 elems) +{ + void *tmp; + + if (elems < ref->p_req_dyn_alloc_elems) + return true; + + tmp = kvmalloc(elems * ref->ctrl->elem_size, GFP_KERNEL); + + if (!tmp) { + ref->p_req_dyn_enomem = true; + return false; + } + ref->p_req_dyn_enomem = false; + kvfree(ref->p_req.p); + ref->p_req.p = tmp; + ref->p_req_dyn_alloc_elems = elems; + return true; } /* Copy the new value to the request value */ void new_to_req(struct v4l2_ctrl_ref *ref) { + struct v4l2_ctrl *ctrl; + if (!ref) return; - ptr_to_ptr(ref->ctrl, ref->ctrl->p_new, ref->p_req); - ref->valid_p_req = true; + + ctrl = ref->ctrl; + if (ctrl->is_dyn_array && !req_alloc_dyn_array(ref, ctrl->new_elems)) + return; + + ref->p_req_elems = ctrl->new_elems; + ptr_to_ptr(ctrl, ctrl->p_new, ref->p_req, ref->p_req_elems); + ref->p_req_valid = true; } /* Copy the current value to the request value */ void cur_to_req(struct v4l2_ctrl_ref *ref) { + struct v4l2_ctrl *ctrl; + if (!ref) return; - ptr_to_ptr(ref->ctrl, ref->ctrl->p_cur, ref->p_req); - ref->valid_p_req = true; + + ctrl = ref->ctrl; + if (ctrl->is_dyn_array && !req_alloc_dyn_array(ref, ctrl->elems)) + return; + + ref->p_req_elems = ctrl->elems; + ptr_to_ptr(ctrl, ctrl->p_cur, ref->p_req, ctrl->elems); + ref->p_req_valid = true; } /* Copy the request value to the new value */ -void req_to_new(struct v4l2_ctrl_ref *ref) +int req_to_new(struct v4l2_ctrl_ref *ref) { + struct v4l2_ctrl *ctrl; + if (!ref) - return; - if (ref->valid_p_req) - ptr_to_ptr(ref->ctrl, ref->p_req, ref->ctrl->p_new); - else - ptr_to_ptr(ref->ctrl, ref->ctrl->p_cur, ref->ctrl->p_new); + return 0; + + ctrl = ref->ctrl; + + /* + * This control was never set in the request, so just use the current + * value. + */ + if (!ref->p_req_valid) { + if (ctrl->is_dyn_array) + ctrl->new_elems = ctrl->elems; + ptr_to_ptr(ctrl, ctrl->p_cur, ctrl->p_new, ctrl->new_elems); + return 0; + } + + /* Not a dynamic array, so just copy the request value */ + if (!ctrl->is_dyn_array) { + ptr_to_ptr(ctrl, ref->p_req, ctrl->p_new, ctrl->new_elems); + return 0; + } + + /* Sanity check, should never happen */ + if (WARN_ON(!ref->p_req_dyn_alloc_elems)) + return -ENOMEM; + + /* + * Check if the number of elements in the request is more than the + * elements in ctrl->p_dyn. If so, attempt to realloc ctrl->p_dyn. + * Note that p_dyn is allocated with twice the number of elements + * in the dynamic array since it has to store both the current and + * new value of such a control. + */ + if (ref->p_req_elems > ctrl->p_dyn_alloc_elems) { + unsigned int sz = ref->p_req_elems * ctrl->elem_size; + void *old = ctrl->p_dyn; + void *tmp = kvzalloc(2 * sz, GFP_KERNEL); + + if (!tmp) + return -ENOMEM; + memcpy(tmp, ctrl->p_new.p, ctrl->elems * ctrl->elem_size); + memcpy(tmp + sz, ctrl->p_cur.p, ctrl->elems * ctrl->elem_size); + ctrl->p_new.p = tmp; + ctrl->p_cur.p = tmp + sz; + ctrl->p_dyn = tmp; + ctrl->p_dyn_alloc_elems = ref->p_req_elems; + kvfree(old); + } + + ctrl->new_elems = ref->p_req_elems; + ptr_to_ptr(ctrl, ref->p_req, ctrl->p_new, ctrl->new_elems); + return 0; } /* Control range checking */ @@ -1110,17 +1200,6 @@ int check_range(enum v4l2_ctrl_type type, } } -/* Validate a new control */ -int validate_new(const struct v4l2_ctrl *ctrl, union v4l2_ctrl_ptr p_new) -{ - unsigned idx; - int err = 0; - - for (idx = 0; !err && idx < ctrl->elems; idx++) - err = ctrl->type_ops->validate(ctrl, idx, p_new); - return err; -} - /* Set the handler's error code if it wasn't set earlier already */ static inline int handler_set_err(struct v4l2_ctrl_handler *hdl, int err) { @@ -1164,6 +1243,8 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl) /* Free all nodes */ list_for_each_entry_safe(ref, next_ref, &hdl->ctrl_refs, node) { list_del(&ref->node); + if (ref->p_req_dyn_alloc_elems) + kvfree(ref->p_req.p); kfree(ref); } /* Free all controls owned by the handler */ @@ -1171,6 +1252,7 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl) list_del(&ctrl->node); list_for_each_entry_safe(sev, next_sev, &ctrl->ev_subs, node) list_del(&sev->node); + kvfree(ctrl->p_dyn); kvfree(ctrl); } kvfree(hdl->buckets); @@ -1286,7 +1368,7 @@ int handler_new_ref(struct v4l2_ctrl_handler *hdl, if (hdl->error) return hdl->error; - if (allocate_req) + if (allocate_req && !ctrl->is_dyn_array) size_extra_req = ctrl->elems * ctrl->elem_size; new_ref = kzalloc(sizeof(*new_ref) + size_extra_req, GFP_KERNEL); if (!new_ref) @@ -1460,7 +1542,6 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, elem_size = sizeof(s32); break; } - tot_ctrl_size = elem_size * elems; /* Sanity checks */ if (id == 0 || name == NULL || !elem_size || @@ -1481,17 +1562,33 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, handler_set_err(hdl, -EINVAL); return NULL; } + if (flags & V4L2_CTRL_FLAG_DYNAMIC_ARRAY) { + /* + * For now only support this for one-dimensional arrays only. + * + * This can be relaxed in the future, but this will + * require more effort. + */ + if (nr_of_dims != 1) { + handler_set_err(hdl, -EINVAL); + return NULL; + } + /* Start with just 1 element */ + elems = 1; + } + tot_ctrl_size = elem_size * elems; sz_extra = 0; if (type == V4L2_CTRL_TYPE_BUTTON) flags |= V4L2_CTRL_FLAG_WRITE_ONLY | V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; else if (type == V4L2_CTRL_TYPE_CTRL_CLASS) flags |= V4L2_CTRL_FLAG_READ_ONLY; - else if (type == V4L2_CTRL_TYPE_INTEGER64 || - type == V4L2_CTRL_TYPE_STRING || - type >= V4L2_CTRL_COMPOUND_TYPES || - is_array) + else if (!(flags & V4L2_CTRL_FLAG_DYNAMIC_ARRAY) && + (type == V4L2_CTRL_TYPE_INTEGER64 || + type == V4L2_CTRL_TYPE_STRING || + type >= V4L2_CTRL_COMPOUND_TYPES || + is_array)) sz_extra += 2 * tot_ctrl_size; if (type >= V4L2_CTRL_COMPOUND_TYPES && p_def.p_const) @@ -1520,7 +1617,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, ctrl->is_ptr = is_array || type >= V4L2_CTRL_COMPOUND_TYPES || ctrl->is_string; ctrl->is_int = !ctrl->is_ptr && type != V4L2_CTRL_TYPE_INTEGER64; ctrl->is_array = is_array; + ctrl->is_dyn_array = !!(flags & V4L2_CTRL_FLAG_DYNAMIC_ARRAY); ctrl->elems = elems; + ctrl->new_elems = elems; ctrl->nr_of_dims = nr_of_dims; if (nr_of_dims) memcpy(ctrl->dims, dims, nr_of_dims * sizeof(dims[0])); @@ -1533,6 +1632,16 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, ctrl->cur.val = ctrl->val = def; data = &ctrl[1]; + if (ctrl->is_dyn_array) { + ctrl->p_dyn_alloc_elems = elems; + ctrl->p_dyn = kvzalloc(2 * elems * elem_size, GFP_KERNEL); + if (!ctrl->p_dyn) { + kvfree(ctrl); + return NULL; + } + data = ctrl->p_dyn; + } + if (!ctrl->is_int) { ctrl->p_new.p = data; ctrl->p_cur.p = data + tot_ctrl_size; @@ -1542,7 +1651,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, } if (type >= V4L2_CTRL_COMPOUND_TYPES && p_def.p_const) { - ctrl->p_def.p = ctrl->p_cur.p + tot_ctrl_size; + if (ctrl->is_dyn_array) + ctrl->p_def.p = &ctrl[1]; + else + ctrl->p_def.p = ctrl->p_cur.p + tot_ctrl_size; memcpy(ctrl->p_def.p, p_def.p_const, elem_size); } @@ -1552,6 +1664,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, } if (handler_new_ref(hdl, ctrl, NULL, false, false)) { + kvfree(ctrl->p_dyn); kvfree(ctrl); return NULL; } @@ -1889,6 +2002,9 @@ static int cluster_changed(struct v4l2_ctrl *master) continue; } + if (ctrl->elems != ctrl->new_elems) + ctrl_changed = true; + for (idx = 0; !ctrl_changed && idx < ctrl->elems; idx++) ctrl_changed = !ctrl->type_ops->equal(ctrl, idx, ctrl->p_cur, ctrl->p_new); diff --git a/drivers/media/v4l2-core/v4l2-ctrls-defs.c b/drivers/media/v4l2-core/v4l2-ctrls-defs.c index 16f42d2fd359..e22921e7ea61 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-defs.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-defs.c @@ -704,9 +704,9 @@ const char * const *v4l2_ctrl_get_menu(u32 id) return hevc_tier; case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE: return hevc_loop_filter_mode; - case V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE: + case V4L2_CID_STATELESS_HEVC_DECODE_MODE: return hevc_decode_mode; - case V4L2_CID_MPEG_VIDEO_HEVC_START_CODE: + case V4L2_CID_STATELESS_HEVC_START_CODE: return hevc_start_code; case V4L2_CID_CAMERA_ORIENTATION: return camera_orientation; @@ -1003,13 +1003,6 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD: return "HEVC Size of Length Field"; case V4L2_CID_MPEG_VIDEO_REF_NUMBER_FOR_PFRAMES: return "Reference Frames for a P-Frame"; case V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR: return "Prepend SPS and PPS to IDR"; - case V4L2_CID_MPEG_VIDEO_HEVC_SPS: return "HEVC Sequence Parameter Set"; - case V4L2_CID_MPEG_VIDEO_HEVC_PPS: return "HEVC Picture Parameter Set"; - case V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS: return "HEVC Slice Parameters"; - case V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX: return "HEVC Scaling Matrix"; - case V4L2_CID_MPEG_VIDEO_HEVC_DECODE_PARAMS: return "HEVC Decode Parameters"; - case V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE: return "HEVC Decode Mode"; - case V4L2_CID_MPEG_VIDEO_HEVC_START_CODE: return "HEVC Start Code"; /* CAMERA controls */ /* Keep the order of the 'case's the same as in v4l2-controls.h! */ @@ -1188,6 +1181,14 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_STATELESS_MPEG2_QUANTISATION: return "MPEG-2 Quantisation Matrices"; case V4L2_CID_STATELESS_VP9_COMPRESSED_HDR: return "VP9 Probabilities Updates"; case V4L2_CID_STATELESS_VP9_FRAME: return "VP9 Frame Decode Parameters"; + case V4L2_CID_STATELESS_HEVC_SPS: return "HEVC Sequence Parameter Set"; + case V4L2_CID_STATELESS_HEVC_PPS: return "HEVC Picture Parameter Set"; + case V4L2_CID_STATELESS_HEVC_SLICE_PARAMS: return "HEVC Slice Parameters"; + case V4L2_CID_STATELESS_HEVC_SCALING_MATRIX: return "HEVC Scaling Matrix"; + case V4L2_CID_STATELESS_HEVC_DECODE_PARAMS: return "HEVC Decode Parameters"; + case V4L2_CID_STATELESS_HEVC_DECODE_MODE: return "HEVC Decode Mode"; + case V4L2_CID_STATELESS_HEVC_START_CODE: return "HEVC Start Code"; + case V4L2_CID_STATELESS_HEVC_ENTRY_POINT_OFFSETS: return "HEVC Entry Point Offsets"; /* Colorimetry controls */ /* Keep the order of the 'case's the same as in v4l2-controls.h! */ @@ -1363,8 +1364,8 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD: case V4L2_CID_MPEG_VIDEO_HEVC_TIER: case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE: - case V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE: - case V4L2_CID_MPEG_VIDEO_HEVC_START_CODE: + case V4L2_CID_STATELESS_HEVC_DECODE_MODE: + case V4L2_CID_STATELESS_HEVC_START_CODE: case V4L2_CID_STATELESS_H264_DECODE_MODE: case V4L2_CID_STATELESS_H264_START_CODE: case V4L2_CID_CAMERA_ORIENTATION: @@ -1502,21 +1503,26 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_STATELESS_VP8_FRAME: *type = V4L2_CTRL_TYPE_VP8_FRAME; break; - case V4L2_CID_MPEG_VIDEO_HEVC_SPS: + case V4L2_CID_STATELESS_HEVC_SPS: *type = V4L2_CTRL_TYPE_HEVC_SPS; break; - case V4L2_CID_MPEG_VIDEO_HEVC_PPS: + case V4L2_CID_STATELESS_HEVC_PPS: *type = V4L2_CTRL_TYPE_HEVC_PPS; break; - case V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS: + case V4L2_CID_STATELESS_HEVC_SLICE_PARAMS: *type = V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS; + *flags |= V4L2_CTRL_FLAG_DYNAMIC_ARRAY; break; - case V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX: + case V4L2_CID_STATELESS_HEVC_SCALING_MATRIX: *type = V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX; break; - case V4L2_CID_MPEG_VIDEO_HEVC_DECODE_PARAMS: + case V4L2_CID_STATELESS_HEVC_DECODE_PARAMS: *type = V4L2_CTRL_TYPE_HEVC_DECODE_PARAMS; break; + case V4L2_CID_STATELESS_HEVC_ENTRY_POINT_OFFSETS: + *type = V4L2_CTRL_TYPE_U32; + *flags |= V4L2_CTRL_FLAG_DYNAMIC_ARRAY; + break; case V4L2_CID_STATELESS_VP9_COMPRESSED_HDR: *type = V4L2_CTRL_TYPE_VP9_COMPRESSED_HDR; break; diff --git a/drivers/media/v4l2-core/v4l2-ctrls-priv.h b/drivers/media/v4l2-core/v4l2-ctrls-priv.h index d4bf2c716f97..aba6176fab6c 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-priv.h +++ b/drivers/media/v4l2-core/v4l2-ctrls-priv.h @@ -57,10 +57,9 @@ void cur_to_new(struct v4l2_ctrl *ctrl); void cur_to_req(struct v4l2_ctrl_ref *ref); void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 ch_flags); void new_to_req(struct v4l2_ctrl_ref *ref); -void req_to_new(struct v4l2_ctrl_ref *ref); +int req_to_new(struct v4l2_ctrl_ref *ref); void send_initial_event(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl); void send_event(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 changes); -int validate_new(const struct v4l2_ctrl *ctrl, union v4l2_ctrl_ptr p_new); int handler_new_ref(struct v4l2_ctrl_handler *hdl, struct v4l2_ctrl *ctrl, struct v4l2_ctrl_ref **ctrl_ref, diff --git a/drivers/media/v4l2-core/v4l2-ctrls-request.c b/drivers/media/v4l2-core/v4l2-ctrls-request.c index 7d098f287fd9..c637049d7a2b 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-request.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-request.c @@ -143,7 +143,7 @@ v4l2_ctrl_request_hdl_ctrl_find(struct v4l2_ctrl_handler *hdl, u32 id) { struct v4l2_ctrl_ref *ref = find_ref_lock(hdl, id); - return (ref && ref->valid_p_req) ? ref->ctrl : NULL; + return (ref && ref->p_req_valid) ? ref->ctrl : NULL; } EXPORT_SYMBOL_GPL(v4l2_ctrl_request_hdl_ctrl_find); @@ -373,7 +373,7 @@ void v4l2_ctrl_request_complete(struct media_request *req, v4l2_ctrl_unlock(master); continue; } - if (ref->valid_p_req) + if (ref->p_req_valid) continue; /* Copy the current control value into the request */ @@ -442,7 +442,7 @@ int v4l2_ctrl_request_setup(struct media_request *req, struct v4l2_ctrl_ref *r = find_ref(hdl, master->cluster[i]->id); - if (r->valid_p_req) { + if (r->p_req_valid) { have_new_data = true; break; } @@ -458,7 +458,11 @@ int v4l2_ctrl_request_setup(struct media_request *req, struct v4l2_ctrl_ref *r = find_ref(hdl, master->cluster[i]->id); - req_to_new(r); + ret = req_to_new(r); + if (ret) { + v4l2_ctrl_unlock(master); + goto error; + } master->cluster[i]->is_new = 1; r->req_done = true; } @@ -490,6 +494,7 @@ int v4l2_ctrl_request_setup(struct media_request *req, break; } +error: media_request_object_put(obj); return ret; } diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 21470de62d72..c314025d977e 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1007,6 +1007,31 @@ static int check_fmt(struct file *file, enum v4l2_buf_type type) return -EINVAL; } +static void v4l_sanitize_colorspace(u32 pixelformat, u32 *colorspace, + u32 *encoding, u32 *quantization, + u32 *xfer_func) +{ + bool is_hsv = pixelformat == V4L2_PIX_FMT_HSV24 || + pixelformat == V4L2_PIX_FMT_HSV32; + + if (!v4l2_is_colorspace_valid(*colorspace)) { + *colorspace = V4L2_COLORSPACE_DEFAULT; + *encoding = V4L2_YCBCR_ENC_DEFAULT; + *quantization = V4L2_QUANTIZATION_DEFAULT; + *xfer_func = V4L2_XFER_FUNC_DEFAULT; + } + + if ((!is_hsv && !v4l2_is_ycbcr_enc_valid(*encoding)) || + (is_hsv && !v4l2_is_hsv_enc_valid(*encoding))) + *encoding = V4L2_YCBCR_ENC_DEFAULT; + + if (!v4l2_is_quant_valid(*quantization)) + *quantization = V4L2_QUANTIZATION_DEFAULT; + + if (!v4l2_is_xfer_func_valid(*xfer_func)) + *xfer_func = V4L2_XFER_FUNC_DEFAULT; +} + static void v4l_sanitize_format(struct v4l2_format *fmt) { unsigned int offset; @@ -1026,20 +1051,40 @@ static void v4l_sanitize_format(struct v4l2_format *fmt) * field to the magic value when the extended pixel format structure * isn't used by applications. */ + if (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE || + fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + if (fmt->fmt.pix.priv != V4L2_PIX_FMT_PRIV_MAGIC) { + fmt->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; + + offset = offsetof(struct v4l2_pix_format, priv) + + sizeof(fmt->fmt.pix.priv); + memset(((void *)&fmt->fmt.pix) + offset, 0, + sizeof(fmt->fmt.pix) - offset); + } + } - if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && - fmt->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) - return; - - if (fmt->fmt.pix.priv == V4L2_PIX_FMT_PRIV_MAGIC) - return; + /* Replace invalid colorspace values with defaults. */ + if (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE || + fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + v4l_sanitize_colorspace(fmt->fmt.pix.pixelformat, + &fmt->fmt.pix.colorspace, + &fmt->fmt.pix.ycbcr_enc, + &fmt->fmt.pix.quantization, + &fmt->fmt.pix.xfer_func); + } else if (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE || + fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + u32 ycbcr_enc = fmt->fmt.pix_mp.ycbcr_enc; + u32 quantization = fmt->fmt.pix_mp.quantization; + u32 xfer_func = fmt->fmt.pix_mp.xfer_func; - fmt->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; + v4l_sanitize_colorspace(fmt->fmt.pix_mp.pixelformat, + &fmt->fmt.pix_mp.colorspace, &ycbcr_enc, + &quantization, &xfer_func); - offset = offsetof(struct v4l2_pix_format, priv) - + sizeof(fmt->fmt.pix.priv); - memset(((void *)&fmt->fmt.pix) + offset, 0, - sizeof(fmt->fmt.pix) - offset); + fmt->fmt.pix_mp.ycbcr_enc = ycbcr_enc; + fmt->fmt.pix_mp.quantization = quantization; + fmt->fmt.pix_mp.xfer_func = xfer_func; + } } static int v4l_querycap(const struct v4l2_ioctl_ops *ops, @@ -1296,6 +1341,8 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_XYUV32: descr = "32-bit XYUV 8-8-8-8"; break; case V4L2_PIX_FMT_VUYA32: descr = "32-bit VUYA 8-8-8-8"; break; case V4L2_PIX_FMT_VUYX32: descr = "32-bit VUYX 8-8-8-8"; break; + case V4L2_PIX_FMT_YUVA32: descr = "32-bit YUVA 8-8-8-8"; break; + case V4L2_PIX_FMT_YUVX32: descr = "32-bit YUVX 8-8-8-8"; break; case V4L2_PIX_FMT_YUV410: descr = "Planar YUV 4:1:0"; break; case V4L2_PIX_FMT_YUV420: descr = "Planar YUV 4:2:0"; break; case V4L2_PIX_FMT_HI240: descr = "8-bit Dithered RGB (BTTV)"; break; @@ -1306,9 +1353,11 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_NV61: descr = "Y/CrCb 4:2:2"; break; case V4L2_PIX_FMT_NV24: descr = "Y/CbCr 4:4:4"; break; case V4L2_PIX_FMT_NV42: descr = "Y/CrCb 4:4:4"; break; + case V4L2_PIX_FMT_P010: descr = "10-bit Y/CbCr 4:2:0"; break; case V4L2_PIX_FMT_NV12_4L4: descr = "Y/CbCr 4:2:0 (4x4 Linear)"; break; case V4L2_PIX_FMT_NV12_16L16: descr = "Y/CbCr 4:2:0 (16x16 Linear)"; break; case V4L2_PIX_FMT_NV12_32L32: descr = "Y/CbCr 4:2:0 (32x32 Linear)"; break; + case V4L2_PIX_FMT_P010_4L4: descr = "10-bit Y/CbCr 4:2:0 (4x4 Linear)"; break; case V4L2_PIX_FMT_NV12M: descr = "Y/CbCr 4:2:0 (N-C)"; break; case V4L2_PIX_FMT_NV21M: descr = "Y/CrCb 4:2:0 (N-C)"; break; case V4L2_PIX_FMT_NV16M: descr = "Y/CbCr 4:2:2 (N-C)"; break; diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 6469f9a25a4e..837e1855f94b 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -925,7 +925,7 @@ static __poll_t v4l2_m2m_poll_for_data(struct file *file, if ((!src_q->streaming || src_q->error || list_empty(&src_q->queued_list)) && (!dst_q->streaming || dst_q->error || - list_empty(&dst_q->queued_list))) + (list_empty(&dst_q->queued_list) && !dst_q->last_buffer_dequeued))) return EPOLLERR; spin_lock_irqsave(&src_q->done_lock, flags); |