diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2024-07-17 18:30:10 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2024-07-17 18:30:10 -0700 |
commit | b1bc554e009e3aeed7e4cfd2e717c7a34a98c683 (patch) | |
tree | db092dd7887e732588250f2c2f932e1bfc3f87a2 /drivers/media | |
parent | 0ffb8a4c96e55ecf0e572aec1a0220af3da84e22 (diff) | |
parent | 68a72104cbcf38ad16500216e213fa4eb21c4be2 (diff) | |
download | linux-b1bc554e009e3aeed7e4cfd2e717c7a34a98c683.tar.gz linux-b1bc554e009e3aeed7e4cfd2e717c7a34a98c683.tar.bz2 linux-b1bc554e009e3aeed7e4cfd2e717c7a34a98c683.zip |
Merge tag 'media/v6.11-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab:
- New sensor drivers: gc05a2, gc08a3 and imx283
- New serializer/deserializer drivers: max96714 and max96717
- New JPEG encoder driver: e5010
- Support for Raspberry Pi PiSP Backend (BE) ISP driver
- Old documentation for av7110 driver removed, as a new version was
added as Documentation/userspace-api/media/dvb/legacy*.rst
- atompisp: Linux firmwares are now available, so drop firmware-related
task from TODO and update firmware logic
- The imx258 driver has gained several improvements
- wave5 driver has gained support for HEVC decoding
- em28xx gained support for MyGica UTV3
- av7110 budget-patch driver removed
- Lots of other cleanups, improvements and fixes
* tag 'media/v6.11-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (301 commits)
media: raspberrypi: Switch to remove_new
media: uapi: pisp_be_config: Add extra config fields
media: uapi: pisp_be_config: Re-sort pisp_be_tiles_config
media: uapi: pisp_common: Capitalize all macros
media: uapi: pisp_common: Add 32 bpp format test
media: uapi: pisp_be_config: Drop BIT() from uAPI
media: stm32: dcmipp: correct error handling in dcmipp_create_subdevs
media: atomisp: Fix spelling mistakes in sh_css_sp.c
media: atomisp: Fix spelling mistake in ia_css_debug.c
media: atomisp: Fix spelling mistake in hmm_bo.c
media: atomisp: Fix spelling mistake in ia_css_eed1_8.host.c
media: atomisp: Fix spelling mistake in sh_css_internal.h
media: atomisp: Fix spelling mistake "pipline" -> "pipeline"
media: atomisp: Remove unused GPIO related defines and APIs
media: atomisp: Replace COMPILATION_ERROR_IF() by static_assert()
media: atomisp: Clean up unused macros from math_support.h
media: atomisp: csi2-bridge: Add DMI quirk for OV5693 on Xiaomi Mipad2
media: atomisp: Update TODO
media: atomisp: Prefix firmware paths with "intel/ipu/"
media: atomisp: Remove firmware_name module parameter
...
Diffstat (limited to 'drivers/media')
258 files changed, 18232 insertions, 4558 deletions
diff --git a/drivers/media/common/siano/smscoreapi.c b/drivers/media/common/siano/smscoreapi.c index 7ebcb10126c9..b6f1eb5dbbdf 100644 --- a/drivers/media/common/siano/smscoreapi.c +++ b/drivers/media/common/siano/smscoreapi.c @@ -839,7 +839,7 @@ static int smscore_configure_board(struct smscore_device_t *coredev) mtu_msg.x_msg_header.msg_flags = 0; mtu_msg.x_msg_header.msg_type = MSG_SMS_SET_MAX_TX_MSG_LEN_REQ; mtu_msg.x_msg_header.msg_length = sizeof(mtu_msg); - mtu_msg.msg_data[0] = board->mtu; + mtu_msg.msg_data = board->mtu; coredev->sendrequest_handler(coredev->context, &mtu_msg, sizeof(mtu_msg)); @@ -852,7 +852,7 @@ static int smscore_configure_board(struct smscore_device_t *coredev) SMS_INIT_MSG(&crys_msg.x_msg_header, MSG_SMS_NEW_CRYSTAL_REQ, sizeof(crys_msg)); - crys_msg.msg_data[0] = board->crystal; + crys_msg.msg_data = board->crystal; coredev->sendrequest_handler(coredev->context, &crys_msg, sizeof(crys_msg)); @@ -1306,7 +1306,7 @@ static int smscore_init_device(struct smscore_device_t *coredev, int mode) msg = (struct sms_msg_data *)SMS_ALIGN_ADDRESS(buffer); SMS_INIT_MSG(&msg->x_msg_header, MSG_SMS_INIT_DEVICE_REQ, sizeof(struct sms_msg_data)); - msg->msg_data[0] = mode; + msg->msg_data = mode; rc = smscore_sendrequest_and_wait(coredev, msg, msg->x_msg_header. msg_length, @@ -1394,7 +1394,7 @@ int smscore_set_device_mode(struct smscore_device_t *coredev, int mode) SMS_INIT_MSG(&msg->x_msg_header, MSG_SMS_INIT_DEVICE_REQ, sizeof(struct sms_msg_data)); - msg->msg_data[0] = mode; + msg->msg_data = mode; rc = smscore_sendrequest_and_wait( coredev, msg, msg->x_msg_header.msg_length, @@ -1554,7 +1554,7 @@ void smscore_onresponse(struct smscore_device_t *coredev, struct sms_msg_data *validity = (struct sms_msg_data *) phdr; pr_debug("MSG_SMS_DATA_VALIDITY_RES, checksum = 0x%x\n", - validity->msg_data[0]); + validity->msg_data); complete(&coredev->data_validity_done); break; } diff --git a/drivers/media/common/siano/smscoreapi.h b/drivers/media/common/siano/smscoreapi.h index f8789ee0d554..82d9f8a64d99 100644 --- a/drivers/media/common/siano/smscoreapi.h +++ b/drivers/media/common/siano/smscoreapi.h @@ -616,7 +616,7 @@ struct sms_msg_hdr { struct sms_msg_data { struct sms_msg_hdr x_msg_header; - u32 msg_data[1]; + u32 msg_data; }; struct sms_msg_data2 { @@ -666,7 +666,7 @@ struct sms_firmware { u32 check_sum; u32 length; u32 start_address; - u8 payload[1]; + u8 payload[]; }; /* statistics information returned as response for @@ -1042,20 +1042,6 @@ struct sms_srvm_signal_status { u32 request_id; }; -struct sms_i2c_req { - u32 device_address; /* I2c device address */ - u32 write_count; /* number of bytes to write */ - u32 read_count; /* number of bytes to read */ - u8 Data[1]; -}; - -struct sms_i2c_res { - u32 status; /* non-zero value in case of failure */ - u32 read_count; /* number of bytes read */ - u8 Data[1]; -}; - - struct smscore_config_gpio { #define SMS_GPIO_DIRECTION_INPUT 0 #define SMS_GPIO_DIRECTION_OUTPUT 1 diff --git a/drivers/media/common/siano/smsdvb-main.c b/drivers/media/common/siano/smsdvb-main.c index d893a0e4672b..44d8fe8b220e 100644 --- a/drivers/media/common/siano/smsdvb-main.c +++ b/drivers/media/common/siano/smsdvb-main.c @@ -689,7 +689,7 @@ static int smsdvb_start_feed(struct dvb_demux_feed *feed) pid_msg.x_msg_header.msg_flags = 0; pid_msg.x_msg_header.msg_type = MSG_SMS_ADD_PID_FILTER_REQ; pid_msg.x_msg_header.msg_length = sizeof(pid_msg); - pid_msg.msg_data[0] = feed->pid; + pid_msg.msg_data = feed->pid; return smsclient_sendrequest(client->smsclient, &pid_msg, sizeof(pid_msg)); @@ -711,7 +711,7 @@ static int smsdvb_stop_feed(struct dvb_demux_feed *feed) pid_msg.x_msg_header.msg_flags = 0; pid_msg.x_msg_header.msg_type = MSG_SMS_REMOVE_PID_FILTER_REQ; pid_msg.x_msg_header.msg_length = sizeof(pid_msg); - pid_msg.msg_data[0] = feed->pid; + pid_msg.msg_data = feed->pid; return smsclient_sendrequest(client->smsclient, &pid_msg, sizeof(pid_msg)); diff --git a/drivers/media/common/siano/smsendian.c b/drivers/media/common/siano/smsendian.c index a3573814919b..b957970c7d97 100644 --- a/drivers/media/common/siano/smsendian.c +++ b/drivers/media/common/siano/smsendian.c @@ -20,11 +20,12 @@ void smsendian_handle_tx_message(void *buffer) struct sms_msg_data *msg = buffer; int i; int msg_words; + u32 *msg_data = &msg->msg_data; switch (msg->x_msg_header.msg_type) { case MSG_SMS_DATA_DOWNLOAD_REQ: { - msg->msg_data[0] = le32_to_cpu((__force __le32)(msg->msg_data[0])); + msg->msg_data = le32_to_cpu((__force __le32)(msg->msg_data)); break; } @@ -33,7 +34,7 @@ void smsendian_handle_tx_message(void *buffer) sizeof(struct sms_msg_hdr))/4; for (i = 0; i < msg_words; i++) - msg->msg_data[i] = le32_to_cpu((__force __le32)msg->msg_data[i]); + msg_data[i] = le32_to_cpu((__force __le32)msg_data[i]); break; } @@ -66,11 +67,12 @@ void smsendian_handle_rx_message(void *buffer) default: { + u32 *msg_data = &msg->msg_data; msg_words = (msg->x_msg_header.msg_length - sizeof(struct sms_msg_hdr))/4; for (i = 0; i < msg_words; i++) - msg->msg_data[i] = le32_to_cpu((__force __le32)msg->msg_data[i]); + msg_data[i] = le32_to_cpu((__force __le32)msg_data[i]); break; } diff --git a/drivers/media/common/uvc.c b/drivers/media/common/uvc.c index 9c0ba7a6c185..c54c2268fee6 100644 --- a/drivers/media/common/uvc.c +++ b/drivers/media/common/uvc.c @@ -180,4 +180,5 @@ const struct uvc_format_desc *uvc_format_by_guid(const u8 guid[16]) } EXPORT_SYMBOL_GPL(uvc_format_by_guid); +MODULE_DESCRIPTION("USB Video Class common code"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 358f1fe42975..0217392fcc0d 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -199,7 +199,6 @@ module_param(debug, int, 0644); }) static void __vb2_queue_cancel(struct vb2_queue *q); -static void __enqueue_in_driver(struct vb2_buffer *vb); static const char *vb2_state_name(enum vb2_buffer_state s) { diff --git a/drivers/media/common/videobuf2/videobuf2-dvb.c b/drivers/media/common/videobuf2/videobuf2-dvb.c index 8c15bcd07eef..3746bf6d1c15 100644 --- a/drivers/media/common/videobuf2/videobuf2-dvb.c +++ b/drivers/media/common/videobuf2/videobuf2-dvb.c @@ -19,6 +19,7 @@ /* ------------------------------------------------------------------ */ MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); +MODULE_DESCRIPTION("Videobuf2 helper library for simple DVB cards"); MODULE_LICENSE("GPL"); /* ------------------------------------------------------------------ */ diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c index acc27376c246..d02a92a81c60 100644 --- a/drivers/media/dvb-frontends/au8522_decoder.c +++ b/drivers/media/dvb-frontends/au8522_decoder.c @@ -25,6 +25,7 @@ #include "au8522_priv.h" MODULE_AUTHOR("Devin Heitmueller"); +MODULE_DESCRIPTION("Auvitek AU8522 QAM/8VSB demodulator driver and video decoder"); MODULE_LICENSE("GPL"); static int au8522_analog_debug; diff --git a/drivers/media/dvb-frontends/dib7000p.c b/drivers/media/dvb-frontends/dib7000p.c index 444fe1c4bf2d..c5582d4fa5be 100644 --- a/drivers/media/dvb-frontends/dib7000p.c +++ b/drivers/media/dvb-frontends/dib7000p.c @@ -32,11 +32,6 @@ MODULE_PARM_DESC(buggy_sfn_workaround, "Enable work-around for buggy SFNs (defau __func__, ##arg); \ } while (0) -struct i2c_device { - struct i2c_adapter *i2c_adap; - u8 i2c_addr; -}; - struct dib7000p_state { struct dvb_frontend demod; struct dib7000p_config cfg; diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c index 6fcaf07e1b82..779cce93e94a 100644 --- a/drivers/media/dvb-frontends/drx39xyj/drxj.c +++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c @@ -976,13 +976,6 @@ static struct drx_aud_data drxj_default_aud_data_g = { /*----------------------------------------------------------------------------- STRUCTURES ----------------------------------------------------------------------------*/ -struct drxjeq_stat { - u16 eq_mse; - u8 eq_mode; - u8 eq_ctrl; - u8 eq_stat; -}; - /* HI command */ struct drxj_hi_cmd { u16 cmd; diff --git a/drivers/media/dvb-frontends/mb86a16.c b/drivers/media/dvb-frontends/mb86a16.c index 0fc45896e7b8..9033e39d75f4 100644 --- a/drivers/media/dvb-frontends/mb86a16.c +++ b/drivers/media/dvb-frontends/mb86a16.c @@ -1854,5 +1854,6 @@ error: return NULL; } EXPORT_SYMBOL_GPL(mb86a16_attach); +MODULE_DESCRIPTION("Fujitsu MB86A16 DVB-S/DSS DC Receiver driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Manu Abraham"); diff --git a/drivers/media/dvb-frontends/mxl5xx_defs.h b/drivers/media/dvb-frontends/mxl5xx_defs.h index 097271f73740..bf08d82ba2cc 100644 --- a/drivers/media/dvb-frontends/mxl5xx_defs.h +++ b/drivers/media/dvb-frontends/mxl5xx_defs.h @@ -168,7 +168,7 @@ struct MBIN_FILE_HEADER_T { struct MBIN_FILE_T { struct MBIN_FILE_HEADER_T header; - u8 data[1]; + u8 data[]; }; struct MBIN_SEGMENT_HEADER_T { @@ -179,7 +179,7 @@ struct MBIN_SEGMENT_HEADER_T { struct MBIN_SEGMENT_T { struct MBIN_SEGMENT_HEADER_T header; - u8 data[1]; + u8 data[]; }; enum MXL_CMD_TYPE_E { MXL_CMD_WRITE = 0, MXL_CMD_READ }; diff --git a/drivers/media/dvb-frontends/stv090x.c b/drivers/media/dvb-frontends/stv090x.c index cc45139057ba..3b02d504941f 100644 --- a/drivers/media/dvb-frontends/stv090x.c +++ b/drivers/media/dvb-frontends/stv090x.c @@ -748,6 +748,22 @@ static int stv090x_write_reg(struct stv090x_state *state, unsigned int reg, u8 d return stv090x_write_regs(state, reg, &tmp, 1); } +static inline void stv090x_tuner_i2c_lock(struct stv090x_state *state) +{ + if (state->config->tuner_i2c_lock) + state->config->tuner_i2c_lock(&state->frontend, 1); + else + mutex_lock(&state->internal->tuner_lock); +} + +static inline void stv090x_tuner_i2c_unlock(struct stv090x_state *state) +{ + if (state->config->tuner_i2c_lock) + state->config->tuner_i2c_lock(&state->frontend, 0); + else + mutex_unlock(&state->internal->tuner_lock); +} + static int stv090x_i2c_gate_ctrl(struct stv090x_state *state, int enable) { u32 reg; @@ -761,12 +777,8 @@ static int stv090x_i2c_gate_ctrl(struct stv090x_state *state, int enable) * In case of any error, the lock is unlocked and exit within the * relevant operations themselves. */ - if (enable) { - if (state->config->tuner_i2c_lock) - state->config->tuner_i2c_lock(&state->frontend, 1); - else - mutex_lock(&state->internal->tuner_lock); - } + if (enable) + stv090x_tuner_i2c_lock(state); reg = STV090x_READ_DEMOD(state, I2CRPT); if (enable) { @@ -782,20 +794,13 @@ static int stv090x_i2c_gate_ctrl(struct stv090x_state *state, int enable) goto err; } - if (!enable) { - if (state->config->tuner_i2c_lock) - state->config->tuner_i2c_lock(&state->frontend, 0); - else - mutex_unlock(&state->internal->tuner_lock); - } + if (!enable) + stv090x_tuner_i2c_unlock(state); return 0; err: dprintk(FE_ERROR, 1, "I/O error"); - if (state->config->tuner_i2c_lock) - state->config->tuner_i2c_lock(&state->frontend, 0); - else - mutex_unlock(&state->internal->tuner_lock); + stv090x_tuner_i2c_unlock(state); return -1; } diff --git a/drivers/media/dvb-frontends/stv0910.c b/drivers/media/dvb-frontends/stv0910.c index e517ff757744..069dec75129c 100644 --- a/drivers/media/dvb-frontends/stv0910.c +++ b/drivers/media/dvb-frontends/stv0910.c @@ -119,11 +119,6 @@ struct stv { u8 vth[6]; }; -struct sinit_table { - u16 address; - u8 data; -}; - struct slookup { s16 value; u32 reg_value; diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index c6d3ee472d81..8ba096b8ebca 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -70,6 +70,26 @@ config VIDEO_GC0308 To compile this driver as a module, choose M here: the module will be called gc0308. +config VIDEO_GC05A2 + tristate "GalaxyCore gc05a2 sensor support" + select V4L2_CCI_I2C + help + This is a Video4Linux2 sensor driver for the GalaxyCore gc05a2 + camera. + + To compile this driver as a module, choose M here: the + module will be called gc05a2. + +config VIDEO_GC08A3 + tristate "GalaxyCore gc08a3 sensor support" + select V4L2_CCI_I2C + help + This is a Video4Linux2 sensor driver for the GalaxyCore gc08a3 + camera. + + To compile this driver as a module, choose M here: the + module will be called gc08a3. + config VIDEO_GC2145 select V4L2_CCI_I2C tristate "GalaxyCore GC2145 sensor support" @@ -139,6 +159,7 @@ config VIDEO_IMX219 config VIDEO_IMX258 tristate "Sony IMX258 sensor support" + select V4L2_CCI_I2C help This is a Video4Linux2 sensor driver for the Sony IMX258 camera. @@ -153,6 +174,16 @@ config VIDEO_IMX274 This is a V4L2 sensor driver for the Sony IMX274 CMOS image sensor. +config VIDEO_IMX283 + tristate "Sony IMX283 sensor support" + select V4L2_CCI_I2C + help + This is a V4L2 sensor driver for the Sony IMX283 + CMOS image sensor. + + To compile this driver as a module, choose M here: the + module will be called imx283. + config VIDEO_IMX290 tristate "Sony IMX290 sensor support" select REGMAP_I2C @@ -659,7 +690,7 @@ config VIDEO_S5K6A3 This is a V4L2 sensor driver for Samsung S5K6A3 raw camera sensor. -config VIDEO_ST_VGXY61 +config VIDEO_VGXY61 tristate "ST VGXY61 sensor support" select V4L2_CCI_I2C depends on OF && GPIOLIB @@ -679,6 +710,7 @@ config VIDEO_THP7312 tristate "THine THP7312 support" depends on I2C select FW_LOADER + select FW_UPLOAD select MEDIA_CONTROLLER select V4L2_CCI_I2C select V4L2_FWNODE @@ -1575,6 +1607,40 @@ config VIDEO_DS90UB960 Device driver for the Texas Instruments DS90UB960 FPD-Link III Deserializer and DS90UB9702 FPD-Link IV Deserializer. +config VIDEO_MAX96714 + tristate "Maxim MAX96714 GMSL2 deserializer" + depends on OF && I2C && VIDEO_DEV + select I2C_MUX + select MEDIA_CONTROLLER + select GPIOLIB + select V4L2_CCI_I2C + select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + help + Device driver for the Maxim MAX96714 GMSL2 Deserializer. + MAX96714 deserializers convert a GMSL2 input to MIPI CSI-2 + output. + + To compile this driver as a module, choose M here: the + module will be called max96714. + +config VIDEO_MAX96717 + tristate "Maxim MAX96717 GMSL2 Serializer support" + depends on OF && I2C && VIDEO_DEV && COMMON_CLK + select I2C_MUX + select MEDIA_CONTROLLER + select GPIOLIB + select V4L2_CCI_I2C + select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + help + Device driver for the Maxim MAX96717 GMSL2 Serializer. + MAX96717 serializers convert video on a MIPI CSI-2 + input to a GMSL2 output. + + To compile this driver as a module, choose M here: the + module will be called max96717. + endmenu endif # VIDEO_DEV diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index dfbe6448b549..fbb988bd067a 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -38,6 +38,8 @@ obj-$(CONFIG_VIDEO_DW9768) += dw9768.o obj-$(CONFIG_VIDEO_DW9807_VCM) += dw9807-vcm.o obj-$(CONFIG_VIDEO_ET8EK8) += et8ek8/ obj-$(CONFIG_VIDEO_GC0308) += gc0308.o +obj-$(CONFIG_VIDEO_GC05A2) += gc05a2.o +obj-$(CONFIG_VIDEO_GC08A3) += gc08a3.o obj-$(CONFIG_VIDEO_GC2145) += gc2145.o obj-$(CONFIG_VIDEO_HI556) += hi556.o obj-$(CONFIG_VIDEO_HI846) += hi846.o @@ -48,6 +50,7 @@ obj-$(CONFIG_VIDEO_IMX214) += imx214.o obj-$(CONFIG_VIDEO_IMX219) += imx219.o obj-$(CONFIG_VIDEO_IMX258) += imx258.o obj-$(CONFIG_VIDEO_IMX274) += imx274.o +obj-$(CONFIG_VIDEO_IMX283) += imx283.o obj-$(CONFIG_VIDEO_IMX290) += imx290.o obj-$(CONFIG_VIDEO_IMX296) += imx296.o obj-$(CONFIG_VIDEO_IMX319) += imx319.o @@ -64,6 +67,8 @@ obj-$(CONFIG_VIDEO_LM3646) += lm3646.o obj-$(CONFIG_VIDEO_M52790) += m52790.o obj-$(CONFIG_VIDEO_MAX9271_LIB) += max9271.o obj-$(CONFIG_VIDEO_MAX9286) += max9286.o +obj-$(CONFIG_VIDEO_MAX96714) += max96714.o +obj-$(CONFIG_VIDEO_MAX96717) += max96717.o obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o obj-$(CONFIG_VIDEO_MT9M001) += mt9m001.o @@ -124,7 +129,6 @@ obj-$(CONFIG_VIDEO_SAA717X) += saa717x.o obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o obj-$(CONFIG_VIDEO_SONY_BTF_MPX) += sony-btf-mpx.o obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o -obj-$(CONFIG_VIDEO_ST_VGXY61) += st-vgxy61.o obj-$(CONFIG_VIDEO_TC358743) += tc358743.o obj-$(CONFIG_VIDEO_TC358746) += tc358746.o obj-$(CONFIG_VIDEO_TDA1997X) += tda1997x.o @@ -148,6 +152,7 @@ obj-$(CONFIG_VIDEO_TW9910) += tw9910.o obj-$(CONFIG_VIDEO_UDA1342) += uda1342.o obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o +obj-$(CONFIG_VIDEO_VGXY61) += vgxy61.o obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o obj-$(CONFIG_VIDEO_WM8739) += wm8739.o diff --git a/drivers/media/i2c/adv748x/adv748x-afe.c b/drivers/media/i2c/adv748x/adv748x-afe.c index 50d9fbadbe38..5edb3295dc58 100644 --- a/drivers/media/i2c/adv748x/adv748x-afe.c +++ b/drivers/media/i2c/adv748x/adv748x-afe.c @@ -114,7 +114,7 @@ static void adv748x_afe_fill_format(struct adv748x_afe *afe, { memset(fmt, 0, sizeof(*fmt)); - fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; + fmt->code = MEDIA_BUS_FMT_UYVY8_1X16; fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; fmt->field = V4L2_FIELD_ALTERNATE; @@ -337,7 +337,7 @@ static int adv748x_afe_enum_mbus_code(struct v4l2_subdev *sd, if (code->index != 0) return -EINVAL; - code->code = MEDIA_BUS_FMT_UYVY8_2X8; + code->code = MEDIA_BUS_FMT_UYVY8_1X16; return 0; } diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c index 5b265b722394..ebe7da8ebed7 100644 --- a/drivers/media/i2c/adv748x/adv748x-csi2.c +++ b/drivers/media/i2c/adv748x/adv748x-csi2.c @@ -6,7 +6,6 @@ */ #include <linux/module.h> -#include <linux/mutex.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> @@ -14,6 +13,15 @@ #include "adv748x.h" +static const unsigned int adv748x_csi2_txa_fmts[] = { + MEDIA_BUS_FMT_UYVY8_1X16, + MEDIA_BUS_FMT_RGB888_1X24, +}; + +static const unsigned int adv748x_csi2_txb_fmts[] = { + MEDIA_BUS_FMT_UYVY8_1X16, +}; + int adv748x_csi2_set_virtual_channel(struct adv748x_csi2 *tx, unsigned int vc) { return tx_write(tx, ADV748X_CSI_VC_REF, vc << ADV748X_CSI_VC_REF_SHIFT); @@ -59,7 +67,33 @@ static int adv748x_csi2_register_link(struct adv748x_csi2 *tx, /* ----------------------------------------------------------------------------- * v4l2_subdev_internal_ops - * + */ + +static int adv748x_csi2_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + static const struct v4l2_mbus_framefmt adv748x_csi2_default_fmt = { + .width = 1280, + .height = 720, + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .colorspace = V4L2_COLORSPACE_SRGB, + .field = V4L2_FIELD_NONE, + .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT, + .quantization = V4L2_QUANTIZATION_DEFAULT, + .xfer_func = V4L2_XFER_FUNC_DEFAULT, + }; + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_state_get_format(state, ADV748X_CSI2_SINK); + *fmt = adv748x_csi2_default_fmt; + + fmt = v4l2_subdev_state_get_format(state, ADV748X_CSI2_SOURCE); + *fmt = adv748x_csi2_default_fmt; + + return 0; +} + +/* * We use the internal registered operation to be able to ensure that our * incremental subdevices (not connected in the forward path) can be registered * against the resulting video path and media device. @@ -109,6 +143,7 @@ static int adv748x_csi2_registered(struct v4l2_subdev *sd) } static const struct v4l2_subdev_internal_ops adv748x_csi2_internal_ops = { + .init_state = adv748x_csi2_init_state, .registered = adv748x_csi2_registered, }; @@ -139,39 +174,55 @@ static const struct v4l2_subdev_video_ops adv748x_csi2_video_ops = { * But we must support setting the pad formats for format propagation. */ -static struct v4l2_mbus_framefmt * -adv748x_csi2_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - unsigned int pad, u32 which) +static int adv748x_csi2_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) { struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); + const unsigned int *codes = is_txa(tx) ? + adv748x_csi2_txa_fmts : + adv748x_csi2_txb_fmts; + size_t num_fmts = is_txa(tx) ? ARRAY_SIZE(adv748x_csi2_txa_fmts) + : ARRAY_SIZE(adv748x_csi2_txb_fmts); - if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_state_get_format(sd_state, pad); + /* + * The format available on the source pad is the one applied on the sink + * pad. + */ + if (code->pad == ADV748X_CSI2_SOURCE) { + struct v4l2_mbus_framefmt *fmt; - return &tx->format; -} + if (code->index) + return -EINVAL; -static int adv748x_csi2_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *sdformat) -{ - struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); - struct adv748x_state *state = tx->state; - struct v4l2_mbus_framefmt *mbusformat; + fmt = v4l2_subdev_state_get_format(sd_state, ADV748X_CSI2_SINK); + code->code = fmt->code; - mbusformat = adv748x_csi2_get_pad_format(sd, sd_state, sdformat->pad, - sdformat->which); - if (!mbusformat) + return 0; + } + + if (code->index >= num_fmts) return -EINVAL; - mutex_lock(&state->mutex); + code->code = codes[code->index]; - sdformat->format = *mbusformat; + return 0; +} - mutex_unlock(&state->mutex); +static bool adv748x_csi2_is_fmt_supported(struct adv748x_csi2 *tx, u32 code) +{ + const unsigned int *codes = is_txa(tx) ? + adv748x_csi2_txa_fmts : + adv748x_csi2_txb_fmts; + size_t num_fmts = is_txa(tx) ? ARRAY_SIZE(adv748x_csi2_txa_fmts) + : ARRAY_SIZE(adv748x_csi2_txb_fmts); + + for (unsigned int i = 0; i < num_fmts; i++) { + if (codes[i] == code) + return true; + } - return 0; + return false; } static int adv748x_csi2_set_format(struct v4l2_subdev *sd, @@ -179,38 +230,26 @@ static int adv748x_csi2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_format *sdformat) { struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); - struct adv748x_state *state = tx->state; struct v4l2_mbus_framefmt *mbusformat; - int ret = 0; - - mbusformat = adv748x_csi2_get_pad_format(sd, sd_state, sdformat->pad, - sdformat->which); - if (!mbusformat) - return -EINVAL; - - mutex_lock(&state->mutex); - if (sdformat->pad == ADV748X_CSI2_SOURCE) { - const struct v4l2_mbus_framefmt *sink_fmt; + if (sdformat->pad == ADV748X_CSI2_SOURCE) + return v4l2_subdev_get_fmt(sd, sd_state, sdformat); - sink_fmt = adv748x_csi2_get_pad_format(sd, sd_state, - ADV748X_CSI2_SINK, - sdformat->which); - - if (!sink_fmt) { - ret = -EINVAL; - goto unlock; - } - - sdformat->format = *sink_fmt; - } + /* + * Make sure the format is supported, if not default it to + * UYVY8 as it's supported by both TXes. + */ + if (!adv748x_csi2_is_fmt_supported(tx, sdformat->format.code)) + sdformat->format.code = MEDIA_BUS_FMT_UYVY8_1X16; + mbusformat = v4l2_subdev_state_get_format(sd_state, sdformat->pad); *mbusformat = sdformat->format; -unlock: - mutex_unlock(&state->mutex); + /* Propagate format to the source pad. */ + mbusformat = v4l2_subdev_state_get_format(sd_state, ADV748X_CSI2_SOURCE); + *mbusformat = sdformat->format; - return ret; + return 0; } static int adv748x_csi2_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad, @@ -228,7 +267,8 @@ static int adv748x_csi2_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad } static const struct v4l2_subdev_pad_ops adv748x_csi2_pad_ops = { - .get_fmt = adv748x_csi2_get_format, + .enum_mbus_code = adv748x_csi2_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = adv748x_csi2_set_format, .get_mbus_config = adv748x_csi2_get_mbus_config, }; @@ -320,6 +360,11 @@ int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx) if (ret) goto err_cleanup_subdev; + tx->sd.state_lock = &state->mutex; + ret = v4l2_subdev_init_finalize(&tx->sd); + if (ret) + goto err_free_ctrl; + ret = v4l2_async_register_subdev(&tx->sd); if (ret) goto err_free_ctrl; diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h index d2b5e722e997..9bc0121d0eff 100644 --- a/drivers/media/i2c/adv748x/adv748x.h +++ b/drivers/media/i2c/adv748x/adv748x.h @@ -75,7 +75,6 @@ enum adv748x_csi2_pads { struct adv748x_csi2 { struct adv748x_state *state; - struct v4l2_mbus_framefmt format; unsigned int page; unsigned int port; unsigned int num_lanes; diff --git a/drivers/media/i2c/adv7511-v4l2.c b/drivers/media/i2c/adv7511-v4l2.c index 79946e9c7401..261871be833f 100644 --- a/drivers/media/i2c/adv7511-v4l2.c +++ b/drivers/media/i2c/adv7511-v4l2.c @@ -62,11 +62,6 @@ MODULE_LICENSE("GPL v2"); ********************************************************************** */ -struct i2c_reg_value { - unsigned char reg; - unsigned char value; -}; - struct adv7511_state_edid { /* total number of blocks */ u32 blocks; diff --git a/drivers/media/i2c/alvium-csi2.c b/drivers/media/i2c/alvium-csi2.c index e65702e3f73e..5ddfd3dcb188 100644 --- a/drivers/media/i2c/alvium-csi2.c +++ b/drivers/media/i2c/alvium-csi2.c @@ -403,21 +403,22 @@ static int alvium_get_bcrm_vers(struct alvium_dev *alvium) static int alvium_get_fw_version(struct alvium_dev *alvium) { struct device *dev = &alvium->i2c_client->dev; - u64 spec, maj, min, pat; - int ret = 0; + u64 val; + int ret; - ret = alvium_read(alvium, REG_BCRM_DEVICE_FW_SPEC_VERSION_R, - &spec, &ret); - ret = alvium_read(alvium, REG_BCRM_DEVICE_FW_MAJOR_VERSION_R, - &maj, &ret); - ret = alvium_read(alvium, REG_BCRM_DEVICE_FW_MINOR_VERSION_R, - &min, &ret); - ret = alvium_read(alvium, REG_BCRM_DEVICE_FW_PATCH_VERSION_R, - &pat, &ret); + ret = alvium_read(alvium, REG_BCRM_DEVICE_FW, &val, NULL); if (ret) return ret; - dev_info(dev, "fw version: %llu.%llu.%llu.%llu\n", spec, maj, min, pat); + dev_info(dev, "fw version: %02u.%02u.%04u.%08x\n", + (u8)((val & BCRM_DEVICE_FW_SPEC_MASK) >> + BCRM_DEVICE_FW_SPEC_SHIFT), + (u8)((val & BCRM_DEVICE_FW_MAJOR_MASK) >> + BCRM_DEVICE_FW_MAJOR_SHIFT), + (u16)((val & BCRM_DEVICE_FW_MINOR_MASK) >> + BCRM_DEVICE_FW_MINOR_SHIFT), + (u32)((val & BCRM_DEVICE_FW_PATCH_MASK) >> + BCRM_DEVICE_FW_PATCH_SHIFT)); return 0; } @@ -1188,6 +1189,20 @@ static int alvium_set_frame_rate(struct alvium_dev *alvium, u64 fr) struct device *dev = &alvium->i2c_client->dev; int ret; + ret = alvium_write_hshake(alvium, REG_BCRM_ACQUISITION_FRAME_RATE_EN_RW, + 1); + if (ret) { + dev_err(dev, "Fail to set acquisition frame rate enable reg\n"); + return ret; + } + + ret = alvium_write_hshake(alvium, REG_BCRM_FRAME_START_TRIGGER_MODE_RW, + 0); + if (ret) { + dev_err(dev, "Fail to set frame start trigger mode reg\n"); + return ret; + } + ret = alvium_write_hshake(alvium, REG_BCRM_ACQUISITION_FRAME_RATE_RW, fr); if (ret) { @@ -1707,6 +1722,27 @@ alvium_code_to_pixfmt(struct alvium_dev *alvium, u32 code) return &alvium->alvium_csi2_fmt[0]; } +static int alvium_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct alvium_dev *alvium = sd_to_alvium(sd); + const struct alvium_pixfmt *alvium_csi2_fmt; + + if (fse->index) + return -EINVAL; + + alvium_csi2_fmt = alvium_code_to_pixfmt(alvium, fse->code); + if (fse->code != alvium_csi2_fmt->code) + return -EINVAL; + + fse->min_width = alvium->img_min_width; + fse->max_width = alvium->img_max_width; + fse->min_height = alvium->img_min_height; + fse->max_height = alvium->img_max_height; + return 0; +} + static int alvium_set_mode(struct alvium_dev *alvium, struct v4l2_subdev_state *state) { @@ -1962,7 +1998,7 @@ static int alvium_g_volatile_ctrl(struct v4l2_ctrl *ctrl) int val; switch (ctrl->id) { - case V4L2_CID_GAIN: + case V4L2_CID_ANALOGUE_GAIN: val = alvium_get_gain(alvium); if (val < 0) return val; @@ -1994,7 +2030,7 @@ static int alvium_s_ctrl(struct v4l2_ctrl *ctrl) return 0; switch (ctrl->id) { - case V4L2_CID_GAIN: + case V4L2_CID_ANALOGUE_GAIN: ret = alvium_set_ctrl_gain(alvium, ctrl->val); break; case V4L2_CID_AUTOGAIN: @@ -2123,7 +2159,7 @@ static int alvium_ctrl_init(struct alvium_dev *alvium) if (alvium->avail_ft.gain) { ctrls->gain = v4l2_ctrl_new_std(hdl, ops, - V4L2_CID_GAIN, + V4L2_CID_ANALOGUE_GAIN, alvium->min_gain, alvium->max_gain, alvium->inc_gain, @@ -2214,6 +2250,7 @@ static const struct v4l2_subdev_video_ops alvium_video_ops = { static const struct v4l2_subdev_pad_ops alvium_pad_ops = { .enum_mbus_code = alvium_enum_mbus_code, + .enum_frame_size = alvium_enum_frame_size, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = alvium_set_fmt, .get_selection = alvium_get_selection, diff --git a/drivers/media/i2c/alvium-csi2.h b/drivers/media/i2c/alvium-csi2.h index 9463f8604fbc..978af44f76c7 100644 --- a/drivers/media/i2c/alvium-csi2.h +++ b/drivers/media/i2c/alvium-csi2.h @@ -31,10 +31,7 @@ #define REG_BCRM_REG_ADDR_R CCI_REG16(0x0014) #define REG_BCRM_FEATURE_INQUIRY_R REG_BCRM_V4L2_64BIT(0x0008) -#define REG_BCRM_DEVICE_FW_SPEC_VERSION_R REG_BCRM_V4L2_8BIT(0x0010) -#define REG_BCRM_DEVICE_FW_MAJOR_VERSION_R REG_BCRM_V4L2_8BIT(0x0011) -#define REG_BCRM_DEVICE_FW_MINOR_VERSION_R REG_BCRM_V4L2_16BIT(0x0012) -#define REG_BCRM_DEVICE_FW_PATCH_VERSION_R REG_BCRM_V4L2_32BIT(0x0014) +#define REG_BCRM_DEVICE_FW REG_BCRM_V4L2_64BIT(0x0010) #define REG_BCRM_WRITE_HANDSHAKE_RW REG_BCRM_V4L2_8BIT(0x0018) /* Streaming Control Registers */ @@ -66,7 +63,7 @@ #define REG_BCRM_ACQUISITION_FRAME_RATE_MIN_R REG_BCRM_V4L2_64BIT(0x0098) #define REG_BCRM_ACQUISITION_FRAME_RATE_MAX_R REG_BCRM_V4L2_64BIT(0x00a0) #define REG_BCRM_ACQUISITION_FRAME_RATE_INC_R REG_BCRM_V4L2_64BIT(0x00a8) -#define REG_BCRM_ACQUISITION_FRAME_RATE_ENABLE_RW REG_BCRM_V4L2_8BIT(0x00b0) +#define REG_BCRM_ACQUISITION_FRAME_RATE_EN_RW REG_BCRM_V4L2_8BIT(0x00b0) #define REG_BCRM_FRAME_START_TRIGGER_MODE_RW REG_BCRM_V4L2_8BIT(0x00b4) #define REG_BCRM_FRAME_START_TRIGGER_SOURCE_RW REG_BCRM_V4L2_8BIT(0x00b8) @@ -205,6 +202,15 @@ #define ALVIUM_LP2HS_DELAY_MS 100 +#define BCRM_DEVICE_FW_MAJOR_MASK GENMASK_ULL(15, 8) +#define BCRM_DEVICE_FW_MAJOR_SHIFT 8 +#define BCRM_DEVICE_FW_MINOR_MASK GENMASK_ULL(31, 16) +#define BCRM_DEVICE_FW_MINOR_SHIFT 16 +#define BCRM_DEVICE_FW_PATCH_MASK GENMASK_ULL(63, 32) +#define BCRM_DEVICE_FW_PATCH_SHIFT 32 +#define BCRM_DEVICE_FW_SPEC_MASK GENMASK_ULL(7, 0) +#define BCRM_DEVICE_FW_SPEC_SHIFT 0 + enum alvium_bcrm_mode { ALVIUM_BCM_MODE, ALVIUM_GENCP_MODE, diff --git a/drivers/media/i2c/dw9768.c b/drivers/media/i2c/dw9768.c index daabbece8c7e..18ef2b35c9aa 100644 --- a/drivers/media/i2c/dw9768.c +++ b/drivers/media/i2c/dw9768.c @@ -115,11 +115,6 @@ static inline struct dw9768 *sd_to_dw9768(struct v4l2_subdev *subdev) return container_of(subdev, struct dw9768, sd); } -struct regval_list { - u8 reg_num; - u8 value; -}; - struct dw9768_aac_mode_ot_multi { u32 aac_mode_enum; u32 ot_multi_base100; diff --git a/drivers/media/i2c/gc05a2.c b/drivers/media/i2c/gc05a2.c new file mode 100644 index 000000000000..dcba29ee725c --- /dev/null +++ b/drivers/media/i2c/gc05a2.c @@ -0,0 +1,1359 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for GalaxyCore gc05a2 image sensor + * + * Copyright 2024 MediaTek + * + * Zhi Mao <zhi.mao@mediatek.com> + */ +#include <linux/array_size.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/container_of.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/math64.h> +#include <linux/mod_devicetable.h> +#include <linux/pm_runtime.h> +#include <linux/property.h> +#include <linux/regulator/consumer.h> +#include <linux/types.h> +#include <linux/units.h> + +#include <media/v4l2-cci.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> + +#define GC05A2_REG_TEST_PATTERN_EN CCI_REG8(0x008c) +#define GC05A2_REG_TEST_PATTERN_IDX CCI_REG8(0x008d) +#define GC05A2_TEST_PATTERN_EN 0x01 + +#define GC05A2_STREAMING_REG CCI_REG8(0x0100) + +#define GC05A2_FLIP_REG CCI_REG8(0x0101) +#define GC05A2_FLIP_H_MASK BIT(0) +#define GC05A2_FLIP_V_MASK BIT(1) + +#define GC05A2_EXP_REG CCI_REG16(0x0202) +#define GC05A2_EXP_MARGIN 16 +#define GC05A2_EXP_MIN 4 +#define GC05A2_EXP_STEP 1 + +#define GC05A2_AGAIN_REG CCI_REG16(0x0204) +#define GC05A2_AGAIN_MIN 1024 +#define GC05A2_AGAIN_MAX (1024 * 16) +#define GC05A2_AGAIN_STEP 1 + +#define GC05A2_FRAME_LENGTH_REG CCI_REG16(0x0340) +#define GC05A2_VTS_MAX 0xffff + +#define GC05A2_REG_CHIP_ID CCI_REG16(0x03f0) +#define GC05A2_CHIP_ID 0x05a2 + +#define GC05A2_NATIVE_WIDTH 2592 +#define GC05A2_NATIVE_HEIGHT 1944 + +#define GC05A2_DEFAULT_CLK_FREQ (24 * HZ_PER_MHZ) +#define GC05A2_MBUS_CODE MEDIA_BUS_FMT_SGRBG10_1X10 +#define GC05A2_DATA_LANES 2 +#define GC05A2_RGB_DEPTH 10 +#define GC05A2_SLEEP_US (2 * USEC_PER_MSEC) + +static const char *const gc05a2_test_pattern_menu[] = { + "No Pattern", "Fade_to_gray_Color Bar", "Color Bar", + "PN9", "Horizental_gradient", "Checkboard Pattern", + "Slant", "Resolution", "Solid Black", + "Solid White", +}; + +static const s64 gc05a2_link_freq_menu_items[] = { + (448 * HZ_PER_MHZ), + (224 * HZ_PER_MHZ), +}; + +static const char *const gc05a2_supply_name[] = { + "avdd", + "dvdd", + "dovdd", +}; + +struct gc05a2 { + struct device *dev; + struct v4l2_subdev sd; + struct media_pad pad; + + struct clk *xclk; + struct regulator_bulk_data supplies[ARRAY_SIZE(gc05a2_supply_name)]; + struct gpio_desc *reset_gpio; + + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + + struct regmap *regmap; + unsigned long link_freq_bitmap; + + /* True if the device has been identified */ + bool identified; + const struct gc05a2_mode *cur_mode; +}; + +struct gc05a2_reg_list { + u32 num_of_regs; + const struct cci_reg_sequence *regs; +}; + +static const struct cci_reg_sequence mode_2592x1944[] = { + /* system */ + { CCI_REG8(0x0135), 0x01 }, + { CCI_REG8(0x0084), 0x21 }, + { CCI_REG8(0x0d05), 0xcc }, + { CCI_REG8(0x0218), 0x00 }, + { CCI_REG8(0x005e), 0x48 }, + { CCI_REG8(0x0d06), 0x01 }, + { CCI_REG8(0x0007), 0x16 }, + { CCI_REG8(0x0101), 0x00 }, + + /* analog */ + { CCI_REG8(0x0342), 0x07 }, + { CCI_REG8(0x0343), 0x28 }, + { CCI_REG8(0x0220), 0x07 }, + { CCI_REG8(0x0221), 0xd0 }, + { CCI_REG8(0x0202), 0x07 }, + { CCI_REG8(0x0203), 0x32 }, + { CCI_REG8(0x0340), 0x07 }, + { CCI_REG8(0x0341), 0xf0 }, + { CCI_REG8(0x0219), 0x00 }, + { CCI_REG8(0x0346), 0x00 }, + { CCI_REG8(0x0347), 0x04 }, + { CCI_REG8(0x0d14), 0x00 }, + { CCI_REG8(0x0d13), 0x05 }, + { CCI_REG8(0x0d16), 0x05 }, + { CCI_REG8(0x0d15), 0x1d }, + { CCI_REG8(0x00c0), 0x0a }, + { CCI_REG8(0x00c1), 0x30 }, + { CCI_REG8(0x034a), 0x07 }, + { CCI_REG8(0x034b), 0xa8 }, + { CCI_REG8(0x0e0a), 0x00 }, + { CCI_REG8(0x0e0b), 0x00 }, + { CCI_REG8(0x0e0e), 0x03 }, + { CCI_REG8(0x0e0f), 0x00 }, + { CCI_REG8(0x0e06), 0x0a }, + { CCI_REG8(0x0e23), 0x15 }, + { CCI_REG8(0x0e24), 0x15 }, + { CCI_REG8(0x0e2a), 0x10 }, + { CCI_REG8(0x0e2b), 0x10 }, + { CCI_REG8(0x0e17), 0x49 }, + { CCI_REG8(0x0e1b), 0x1c }, + { CCI_REG8(0x0e3a), 0x36 }, + { CCI_REG8(0x0d11), 0x84 }, + { CCI_REG8(0x0e52), 0x14 }, + { CCI_REG8(0x000b), 0x10 }, + { CCI_REG8(0x0008), 0x08 }, + { CCI_REG8(0x0223), 0x17 }, + { CCI_REG8(0x0d27), 0x39 }, + { CCI_REG8(0x0d22), 0x00 }, + { CCI_REG8(0x03f6), 0x0d }, + { CCI_REG8(0x0d04), 0x07 }, + { CCI_REG8(0x03f3), 0x72 }, + { CCI_REG8(0x03f4), 0xb8 }, + { CCI_REG8(0x03f5), 0xbc }, + { CCI_REG8(0x0d02), 0x73 }, + + /* auto load start */ + { CCI_REG8(0x00cb), 0x00 }, + + /* OUT 2592*1944 */ + { CCI_REG8(0x0350), 0x01 }, + { CCI_REG8(0x0353), 0x00 }, + { CCI_REG8(0x0354), 0x08 }, + { CCI_REG16(0x034c), 2592 }, /* Width */ + { CCI_REG8(0x021f), 0x14 }, + + /* MIPI */ + { CCI_REG8(0x0107), 0x05 }, + { CCI_REG8(0x0117), 0x01 }, + { CCI_REG8(0x0d81), 0x00 }, + { CCI_REG8(0x0d84), 0x0c }, + { CCI_REG8(0x0d85), 0xa8 }, + { CCI_REG8(0x0d86), 0x06 }, + { CCI_REG8(0x0d87), 0x55 }, + { CCI_REG8(0x0db3), 0x06 }, + { CCI_REG8(0x0db4), 0x08 }, + { CCI_REG8(0x0db5), 0x1e }, + { CCI_REG8(0x0db6), 0x02 }, + { CCI_REG8(0x0db8), 0x12 }, + { CCI_REG8(0x0db9), 0x0a }, + { CCI_REG8(0x0d93), 0x06 }, + { CCI_REG8(0x0d94), 0x09 }, + { CCI_REG8(0x0d95), 0x0d }, + { CCI_REG8(0x0d99), 0x0b }, + { CCI_REG8(0x0084), 0x01 }, + { CCI_REG8(0x0110), 0x01 }, +}; + +static const struct cci_reg_sequence mode_1280x720[] = { + /* system */ + { CCI_REG8(0x0135), 0x05 }, + { CCI_REG8(0x0084), 0x21 }, + { CCI_REG8(0x0d05), 0xcc }, + { CCI_REG8(0x0218), 0x80 }, + { CCI_REG8(0x005e), 0x49 }, + { CCI_REG8(0x0d06), 0x81 }, + { CCI_REG8(0x0007), 0x16 }, + { CCI_REG8(0x0101), 0x00 }, + + /* analog */ + { CCI_REG8(0x0342), 0x07 }, + { CCI_REG8(0x0343), 0x10 }, + { CCI_REG8(0x0220), 0x07 }, + { CCI_REG8(0x0221), 0xd0 }, + { CCI_REG8(0x0202), 0x03 }, + { CCI_REG8(0x0203), 0x32 }, + { CCI_REG8(0x0340), 0x04 }, + { CCI_REG8(0x0341), 0x08 }, + { CCI_REG8(0x0219), 0x00 }, + { CCI_REG8(0x0346), 0x01 }, + { CCI_REG8(0x0347), 0x00 }, + { CCI_REG8(0x0d14), 0x00 }, + { CCI_REG8(0x0d13), 0x05 }, + { CCI_REG8(0x0d16), 0x05 }, + { CCI_REG8(0x0d15), 0x1d }, + { CCI_REG8(0x00c0), 0x0a }, + { CCI_REG8(0x00c1), 0x30 }, + { CCI_REG8(0x034a), 0x05 }, + { CCI_REG8(0x034b), 0xb0 }, + { CCI_REG8(0x0e0a), 0x00 }, + { CCI_REG8(0x0e0b), 0x00 }, + { CCI_REG8(0x0e0e), 0x03 }, + { CCI_REG8(0x0e0f), 0x00 }, + { CCI_REG8(0x0e06), 0x0a }, + { CCI_REG8(0x0e23), 0x15 }, + { CCI_REG8(0x0e24), 0x15 }, + { CCI_REG8(0x0e2a), 0x10 }, + { CCI_REG8(0x0e2b), 0x10 }, + { CCI_REG8(0x0e17), 0x49 }, + { CCI_REG8(0x0e1b), 0x1c }, + { CCI_REG8(0x0e3a), 0x36 }, + { CCI_REG8(0x0d11), 0x84 }, + { CCI_REG8(0x0e52), 0x14 }, + { CCI_REG8(0x000b), 0x0e }, + { CCI_REG8(0x0008), 0x03 }, + { CCI_REG8(0x0223), 0x16 }, + { CCI_REG8(0x0d27), 0x39 }, + { CCI_REG8(0x0d22), 0x00 }, + { CCI_REG8(0x03f6), 0x0d }, + { CCI_REG8(0x0d04), 0x07 }, + { CCI_REG8(0x03f3), 0x72 }, + { CCI_REG8(0x03f4), 0xb8 }, + { CCI_REG8(0x03f5), 0xbc }, + { CCI_REG8(0x0d02), 0x73 }, + + /* auto load start */ + { CCI_REG8(0x00cb), 0xfc }, + + /* OUT 1280x720 */ + { CCI_REG8(0x0350), 0x01 }, + { CCI_REG8(0x0353), 0x00 }, + { CCI_REG8(0x0354), 0x0c }, + { CCI_REG16(0x034c), 1280 }, /* Width */ + { CCI_REG8(0x021f), 0x14 }, + + /* MIPI */ + { CCI_REG8(0x0107), 0x05 }, + { CCI_REG8(0x0117), 0x01 }, + { CCI_REG8(0x0d81), 0x00 }, + { CCI_REG8(0x0d84), 0x06 }, + { CCI_REG8(0x0d85), 0x40 }, + { CCI_REG8(0x0d86), 0x03 }, + { CCI_REG8(0x0d87), 0x21 }, + { CCI_REG8(0x0db3), 0x03 }, + { CCI_REG8(0x0db4), 0x04 }, + { CCI_REG8(0x0db5), 0x0d }, + { CCI_REG8(0x0db6), 0x01 }, + { CCI_REG8(0x0db8), 0x04 }, + { CCI_REG8(0x0db9), 0x06 }, + { CCI_REG8(0x0d93), 0x03 }, + { CCI_REG8(0x0d94), 0x04 }, + { CCI_REG8(0x0d95), 0x05 }, + { CCI_REG8(0x0d99), 0x06 }, + { CCI_REG8(0x0084), 0x01 }, + { CCI_REG8(0x0110), 0x01 }, +}; + +static const struct cci_reg_sequence mode_table_common[] = { + { GC05A2_STREAMING_REG, 0x00 }, + /* system */ + { CCI_REG8(0x0315), 0xd4 }, + { CCI_REG8(0x0d06), 0x01 }, + { CCI_REG8(0x0a70), 0x80 }, + { CCI_REG8(0x031a), 0x00 }, + { CCI_REG8(0x0314), 0x00 }, + { CCI_REG8(0x0130), 0x08 }, + { CCI_REG8(0x0132), 0x01 }, + { CCI_REG8(0x0136), 0x38 }, + { CCI_REG8(0x0137), 0x03 }, + { CCI_REG8(0x0134), 0x5b }, + { CCI_REG8(0x031c), 0xe0 }, + { CCI_REG8(0x0d82), 0x14 }, + { CCI_REG8(0x0dd1), 0x56 }, + { CCI_REG8(0x0af4), 0x01 }, + { CCI_REG8(0x0002), 0x10 }, + { CCI_REG8(0x00c3), 0x34 }, + { CCI_REG8(0x00c4), 0x00 }, + { CCI_REG8(0x00c5), 0x01 }, + { CCI_REG8(0x0af6), 0x00 }, + { CCI_REG8(0x0ba0), 0x17 }, + { CCI_REG8(0x0ba1), 0x00 }, + { CCI_REG8(0x0ba2), 0x00 }, + { CCI_REG8(0x0ba3), 0x00 }, + { CCI_REG8(0x0ba4), 0x03 }, + { CCI_REG8(0x0ba5), 0x00 }, + { CCI_REG8(0x0ba6), 0x00 }, + { CCI_REG8(0x0ba7), 0x00 }, + { CCI_REG8(0x0ba8), 0x40 }, + { CCI_REG8(0x0ba9), 0x00 }, + { CCI_REG8(0x0baa), 0x00 }, + { CCI_REG8(0x0bab), 0x00 }, + { CCI_REG8(0x0bac), 0x40 }, + { CCI_REG8(0x0bad), 0x00 }, + { CCI_REG8(0x0bae), 0x00 }, + { CCI_REG8(0x0baf), 0x00 }, + { CCI_REG8(0x0bb0), 0x02 }, + { CCI_REG8(0x0bb1), 0x00 }, + { CCI_REG8(0x0bb2), 0x00 }, + { CCI_REG8(0x0bb3), 0x00 }, + { CCI_REG8(0x0bb8), 0x02 }, + { CCI_REG8(0x0bb9), 0x00 }, + { CCI_REG8(0x0bba), 0x00 }, + { CCI_REG8(0x0bbb), 0x00 }, + { CCI_REG8(0x0a70), 0x80 }, + { CCI_REG8(0x0a71), 0x00 }, + { CCI_REG8(0x0a72), 0x00 }, + { CCI_REG8(0x0a66), 0x00 }, + { CCI_REG8(0x0a67), 0x80 }, + { CCI_REG8(0x0a4d), 0x4e }, + { CCI_REG8(0x0a50), 0x00 }, + { CCI_REG8(0x0a4f), 0x0c }, + { CCI_REG8(0x0a66), 0x00 }, + { CCI_REG8(0x00ca), 0x00 }, + { CCI_REG8(0x00cc), 0x00 }, + { CCI_REG8(0x00cd), 0x00 }, + { CCI_REG8(0x0aa1), 0x00 }, + { CCI_REG8(0x0aa2), 0xe0 }, + { CCI_REG8(0x0aa3), 0x00 }, + { CCI_REG8(0x0aa4), 0x40 }, + { CCI_REG8(0x0a90), 0x03 }, + { CCI_REG8(0x0a91), 0x0e }, + { CCI_REG8(0x0a94), 0x80 }, + { CCI_REG8(0x0af6), 0x20 }, + { CCI_REG8(0x0b00), 0x91 }, + { CCI_REG8(0x0b01), 0x17 }, + { CCI_REG8(0x0b02), 0x01 }, + { CCI_REG8(0x0b03), 0x00 }, + { CCI_REG8(0x0b04), 0x01 }, + { CCI_REG8(0x0b05), 0x17 }, + { CCI_REG8(0x0b06), 0x01 }, + { CCI_REG8(0x0b07), 0x00 }, + { CCI_REG8(0x0ae9), 0x01 }, + { CCI_REG8(0x0aea), 0x02 }, + { CCI_REG8(0x0ae8), 0x53 }, + { CCI_REG8(0x0ae8), 0x43 }, + { CCI_REG8(0x0af6), 0x30 }, + { CCI_REG8(0x0b00), 0x08 }, + { CCI_REG8(0x0b01), 0x0f }, + { CCI_REG8(0x0b02), 0x00 }, + { CCI_REG8(0x0b04), 0x1c }, + { CCI_REG8(0x0b05), 0x24 }, + { CCI_REG8(0x0b06), 0x00 }, + { CCI_REG8(0x0b08), 0x30 }, + { CCI_REG8(0x0b09), 0x40 }, + { CCI_REG8(0x0b0a), 0x00 }, + { CCI_REG8(0x0b0c), 0x0e }, + { CCI_REG8(0x0b0d), 0x2a }, + { CCI_REG8(0x0b0e), 0x00 }, + { CCI_REG8(0x0b10), 0x0e }, + { CCI_REG8(0x0b11), 0x2b }, + { CCI_REG8(0x0b12), 0x00 }, + { CCI_REG8(0x0b14), 0x0e }, + { CCI_REG8(0x0b15), 0x23 }, + { CCI_REG8(0x0b16), 0x00 }, + { CCI_REG8(0x0b18), 0x0e }, + { CCI_REG8(0x0b19), 0x24 }, + { CCI_REG8(0x0b1a), 0x00 }, + { CCI_REG8(0x0b1c), 0x0c }, + { CCI_REG8(0x0b1d), 0x0c }, + { CCI_REG8(0x0b1e), 0x00 }, + { CCI_REG8(0x0b20), 0x03 }, + { CCI_REG8(0x0b21), 0x03 }, + { CCI_REG8(0x0b22), 0x00 }, + { CCI_REG8(0x0b24), 0x0e }, + { CCI_REG8(0x0b25), 0x0e }, + { CCI_REG8(0x0b26), 0x00 }, + { CCI_REG8(0x0b28), 0x03 }, + { CCI_REG8(0x0b29), 0x03 }, + { CCI_REG8(0x0b2a), 0x00 }, + { CCI_REG8(0x0b2c), 0x12 }, + { CCI_REG8(0x0b2d), 0x12 }, + { CCI_REG8(0x0b2e), 0x00 }, + { CCI_REG8(0x0b30), 0x08 }, + { CCI_REG8(0x0b31), 0x08 }, + { CCI_REG8(0x0b32), 0x00 }, + { CCI_REG8(0x0b34), 0x14 }, + { CCI_REG8(0x0b35), 0x14 }, + { CCI_REG8(0x0b36), 0x00 }, + { CCI_REG8(0x0b38), 0x10 }, + { CCI_REG8(0x0b39), 0x10 }, + { CCI_REG8(0x0b3a), 0x00 }, + { CCI_REG8(0x0b3c), 0x16 }, + { CCI_REG8(0x0b3d), 0x16 }, + { CCI_REG8(0x0b3e), 0x00 }, + { CCI_REG8(0x0b40), 0x10 }, + { CCI_REG8(0x0b41), 0x10 }, + { CCI_REG8(0x0b42), 0x00 }, + { CCI_REG8(0x0b44), 0x19 }, + { CCI_REG8(0x0b45), 0x19 }, + { CCI_REG8(0x0b46), 0x00 }, + { CCI_REG8(0x0b48), 0x16 }, + { CCI_REG8(0x0b49), 0x16 }, + { CCI_REG8(0x0b4a), 0x00 }, + { CCI_REG8(0x0b4c), 0x19 }, + { CCI_REG8(0x0b4d), 0x19 }, + { CCI_REG8(0x0b4e), 0x00 }, + { CCI_REG8(0x0b50), 0x16 }, + { CCI_REG8(0x0b51), 0x16 }, + { CCI_REG8(0x0b52), 0x00 }, + { CCI_REG8(0x0b80), 0x01 }, + { CCI_REG8(0x0b81), 0x00 }, + { CCI_REG8(0x0b82), 0x00 }, + { CCI_REG8(0x0b84), 0x00 }, + { CCI_REG8(0x0b85), 0x00 }, + { CCI_REG8(0x0b86), 0x00 }, + { CCI_REG8(0x0b88), 0x01 }, + { CCI_REG8(0x0b89), 0x6a }, + { CCI_REG8(0x0b8a), 0x00 }, + { CCI_REG8(0x0b8c), 0x00 }, + { CCI_REG8(0x0b8d), 0x01 }, + { CCI_REG8(0x0b8e), 0x00 }, + { CCI_REG8(0x0b90), 0x01 }, + { CCI_REG8(0x0b91), 0xf6 }, + { CCI_REG8(0x0b92), 0x00 }, + { CCI_REG8(0x0b94), 0x00 }, + { CCI_REG8(0x0b95), 0x02 }, + { CCI_REG8(0x0b96), 0x00 }, + { CCI_REG8(0x0b98), 0x02 }, + { CCI_REG8(0x0b99), 0xc4 }, + { CCI_REG8(0x0b9a), 0x00 }, + { CCI_REG8(0x0b9c), 0x00 }, + { CCI_REG8(0x0b9d), 0x03 }, + { CCI_REG8(0x0b9e), 0x00 }, + { CCI_REG8(0x0ba0), 0x03 }, + { CCI_REG8(0x0ba1), 0xd8 }, + { CCI_REG8(0x0ba2), 0x00 }, + { CCI_REG8(0x0ba4), 0x00 }, + { CCI_REG8(0x0ba5), 0x04 }, + { CCI_REG8(0x0ba6), 0x00 }, + { CCI_REG8(0x0ba8), 0x05 }, + { CCI_REG8(0x0ba9), 0x4d }, + { CCI_REG8(0x0baa), 0x00 }, + { CCI_REG8(0x0bac), 0x00 }, + { CCI_REG8(0x0bad), 0x05 }, + { CCI_REG8(0x0bae), 0x00 }, + { CCI_REG8(0x0bb0), 0x07 }, + { CCI_REG8(0x0bb1), 0x3e }, + { CCI_REG8(0x0bb2), 0x00 }, + { CCI_REG8(0x0bb4), 0x00 }, + { CCI_REG8(0x0bb5), 0x06 }, + { CCI_REG8(0x0bb6), 0x00 }, + { CCI_REG8(0x0bb8), 0x0a }, + { CCI_REG8(0x0bb9), 0x1a }, + { CCI_REG8(0x0bba), 0x00 }, + { CCI_REG8(0x0bbc), 0x09 }, + { CCI_REG8(0x0bbd), 0x36 }, + { CCI_REG8(0x0bbe), 0x00 }, + { CCI_REG8(0x0bc0), 0x0e }, + { CCI_REG8(0x0bc1), 0x66 }, + { CCI_REG8(0x0bc2), 0x00 }, + { CCI_REG8(0x0bc4), 0x10 }, + { CCI_REG8(0x0bc5), 0x06 }, + { CCI_REG8(0x0bc6), 0x00 }, + { CCI_REG8(0x02c1), 0xe0 }, + { CCI_REG8(0x0207), 0x04 }, + { CCI_REG8(0x02c2), 0x10 }, + { CCI_REG8(0x02c3), 0x74 }, + { CCI_REG8(0x02c5), 0x09 }, + { CCI_REG8(0x02c1), 0xe0 }, + { CCI_REG8(0x0207), 0x04 }, + { CCI_REG8(0x02c2), 0x10 }, + { CCI_REG8(0x02c5), 0x09 }, + { CCI_REG8(0x02c1), 0xe0 }, + { CCI_REG8(0x0207), 0x04 }, + { CCI_REG8(0x02c2), 0x10 }, + { CCI_REG8(0x02c5), 0x09 }, + { CCI_REG8(0x0aa1), 0x15 }, + { CCI_REG8(0x0aa2), 0x50 }, + { CCI_REG8(0x0aa3), 0x00 }, + { CCI_REG8(0x0aa4), 0x09 }, + { CCI_REG8(0x0a90), 0x25 }, + { CCI_REG8(0x0a91), 0x0e }, + { CCI_REG8(0x0a94), 0x80 }, + + /* ISP */ + { CCI_REG8(0x0050), 0x00 }, + { CCI_REG8(0x0089), 0x83 }, + { CCI_REG8(0x005a), 0x40 }, + { CCI_REG8(0x00c3), 0x35 }, + { CCI_REG8(0x00c4), 0x80 }, + { CCI_REG8(0x0080), 0x10 }, + { CCI_REG8(0x0040), 0x12 }, + { CCI_REG8(0x0053), 0x0a }, + { CCI_REG8(0x0054), 0x44 }, + { CCI_REG8(0x0055), 0x32 }, + { CCI_REG8(0x0058), 0x89 }, + { CCI_REG8(0x004a), 0x03 }, + { CCI_REG8(0x0048), 0xf0 }, + { CCI_REG8(0x0049), 0x0f }, + { CCI_REG8(0x0041), 0x20 }, + { CCI_REG8(0x0043), 0x0a }, + { CCI_REG8(0x009d), 0x08 }, + { CCI_REG8(0x0236), 0x40 }, + { CCI_REG8(0x0204), 0x04 }, + { CCI_REG8(0x0205), 0x00 }, + { CCI_REG8(0x02b3), 0x00 }, + { CCI_REG8(0x02b4), 0x00 }, + { CCI_REG8(0x009e), 0x01 }, + { CCI_REG8(0x009f), 0x94 }, + + /* auto load REG */ + { CCI_REG8(0x0aa1), 0x10 }, + { CCI_REG8(0x0aa2), 0xf8 }, + { CCI_REG8(0x0aa3), 0x00 }, + { CCI_REG8(0x0aa4), 0x1f }, + { CCI_REG8(0x0a90), 0x11 }, + { CCI_REG8(0x0a91), 0x0e }, + { CCI_REG8(0x0a94), 0x80 }, + { CCI_REG8(0x03fe), 0x00 }, + { CCI_REG8(0x0a90), 0x00 }, + { CCI_REG8(0x0a70), 0x00 }, + { CCI_REG8(0x0a67), 0x00 }, + { CCI_REG8(0x0af4), 0x29 }, + + /* DPHY */ + { CCI_REG8(0x0d80), 0x07 }, + { CCI_REG8(0x0dd3), 0x18 }, + + /* CISCTL_Reset */ + { CCI_REG8(0x031c), 0x80 }, + { CCI_REG8(0x03fe), 0x30 }, + { CCI_REG8(0x0d17), 0x06 }, + { CCI_REG8(0x03fe), 0x00 }, + { CCI_REG8(0x0d17), 0x00 }, + { CCI_REG8(0x031c), 0x93 }, + { CCI_REG8(0x03fe), 0x00 }, + { CCI_REG8(0x031c), 0x80 }, + { CCI_REG8(0x03fe), 0x30 }, + { CCI_REG8(0x0d17), 0x06 }, + { CCI_REG8(0x03fe), 0x00 }, + { CCI_REG8(0x0d17), 0x00 }, + { CCI_REG8(0x031c), 0x93 }, +}; + +struct gc05a2_mode { + u32 width; + u32 height; + const struct gc05a2_reg_list reg_list; + + u32 hts; /* Horizontal timining size */ + u32 vts_def; /* Default vertical timining size */ + u32 vts_min; /* Min vertical timining size */ +}; + +/* Declare modes in order, from biggest to smallest height. */ +static const struct gc05a2_mode gc05a2_modes[] = { + { + /* 2592*1944@30fps */ + .width = GC05A2_NATIVE_WIDTH, + .height = GC05A2_NATIVE_HEIGHT, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_2592x1944), + .regs = mode_2592x1944, + }, + .hts = 3664, + .vts_def = 2032, + .vts_min = 2032, + }, + { + /* 1280*720@60fps */ + .width = 1280, + .height = 720, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1280x720), + .regs = mode_1280x720, + }, + .hts = 3616, + .vts_def = 1032, + .vts_min = 1032, + }, +}; + +static inline struct gc05a2 *to_gc05a2(struct v4l2_subdev *sd) +{ + return container_of(sd, struct gc05a2, sd); +} + +static int gc05a2_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct gc05a2 *gc05a2 = to_gc05a2(sd); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(gc05a2_supply_name), + gc05a2->supplies); + if (ret < 0) { + dev_err(gc05a2->dev, "failed to enable regulators: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(gc05a2->xclk); + if (ret < 0) { + regulator_bulk_disable(ARRAY_SIZE(gc05a2_supply_name), + gc05a2->supplies); + dev_err(gc05a2->dev, "clk prepare enable failed\n"); + return ret; + } + + fsleep(GC05A2_SLEEP_US); + + gpiod_set_value_cansleep(gc05a2->reset_gpio, 0); + fsleep(GC05A2_SLEEP_US); + + return 0; +} + +static int gc05a2_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct gc05a2 *gc05a2 = to_gc05a2(sd); + + clk_disable_unprepare(gc05a2->xclk); + gpiod_set_value_cansleep(gc05a2->reset_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(gc05a2_supply_name), + gc05a2->supplies); + + return 0; +} + +static int gc05a2_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = GC05A2_MBUS_CODE; + + return 0; +} + +static int gc05a2_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->code != GC05A2_MBUS_CODE) + return -EINVAL; + + if (fse->index >= ARRAY_SIZE(gc05a2_modes)) + return -EINVAL; + + fse->min_width = gc05a2_modes[fse->index].width; + fse->max_width = gc05a2_modes[fse->index].width; + fse->min_height = gc05a2_modes[fse->index].height; + fse->max_height = gc05a2_modes[fse->index].height; + + return 0; +} + +static int gc05a2_update_cur_mode_controls(struct gc05a2 *gc05a2, + const struct gc05a2_mode *mode) +{ + s64 exposure_max, h_blank; + int ret; + + ret = __v4l2_ctrl_modify_range(gc05a2->vblank, + mode->vts_min - mode->height, + GC05A2_VTS_MAX - mode->height, 1, + mode->vts_def - mode->height); + if (ret) { + dev_err(gc05a2->dev, "VB ctrl range update failed\n"); + return ret; + } + + h_blank = mode->hts - mode->width; + ret = __v4l2_ctrl_modify_range(gc05a2->hblank, h_blank, h_blank, 1, + h_blank); + if (ret) { + dev_err(gc05a2->dev, "HB ctrl range update failed\n"); + return ret; + } + + exposure_max = mode->vts_def - GC05A2_EXP_MARGIN; + ret = __v4l2_ctrl_modify_range(gc05a2->exposure, GC05A2_EXP_MIN, + exposure_max, GC05A2_EXP_STEP, + exposure_max); + if (ret) { + dev_err(gc05a2->dev, "exposure ctrl range update failed\n"); + return ret; + } + + return 0; +} + +static void gc05a2_update_pad_format(struct gc05a2 *gc08a3, + const struct gc05a2_mode *mode, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->width = mode->width; + fmt->height = mode->height; + fmt->code = GC05A2_MBUS_CODE; + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_RAW; + fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); + fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + fmt->xfer_func = V4L2_XFER_FUNC_NONE; +} + +static int gc05a2_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt) +{ + struct gc05a2 *gc05a2 = to_gc05a2(sd); + struct v4l2_mbus_framefmt *mbus_fmt; + struct v4l2_rect *crop; + const struct gc05a2_mode *mode; + + mode = v4l2_find_nearest_size(gc05a2_modes, ARRAY_SIZE(gc05a2_modes), + width, height, fmt->format.width, + fmt->format.height); + + /* update crop info to subdev state */ + crop = v4l2_subdev_state_get_crop(state, 0); + crop->width = mode->width; + crop->height = mode->height; + + /* update fmt info to subdev state */ + gc05a2_update_pad_format(gc05a2, mode, &fmt->format); + mbus_fmt = v4l2_subdev_state_get_format(state, 0); + *mbus_fmt = fmt->format; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + gc05a2->cur_mode = mode; + gc05a2_update_cur_mode_controls(gc05a2, mode); + + return 0; +} + +static int gc05a2_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(state, 0); + break; + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = GC05A2_NATIVE_WIDTH; + sel->r.height = GC05A2_NATIVE_HEIGHT; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int gc05a2_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .pad = 0, + .format = { + .code = GC05A2_MBUS_CODE, + .width = gc05a2_modes[0].width, + .height = gc05a2_modes[0].height, + }, + }; + + gc05a2_set_format(sd, state, &fmt); + + return 0; +} + +static int gc05a2_set_ctrl_hflip(struct gc05a2 *gc05a2, u32 ctrl_val) +{ + int ret; + u64 val; + + ret = cci_read(gc05a2->regmap, GC05A2_FLIP_REG, &val, NULL); + if (ret) { + dev_err(gc05a2->dev, "read hflip register failed: %d\n", ret); + return ret; + } + + return cci_update_bits(gc05a2->regmap, GC05A2_FLIP_REG, + GC05A2_FLIP_H_MASK, + ctrl_val ? GC05A2_FLIP_H_MASK : 0, NULL); +} + +static int gc05a2_set_ctrl_vflip(struct gc05a2 *gc05a2, u32 ctrl_val) +{ + int ret; + u64 val; + + ret = cci_read(gc05a2->regmap, GC05A2_FLIP_REG, &val, NULL); + if (ret) { + dev_err(gc05a2->dev, "read vflip register failed: %d\n", ret); + return ret; + } + + return cci_update_bits(gc05a2->regmap, GC05A2_FLIP_REG, + GC05A2_FLIP_V_MASK, + ctrl_val ? GC05A2_FLIP_V_MASK : 0, NULL); +} + +static int gc05a2_test_pattern(struct gc05a2 *gc05a2, u32 pattern_menu) +{ + u32 pattern; + int ret; + + if (pattern_menu) { + switch (pattern_menu) { + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + pattern = pattern_menu << 4; + break; + + case 8: + pattern = 0; + break; + + case 9: + pattern = 4; + break; + + default: + /* Set pattern to 0, it's a safe default. */ + pattern = 0; + break; + } + + ret = cci_write(gc05a2->regmap, GC05A2_REG_TEST_PATTERN_IDX, + pattern, NULL); + if (ret) + return ret; + + return cci_write(gc05a2->regmap, GC05A2_REG_TEST_PATTERN_EN, + GC05A2_TEST_PATTERN_EN, NULL); + } else { + return cci_write(gc05a2->regmap, GC05A2_REG_TEST_PATTERN_EN, + 0x00, NULL); + } +} + +static int gc05a2_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gc05a2 *gc05a2 = + container_of(ctrl->handler, struct gc05a2, ctrls); + int ret = 0; + s64 exposure_max; + struct v4l2_subdev_state *state; + const struct v4l2_mbus_framefmt *format; + + state = v4l2_subdev_get_locked_active_state(&gc05a2->sd); + format = v4l2_subdev_state_get_format(state, 0); + + if (ctrl->id == V4L2_CID_VBLANK) { + /* Update max exposure while meeting expected vblanking */ + exposure_max = format->height + ctrl->val - GC05A2_EXP_MARGIN; + __v4l2_ctrl_modify_range(gc05a2->exposure, + gc05a2->exposure->minimum, + exposure_max, gc05a2->exposure->step, + exposure_max); + } + + /* + * Applying V4L2 control value only happens + * when power is on for streaming. + */ + if (!pm_runtime_get_if_active(gc05a2->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + ret = cci_write(gc05a2->regmap, GC05A2_EXP_REG, + ctrl->val, NULL); + break; + + case V4L2_CID_ANALOGUE_GAIN: + ret = cci_write(gc05a2->regmap, GC05A2_AGAIN_REG, + ctrl->val, NULL); + break; + + case V4L2_CID_VBLANK: + ret = cci_write(gc05a2->regmap, GC05A2_FRAME_LENGTH_REG, + gc05a2->cur_mode->height + ctrl->val, NULL); + break; + + case V4L2_CID_HFLIP: + ret = gc05a2_set_ctrl_hflip(gc05a2, ctrl->val); + break; + + case V4L2_CID_VFLIP: + ret = gc05a2_set_ctrl_vflip(gc05a2, ctrl->val); + break; + + case V4L2_CID_TEST_PATTERN: + ret = gc05a2_test_pattern(gc05a2, ctrl->val); + break; + + default: + break; + } + + pm_runtime_put(gc05a2->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops gc05a2_ctrl_ops = { + .s_ctrl = gc05a2_set_ctrl, +}; + +static int gc05a2_identify_module(struct gc05a2 *gc05a2) +{ + u64 val; + int ret; + + if (gc05a2->identified) + return 0; + + ret = cci_read(gc05a2->regmap, GC05A2_REG_CHIP_ID, &val, NULL); + if (ret) + return ret; + + if (val != GC05A2_CHIP_ID) { + dev_err(gc05a2->dev, "chip id mismatch: 0x%x!=0x%llx", + GC05A2_CHIP_ID, val); + return -ENXIO; + } + + gc05a2->identified = true; + + return 0; +} + +static int gc05a2_start_streaming(struct gc05a2 *gc05a2) +{ + const struct gc05a2_mode *mode; + const struct gc05a2_reg_list *reg_list; + int ret; + + ret = pm_runtime_resume_and_get(gc05a2->dev); + if (ret < 0) + return ret; + + ret = gc05a2_identify_module(gc05a2); + if (ret) + goto err_rpm_put; + + ret = cci_multi_reg_write(gc05a2->regmap, + mode_table_common, + ARRAY_SIZE(mode_table_common), NULL); + if (ret) + goto err_rpm_put; + + mode = gc05a2->cur_mode; + reg_list = &mode->reg_list; + + ret = cci_multi_reg_write(gc05a2->regmap, + reg_list->regs, reg_list->num_of_regs, NULL); + if (ret < 0) + goto err_rpm_put; + + ret = __v4l2_ctrl_handler_setup(&gc05a2->ctrls); + if (ret < 0) { + dev_err(gc05a2->dev, "could not sync v4l2 controls\n"); + goto err_rpm_put; + } + + ret = cci_write(gc05a2->regmap, GC05A2_STREAMING_REG, 1, NULL); + if (ret < 0) { + dev_err(gc05a2->dev, "write STREAMING_REG failed: %d\n", ret); + goto err_rpm_put; + } + + return 0; + +err_rpm_put: + pm_runtime_put(gc05a2->dev); + return ret; +} + +static int gc05a2_stop_streaming(struct gc05a2 *gc05a2) +{ + int ret; + + ret = cci_write(gc05a2->regmap, GC05A2_STREAMING_REG, 0, NULL); + if (ret < 0) + dev_err(gc05a2->dev, "could not sent stop streaming %d\n", ret); + + pm_runtime_put(gc05a2->dev); + return ret; +} + +static int gc05a2_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct gc05a2 *gc05a2 = to_gc05a2(subdev); + struct v4l2_subdev_state *state; + int ret; + + state = v4l2_subdev_lock_and_get_active_state(subdev); + + if (enable) + ret = gc05a2_start_streaming(gc05a2); + else + ret = gc05a2_stop_streaming(gc05a2); + + v4l2_subdev_unlock_state(state); + + return ret; +} + +static const struct v4l2_subdev_video_ops gc05a2_video_ops = { + .s_stream = gc05a2_s_stream, +}; + +static const struct v4l2_subdev_pad_ops gc05a2_subdev_pad_ops = { + .enum_mbus_code = gc05a2_enum_mbus_code, + .enum_frame_size = gc05a2_enum_frame_size, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = gc05a2_set_format, + .get_selection = gc05a2_get_selection, +}; + +static const struct v4l2_subdev_core_ops gc05a2_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_ops gc05a2_subdev_ops = { + .core = &gc05a2_core_ops, + .video = &gc05a2_video_ops, + .pad = &gc05a2_subdev_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops gc05a2_internal_ops = { + .init_state = gc05a2_init_state, +}; + +static int gc05a2_get_regulators(struct device *dev, struct gc05a2 *gc05a2) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(gc05a2_supply_name); i++) + gc05a2->supplies[i].supply = gc05a2_supply_name[i]; + + return devm_regulator_bulk_get(dev, ARRAY_SIZE(gc05a2_supply_name), + gc05a2->supplies); +} + +static int gc05a2_parse_fwnode(struct gc05a2 *gc05a2) +{ + struct fwnode_handle *endpoint; + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + int ret; + struct device *dev = gc05a2->dev; + + endpoint = + fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!endpoint) + return dev_err_probe(dev, -EINVAL, "Missing endpoint node\n"); + + ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg); + if (ret) { + dev_err_probe(dev, ret, "parsing endpoint node failed\n"); + goto done; + } + + ret = v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies, + bus_cfg.nr_of_link_frequencies, + gc05a2_link_freq_menu_items, + ARRAY_SIZE(gc05a2_link_freq_menu_items), + &gc05a2->link_freq_bitmap); + if (ret) + goto done; + +done: + v4l2_fwnode_endpoint_free(&bus_cfg); + fwnode_handle_put(endpoint); + return ret; +} + +static u64 gc05a2_to_pixel_rate(u32 f_index) +{ + u64 pixel_rate = + gc05a2_link_freq_menu_items[f_index] * 2 * GC05A2_DATA_LANES; + + return div_u64(pixel_rate, GC05A2_RGB_DEPTH); +} + +static int gc05a2_init_controls(struct gc05a2 *gc05a2) +{ + struct i2c_client *client = v4l2_get_subdevdata(&gc05a2->sd); + const struct gc05a2_mode *mode = &gc05a2_modes[0]; + const struct v4l2_ctrl_ops *ops = &gc05a2_ctrl_ops; + struct v4l2_fwnode_device_properties props; + struct v4l2_ctrl_handler *ctrl_hdlr; + s64 exposure_max, h_blank; + int ret; + + ctrl_hdlr = &gc05a2->ctrls; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 9); + if (ret) + return ret; + + gc05a2->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &gc05a2_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + gc05a2->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &gc05a2_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_cluster(2, &gc05a2->hflip); + + gc05a2->link_freq = + v4l2_ctrl_new_int_menu(ctrl_hdlr, + &gc05a2_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(gc05a2_link_freq_menu_items) - 1, + 0, + gc05a2_link_freq_menu_items); + if (gc05a2->link_freq) + gc05a2->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + gc05a2->pixel_rate = + v4l2_ctrl_new_std(ctrl_hdlr, + &gc05a2_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, + gc05a2_to_pixel_rate(0), + 1, + gc05a2_to_pixel_rate(0)); + + gc05a2->vblank = + v4l2_ctrl_new_std(ctrl_hdlr, + &gc05a2_ctrl_ops, V4L2_CID_VBLANK, + mode->vts_min - mode->height, + GC05A2_VTS_MAX - mode->height, 1, + mode->vts_def - mode->height); + + h_blank = mode->hts - mode->width; + gc05a2->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &gc05a2_ctrl_ops, + V4L2_CID_HBLANK, h_blank, h_blank, 1, + h_blank); + if (gc05a2->hblank) + gc05a2->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(ctrl_hdlr, &gc05a2_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, GC05A2_AGAIN_MIN, + GC05A2_AGAIN_MAX, GC05A2_AGAIN_STEP, + GC05A2_AGAIN_MIN); + + exposure_max = mode->vts_def - GC05A2_EXP_MARGIN; + gc05a2->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &gc05a2_ctrl_ops, + V4L2_CID_EXPOSURE, GC05A2_EXP_MIN, + exposure_max, GC05A2_EXP_STEP, + exposure_max); + + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &gc05a2_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(gc05a2_test_pattern_menu) - 1, + 0, 0, gc05a2_test_pattern_menu); + + /* register properties to fwnode (e.g. rotation, orientation) */ + ret = v4l2_fwnode_device_parse(&client->dev, &props); + if (ret) + goto error_ctrls; + + ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, ops, &props); + if (ret) + goto error_ctrls; + + if (ctrl_hdlr->error) { + ret = ctrl_hdlr->error; + goto error_ctrls; + } + + gc05a2->sd.ctrl_handler = ctrl_hdlr; + + return 0; + +error_ctrls: + v4l2_ctrl_handler_free(ctrl_hdlr); + + return ret; +} + +static int gc05a2_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct gc05a2 *gc05a2; + int ret; + + gc05a2 = devm_kzalloc(dev, sizeof(*gc05a2), GFP_KERNEL); + if (!gc05a2) + return -ENOMEM; + + gc05a2->dev = dev; + + ret = gc05a2_parse_fwnode(gc05a2); + if (ret) + return ret; + + gc05a2->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(gc05a2->regmap)) + return dev_err_probe(dev, PTR_ERR(gc05a2->regmap), + "failed to init CCI\n"); + + gc05a2->xclk = devm_clk_get(dev, NULL); + if (IS_ERR(gc05a2->xclk)) + return dev_err_probe(dev, PTR_ERR(gc05a2->xclk), + "failed to get xclk\n"); + + ret = clk_set_rate(gc05a2->xclk, GC05A2_DEFAULT_CLK_FREQ); + if (ret) + return dev_err_probe(dev, ret, + "failed to set xclk frequency\n"); + + ret = gc05a2_get_regulators(dev, gc05a2); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to get regulators\n"); + + gc05a2->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(gc05a2->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(gc05a2->reset_gpio), + "failed to get gpio\n"); + + v4l2_i2c_subdev_init(&gc05a2->sd, client, &gc05a2_subdev_ops); + gc05a2->sd.internal_ops = &gc05a2_internal_ops; + gc05a2->cur_mode = &gc05a2_modes[0]; + + ret = gc05a2_init_controls(gc05a2); + if (ret) + return dev_err_probe(dev, ret, + "failed to init controls\n"); + + gc05a2->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; + gc05a2->pad.flags = MEDIA_PAD_FL_SOURCE; + gc05a2->sd.dev = &client->dev; + gc05a2->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + ret = media_entity_pads_init(&gc05a2->sd.entity, 1, &gc05a2->pad); + if (ret < 0) { + dev_err_probe(dev, ret, "could not register media entity\n"); + goto err_v4l2_ctrl_handler_free; + } + + gc05a2->sd.state_lock = gc05a2->ctrls.lock; + ret = v4l2_subdev_init_finalize(&gc05a2->sd); + if (ret < 0) { + dev_err_probe(dev, ret, "v4l2 subdev init error\n"); + goto err_media_entity_cleanup; + } + + pm_runtime_enable(gc05a2->dev); + pm_runtime_set_autosuspend_delay(gc05a2->dev, 1000); + pm_runtime_use_autosuspend(gc05a2->dev); + pm_runtime_idle(gc05a2->dev); + + ret = v4l2_async_register_subdev_sensor(&gc05a2->sd); + if (ret < 0) { + dev_err_probe(dev, ret, "could not register v4l2 device\n"); + goto err_rpm; + } + + return 0; + +err_rpm: + pm_runtime_disable(gc05a2->dev); + v4l2_subdev_cleanup(&gc05a2->sd); + +err_media_entity_cleanup: + media_entity_cleanup(&gc05a2->sd.entity); + +err_v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(&gc05a2->ctrls); + + return ret; +} + +static void gc05a2_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc05a2 *gc05a2 = to_gc05a2(sd); + + v4l2_async_unregister_subdev(&gc05a2->sd); + v4l2_subdev_cleanup(sd); + media_entity_cleanup(&gc05a2->sd.entity); + v4l2_ctrl_handler_free(&gc05a2->ctrls); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + gc05a2_power_off(gc05a2->dev); + pm_runtime_set_suspended(&client->dev); +} + +static const struct of_device_id gc05a2_of_match[] = { + { .compatible = "galaxycore,gc05a2" }, + {} +}; +MODULE_DEVICE_TABLE(of, gc05a2_of_match); + +static DEFINE_RUNTIME_DEV_PM_OPS(gc05a2_pm_ops, + gc05a2_power_off, + gc05a2_power_on, + NULL); + +static struct i2c_driver gc05a2_i2c_driver = { + .driver = { + .of_match_table = gc05a2_of_match, + .pm = pm_ptr(&gc05a2_pm_ops), + .name = "gc05a2", + }, + .probe = gc05a2_probe, + .remove = gc05a2_remove, +}; +module_i2c_driver(gc05a2_i2c_driver); + +MODULE_DESCRIPTION("GalaxyCore gc05a2 Camera driver"); +MODULE_AUTHOR("Zhi Mao <zhi.mao@mediatek.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/gc08a3.c b/drivers/media/i2c/gc08a3.c new file mode 100644 index 000000000000..7680d807e7a5 --- /dev/null +++ b/drivers/media/i2c/gc08a3.c @@ -0,0 +1,1339 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for GalaxyCore gc08a3 image sensor + * + * Copyright 2024 MediaTek + * + * Zhi Mao <zhi.mao@mediatek.com> + */ +#include <linux/array_size.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/container_of.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/math64.h> +#include <linux/mod_devicetable.h> +#include <linux/pm_runtime.h> +#include <linux/property.h> +#include <linux/regulator/consumer.h> +#include <linux/types.h> +#include <linux/units.h> + +#include <media/v4l2-cci.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> + +#define GC08A3_REG_TEST_PATTERN_EN CCI_REG8(0x008c) +#define GC08A3_REG_TEST_PATTERN_IDX CCI_REG8(0x008d) +#define GC08A3_TEST_PATTERN_EN 0x01 + +#define GC08A3_STREAMING_REG CCI_REG8(0x0100) + +#define GC08A3_FLIP_REG CCI_REG8(0x0101) +#define GC08A3_FLIP_H_MASK BIT(0) +#define GC08A3_FLIP_V_MASK BIT(1) + +#define GC08A3_EXP_REG CCI_REG16(0x0202) +#define GC08A3_EXP_MARGIN 16 +#define GC08A3_EXP_MIN 4 +#define GC08A3_EXP_STEP 1 + +#define GC08A3_AGAIN_REG CCI_REG16(0x0204) +#define GC08A3_AGAIN_MIN 1024 +#define GC08A3_AGAIN_MAX (1024 * 16) +#define GC08A3_AGAIN_STEP 1 + +#define GC08A3_FRAME_LENGTH_REG CCI_REG16(0x0340) +#define GC08A3_VTS_MAX 0xfff0 + +#define GC08A3_REG_CHIP_ID CCI_REG16(0x03f0) +#define GC08A3_CHIP_ID 0x08a3 + +#define GC08A3_NATIVE_WIDTH 3264 +#define GC08A3_NATIVE_HEIGHT 2448 + +#define GC08A3_DEFAULT_CLK_FREQ (24 * HZ_PER_MHZ) +#define GC08A3_MBUS_CODE MEDIA_BUS_FMT_SRGGB10_1X10 +#define GC08A3_DATA_LANES 4 + +#define GC08A3_RGB_DEPTH 10 + +#define GC08A3_SLEEP_US (2 * USEC_PER_MSEC) + +static const char *const gc08a3_test_pattern_menu[] = { + "No Pattern", "Solid Black", "Colour Bar", "Solid White", + "Solid Red", "Solid Green", "Solid Blue", "Solid Yellow", +}; + +static const s64 gc08a3_link_freq_menu_items[] = { + (336 * HZ_PER_MHZ), + (207 * HZ_PER_MHZ), +}; + +static const char *const gc08a3_supply_name[] = { + "avdd", + "dvdd", + "dovdd", +}; + +struct gc08a3 { + struct device *dev; + struct v4l2_subdev sd; + struct media_pad pad; + + struct clk *xclk; + struct regulator_bulk_data supplies[ARRAY_SIZE(gc08a3_supply_name)]; + struct gpio_desc *reset_gpio; + + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + + struct regmap *regmap; + unsigned long link_freq_bitmap; + const struct gc08a3_mode *cur_mode; +}; + +struct gc08a3_reg_list { + u32 num_of_regs; + const struct cci_reg_sequence *regs; +}; + +static const struct cci_reg_sequence mode_3264x2448[] = { + /* system */ + { CCI_REG8(0x0336), 0x70 }, + { CCI_REG8(0x0383), 0xbb }, + { CCI_REG8(0x0344), 0x00 }, + { CCI_REG8(0x0345), 0x06 }, + { CCI_REG8(0x0346), 0x00 }, + { CCI_REG8(0x0347), 0x04 }, + { CCI_REG8(0x0348), 0x0c }, + { CCI_REG8(0x0349), 0xd0 }, + { CCI_REG8(0x034a), 0x09 }, + { CCI_REG8(0x034b), 0x9c }, + { CCI_REG8(0x0202), 0x09 }, + { CCI_REG8(0x0203), 0x04 }, + { CCI_REG8(0x0340), 0x09 }, + { CCI_REG8(0x0341), 0xf4 }, + { CCI_REG8(0x0342), 0x07 }, + { CCI_REG8(0x0343), 0x1c }, + + { CCI_REG8(0x0226), 0x00 }, + { CCI_REG8(0x0227), 0x28 }, + { CCI_REG8(0x0e38), 0x49 }, + { CCI_REG8(0x0210), 0x13 }, + { CCI_REG8(0x0218), 0x00 }, + { CCI_REG8(0x0241), 0x88 }, + { CCI_REG8(0x0392), 0x60 }, + + /* ISP */ + { CCI_REG8(0x00a2), 0x00 }, + { CCI_REG8(0x00a3), 0x00 }, + { CCI_REG8(0x00ab), 0x00 }, + { CCI_REG8(0x00ac), 0x00 }, + + /* GAIN */ + { CCI_REG8(0x0204), 0x04 }, + { CCI_REG8(0x0205), 0x00 }, + { CCI_REG8(0x0050), 0x5c }, + { CCI_REG8(0x0051), 0x44 }, + + /* out window */ + { CCI_REG8(0x009a), 0x66 }, + { CCI_REG8(0x0351), 0x00 }, + { CCI_REG8(0x0352), 0x06 }, + { CCI_REG8(0x0353), 0x00 }, + { CCI_REG8(0x0354), 0x08 }, + { CCI_REG8(0x034c), 0x0c }, + { CCI_REG8(0x034d), 0xc0 }, + { CCI_REG8(0x034e), 0x09 }, + { CCI_REG8(0x034f), 0x90 }, + + /* MIPI */ + { CCI_REG8(0x0114), 0x03 }, + { CCI_REG8(0x0180), 0x65 }, + { CCI_REG8(0x0181), 0xf0 }, + { CCI_REG8(0x0185), 0x01 }, + { CCI_REG8(0x0115), 0x30 }, + { CCI_REG8(0x011b), 0x12 }, + { CCI_REG8(0x011c), 0x12 }, + { CCI_REG8(0x0121), 0x06 }, + { CCI_REG8(0x0122), 0x06 }, + { CCI_REG8(0x0123), 0x15 }, + { CCI_REG8(0x0124), 0x01 }, + { CCI_REG8(0x0125), 0x0b }, + { CCI_REG8(0x0126), 0x08 }, + { CCI_REG8(0x0129), 0x06 }, + { CCI_REG8(0x012a), 0x08 }, + { CCI_REG8(0x012b), 0x08 }, + + { CCI_REG8(0x0a73), 0x60 }, + { CCI_REG8(0x0a70), 0x11 }, + { CCI_REG8(0x0313), 0x80 }, + { CCI_REG8(0x0aff), 0x00 }, + { CCI_REG8(0x0a70), 0x00 }, + { CCI_REG8(0x00a4), 0x80 }, + { CCI_REG8(0x0316), 0x01 }, + { CCI_REG8(0x0a67), 0x00 }, + { CCI_REG8(0x0084), 0x10 }, + { CCI_REG8(0x0102), 0x09 }, +}; + +static const struct cci_reg_sequence mode_1920x1080[] = { + /* system */ + { CCI_REG8(0x0336), 0x45 }, + { CCI_REG8(0x0383), 0x8b }, + { CCI_REG8(0x0344), 0x02 }, + { CCI_REG8(0x0345), 0xa6 }, + { CCI_REG8(0x0346), 0x02 }, + { CCI_REG8(0x0347), 0xb0 }, + { CCI_REG8(0x0348), 0x07 }, + { CCI_REG8(0x0349), 0x90 }, + { CCI_REG8(0x034a), 0x04 }, + { CCI_REG8(0x034b), 0x44 }, + { CCI_REG8(0x0202), 0x03 }, + { CCI_REG8(0x0203), 0x00 }, + { CCI_REG8(0x0340), 0x04 }, + { CCI_REG8(0x0341), 0xfc }, + { CCI_REG8(0x0342), 0x07 }, + { CCI_REG8(0x0343), 0x1c }, + { CCI_REG8(0x0226), 0x00 }, + { CCI_REG8(0x0227), 0x88 }, + { CCI_REG8(0x0e38), 0x49 }, + { CCI_REG8(0x0210), 0x13 }, + { CCI_REG8(0x0218), 0x00 }, + { CCI_REG8(0x0241), 0x88 }, + { CCI_REG8(0x0392), 0x60 }, + + /* ISP */ + { CCI_REG8(0x00a2), 0xac }, + { CCI_REG8(0x00a3), 0x02 }, + { CCI_REG8(0x00ab), 0xa0 }, + { CCI_REG8(0x00ac), 0x02 }, + + /* GAIN */ + { CCI_REG8(0x0204), 0x04 }, + { CCI_REG8(0x0205), 0x00 }, + { CCI_REG8(0x0050), 0x38 }, + { CCI_REG8(0x0051), 0x20 }, + + /* out window */ + { CCI_REG8(0x009a), 0x66 }, + { CCI_REG8(0x0351), 0x00 }, + { CCI_REG8(0x0352), 0x06 }, + { CCI_REG8(0x0353), 0x00 }, + { CCI_REG8(0x0354), 0x08 }, + { CCI_REG8(0x034c), 0x07 }, + { CCI_REG8(0x034d), 0x80 }, + { CCI_REG8(0x034e), 0x04 }, + { CCI_REG8(0x034f), 0x38 }, + + /* MIPI */ + { CCI_REG8(0x0114), 0x03 }, + { CCI_REG8(0x0180), 0x65 }, + { CCI_REG8(0x0181), 0xf0 }, + { CCI_REG8(0x0185), 0x01 }, + { CCI_REG8(0x0115), 0x30 }, + { CCI_REG8(0x011b), 0x12 }, + { CCI_REG8(0x011c), 0x12 }, + { CCI_REG8(0x0121), 0x02 }, + { CCI_REG8(0x0122), 0x03 }, + { CCI_REG8(0x0123), 0x0c }, + { CCI_REG8(0x0124), 0x00 }, + { CCI_REG8(0x0125), 0x09 }, + { CCI_REG8(0x0126), 0x06 }, + { CCI_REG8(0x0129), 0x04 }, + { CCI_REG8(0x012a), 0x03 }, + { CCI_REG8(0x012b), 0x06 }, + + { CCI_REG8(0x0a73), 0x60 }, + { CCI_REG8(0x0a70), 0x11 }, + { CCI_REG8(0x0313), 0x80 }, + { CCI_REG8(0x0aff), 0x00 }, + { CCI_REG8(0x0a70), 0x00 }, + { CCI_REG8(0x00a4), 0x80 }, + { CCI_REG8(0x0316), 0x01 }, + { CCI_REG8(0x0a67), 0x00 }, + { CCI_REG8(0x0084), 0x10 }, + { CCI_REG8(0x0102), 0x09 }, +}; + +static const struct cci_reg_sequence mode_table_common[] = { + { GC08A3_STREAMING_REG, 0x00 }, + /* system */ + { CCI_REG8(0x031c), 0x60 }, + { CCI_REG8(0x0337), 0x04 }, + { CCI_REG8(0x0335), 0x51 }, + { CCI_REG8(0x0336), 0x70 }, + { CCI_REG8(0x0383), 0xbb }, + { CCI_REG8(0x031a), 0x00 }, + { CCI_REG8(0x0321), 0x10 }, + { CCI_REG8(0x0327), 0x03 }, + { CCI_REG8(0x0325), 0x40 }, + { CCI_REG8(0x0326), 0x23 }, + { CCI_REG8(0x0314), 0x11 }, + { CCI_REG8(0x0315), 0xd6 }, + { CCI_REG8(0x0316), 0x01 }, + { CCI_REG8(0x0334), 0x40 }, + { CCI_REG8(0x0324), 0x42 }, + { CCI_REG8(0x031c), 0x00 }, + { CCI_REG8(0x031c), 0x9f }, + { CCI_REG8(0x039a), 0x13 }, + { CCI_REG8(0x0084), 0x30 }, + { CCI_REG8(0x02b3), 0x08 }, + { CCI_REG8(0x0057), 0x0c }, + { CCI_REG8(0x05c3), 0x50 }, + { CCI_REG8(0x0311), 0x90 }, + { CCI_REG8(0x05a0), 0x02 }, + { CCI_REG8(0x0074), 0x0a }, + { CCI_REG8(0x0059), 0x11 }, + { CCI_REG8(0x0070), 0x05 }, + { CCI_REG8(0x0101), 0x00 }, + + /* analog */ + { CCI_REG8(0x0344), 0x00 }, + { CCI_REG8(0x0345), 0x06 }, + { CCI_REG8(0x0346), 0x00 }, + { CCI_REG8(0x0347), 0x04 }, + { CCI_REG8(0x0348), 0x0c }, + { CCI_REG8(0x0349), 0xd0 }, + { CCI_REG8(0x034a), 0x09 }, + { CCI_REG8(0x034b), 0x9c }, + { CCI_REG8(0x0202), 0x09 }, + { CCI_REG8(0x0203), 0x04 }, + + { CCI_REG8(0x0219), 0x05 }, + { CCI_REG8(0x0226), 0x00 }, + { CCI_REG8(0x0227), 0x28 }, + { CCI_REG8(0x0e0a), 0x00 }, + { CCI_REG8(0x0e0b), 0x00 }, + { CCI_REG8(0x0e24), 0x04 }, + { CCI_REG8(0x0e25), 0x04 }, + { CCI_REG8(0x0e26), 0x00 }, + { CCI_REG8(0x0e27), 0x10 }, + { CCI_REG8(0x0e01), 0x74 }, + { CCI_REG8(0x0e03), 0x47 }, + { CCI_REG8(0x0e04), 0x33 }, + { CCI_REG8(0x0e05), 0x44 }, + { CCI_REG8(0x0e06), 0x44 }, + { CCI_REG8(0x0e0c), 0x1e }, + { CCI_REG8(0x0e17), 0x3a }, + { CCI_REG8(0x0e18), 0x3c }, + { CCI_REG8(0x0e19), 0x40 }, + { CCI_REG8(0x0e1a), 0x42 }, + { CCI_REG8(0x0e28), 0x21 }, + { CCI_REG8(0x0e2b), 0x68 }, + { CCI_REG8(0x0e2c), 0x0d }, + { CCI_REG8(0x0e2d), 0x08 }, + { CCI_REG8(0x0e34), 0xf4 }, + { CCI_REG8(0x0e35), 0x44 }, + { CCI_REG8(0x0e36), 0x07 }, + { CCI_REG8(0x0e38), 0x49 }, + { CCI_REG8(0x0210), 0x13 }, + { CCI_REG8(0x0218), 0x00 }, + { CCI_REG8(0x0241), 0x88 }, + { CCI_REG8(0x0e32), 0x00 }, + { CCI_REG8(0x0e33), 0x18 }, + { CCI_REG8(0x0e42), 0x03 }, + { CCI_REG8(0x0e43), 0x80 }, + { CCI_REG8(0x0e44), 0x04 }, + { CCI_REG8(0x0e45), 0x00 }, + { CCI_REG8(0x0e4f), 0x04 }, + { CCI_REG8(0x057a), 0x20 }, + { CCI_REG8(0x0381), 0x7c }, + { CCI_REG8(0x0382), 0x9b }, + { CCI_REG8(0x0384), 0xfb }, + { CCI_REG8(0x0389), 0x38 }, + { CCI_REG8(0x038a), 0x03 }, + { CCI_REG8(0x0390), 0x6a }, + { CCI_REG8(0x0391), 0x0b }, + { CCI_REG8(0x0392), 0x60 }, + { CCI_REG8(0x0393), 0xc1 }, + { CCI_REG8(0x0396), 0xff }, + { CCI_REG8(0x0398), 0x62 }, + + /* cisctl reset */ + { CCI_REG8(0x031c), 0x80 }, + { CCI_REG8(0x03fe), 0x10 }, + { CCI_REG8(0x03fe), 0x00 }, + { CCI_REG8(0x031c), 0x9f }, + { CCI_REG8(0x03fe), 0x00 }, + { CCI_REG8(0x03fe), 0x00 }, + { CCI_REG8(0x03fe), 0x00 }, + { CCI_REG8(0x03fe), 0x00 }, + { CCI_REG8(0x031c), 0x80 }, + { CCI_REG8(0x03fe), 0x10 }, + { CCI_REG8(0x03fe), 0x00 }, + { CCI_REG8(0x031c), 0x9f }, + { CCI_REG8(0x0360), 0x01 }, + { CCI_REG8(0x0360), 0x00 }, + { CCI_REG8(0x0316), 0x09 }, + { CCI_REG8(0x0a67), 0x80 }, + { CCI_REG8(0x0313), 0x00 }, + { CCI_REG8(0x0a53), 0x0e }, + { CCI_REG8(0x0a65), 0x17 }, + { CCI_REG8(0x0a68), 0xa1 }, + { CCI_REG8(0x0a58), 0x00 }, + { CCI_REG8(0x0ace), 0x0c }, + { CCI_REG8(0x00a4), 0x00 }, + { CCI_REG8(0x00a5), 0x01 }, + { CCI_REG8(0x00a7), 0x09 }, + { CCI_REG8(0x00a8), 0x9c }, + { CCI_REG8(0x00a9), 0x0c }, + { CCI_REG8(0x00aa), 0xd0 }, + { CCI_REG8(0x0a8a), 0x00 }, + { CCI_REG8(0x0a8b), 0xe0 }, + { CCI_REG8(0x0a8c), 0x13 }, + { CCI_REG8(0x0a8d), 0xe8 }, + { CCI_REG8(0x0a90), 0x0a }, + { CCI_REG8(0x0a91), 0x10 }, + { CCI_REG8(0x0a92), 0xf8 }, + { CCI_REG8(0x0a71), 0xf2 }, + { CCI_REG8(0x0a72), 0x12 }, + { CCI_REG8(0x0a73), 0x64 }, + { CCI_REG8(0x0a75), 0x41 }, + { CCI_REG8(0x0a70), 0x07 }, + { CCI_REG8(0x0313), 0x80 }, + + /* ISP */ + { CCI_REG8(0x00a0), 0x01 }, + { CCI_REG8(0x0080), 0xd2 }, + { CCI_REG8(0x0081), 0x3f }, + { CCI_REG8(0x0087), 0x51 }, + { CCI_REG8(0x0089), 0x03 }, + { CCI_REG8(0x009b), 0x40 }, + { CCI_REG8(0x05a0), 0x82 }, + { CCI_REG8(0x05ac), 0x00 }, + { CCI_REG8(0x05ad), 0x01 }, + { CCI_REG8(0x05ae), 0x00 }, + { CCI_REG8(0x0800), 0x0a }, + { CCI_REG8(0x0801), 0x14 }, + { CCI_REG8(0x0802), 0x28 }, + { CCI_REG8(0x0803), 0x34 }, + { CCI_REG8(0x0804), 0x0e }, + { CCI_REG8(0x0805), 0x33 }, + { CCI_REG8(0x0806), 0x03 }, + { CCI_REG8(0x0807), 0x8a }, + { CCI_REG8(0x0808), 0x50 }, + { CCI_REG8(0x0809), 0x00 }, + { CCI_REG8(0x080a), 0x34 }, + { CCI_REG8(0x080b), 0x03 }, + { CCI_REG8(0x080c), 0x26 }, + { CCI_REG8(0x080d), 0x03 }, + { CCI_REG8(0x080e), 0x18 }, + { CCI_REG8(0x080f), 0x03 }, + { CCI_REG8(0x0810), 0x10 }, + { CCI_REG8(0x0811), 0x03 }, + { CCI_REG8(0x0812), 0x00 }, + { CCI_REG8(0x0813), 0x00 }, + { CCI_REG8(0x0814), 0x01 }, + { CCI_REG8(0x0815), 0x00 }, + { CCI_REG8(0x0816), 0x01 }, + { CCI_REG8(0x0817), 0x00 }, + { CCI_REG8(0x0818), 0x00 }, + { CCI_REG8(0x0819), 0x0a }, + { CCI_REG8(0x081a), 0x01 }, + { CCI_REG8(0x081b), 0x6c }, + { CCI_REG8(0x081c), 0x00 }, + { CCI_REG8(0x081d), 0x0b }, + { CCI_REG8(0x081e), 0x02 }, + { CCI_REG8(0x081f), 0x00 }, + { CCI_REG8(0x0820), 0x00 }, + { CCI_REG8(0x0821), 0x0c }, + { CCI_REG8(0x0822), 0x02 }, + { CCI_REG8(0x0823), 0xd9 }, + { CCI_REG8(0x0824), 0x00 }, + { CCI_REG8(0x0825), 0x0d }, + { CCI_REG8(0x0826), 0x03 }, + { CCI_REG8(0x0827), 0xf0 }, + { CCI_REG8(0x0828), 0x00 }, + { CCI_REG8(0x0829), 0x0e }, + { CCI_REG8(0x082a), 0x05 }, + { CCI_REG8(0x082b), 0x94 }, + { CCI_REG8(0x082c), 0x09 }, + { CCI_REG8(0x082d), 0x6e }, + { CCI_REG8(0x082e), 0x07 }, + { CCI_REG8(0x082f), 0xe6 }, + { CCI_REG8(0x0830), 0x10 }, + { CCI_REG8(0x0831), 0x0e }, + { CCI_REG8(0x0832), 0x0b }, + { CCI_REG8(0x0833), 0x2c }, + { CCI_REG8(0x0834), 0x14 }, + { CCI_REG8(0x0835), 0xae }, + { CCI_REG8(0x0836), 0x0f }, + { CCI_REG8(0x0837), 0xc4 }, + { CCI_REG8(0x0838), 0x18 }, + { CCI_REG8(0x0839), 0x0e }, + { CCI_REG8(0x05ac), 0x01 }, + { CCI_REG8(0x059a), 0x00 }, + { CCI_REG8(0x059b), 0x00 }, + { CCI_REG8(0x059c), 0x01 }, + { CCI_REG8(0x0598), 0x00 }, + { CCI_REG8(0x0597), 0x14 }, + { CCI_REG8(0x05ab), 0x09 }, + { CCI_REG8(0x05a4), 0x02 }, + { CCI_REG8(0x05a3), 0x05 }, + { CCI_REG8(0x05a0), 0xc2 }, + { CCI_REG8(0x0207), 0xc4 }, + + /* GAIN */ + { CCI_REG8(0x0208), 0x01 }, + { CCI_REG8(0x0209), 0x72 }, + { CCI_REG8(0x0204), 0x04 }, + { CCI_REG8(0x0205), 0x00 }, + + { CCI_REG8(0x0040), 0x22 }, + { CCI_REG8(0x0041), 0x20 }, + { CCI_REG8(0x0043), 0x10 }, + { CCI_REG8(0x0044), 0x00 }, + { CCI_REG8(0x0046), 0x08 }, + { CCI_REG8(0x0047), 0xf0 }, + { CCI_REG8(0x0048), 0x0f }, + { CCI_REG8(0x004b), 0x0f }, + { CCI_REG8(0x004c), 0x00 }, + { CCI_REG8(0x0050), 0x5c }, + { CCI_REG8(0x0051), 0x44 }, + { CCI_REG8(0x005b), 0x03 }, + { CCI_REG8(0x00c0), 0x00 }, + { CCI_REG8(0x00c1), 0x80 }, + { CCI_REG8(0x00c2), 0x31 }, + { CCI_REG8(0x00c3), 0x00 }, + { CCI_REG8(0x0460), 0x04 }, + { CCI_REG8(0x0462), 0x08 }, + { CCI_REG8(0x0464), 0x0e }, + { CCI_REG8(0x0466), 0x0a }, + { CCI_REG8(0x0468), 0x12 }, + { CCI_REG8(0x046a), 0x12 }, + { CCI_REG8(0x046c), 0x10 }, + { CCI_REG8(0x046e), 0x0c }, + { CCI_REG8(0x0461), 0x03 }, + { CCI_REG8(0x0463), 0x03 }, + { CCI_REG8(0x0465), 0x03 }, + { CCI_REG8(0x0467), 0x03 }, + { CCI_REG8(0x0469), 0x04 }, + { CCI_REG8(0x046b), 0x04 }, + { CCI_REG8(0x046d), 0x04 }, + { CCI_REG8(0x046f), 0x04 }, + { CCI_REG8(0x0470), 0x04 }, + { CCI_REG8(0x0472), 0x10 }, + { CCI_REG8(0x0474), 0x26 }, + { CCI_REG8(0x0476), 0x38 }, + { CCI_REG8(0x0478), 0x20 }, + { CCI_REG8(0x047a), 0x30 }, + { CCI_REG8(0x047c), 0x38 }, + { CCI_REG8(0x047e), 0x60 }, + { CCI_REG8(0x0471), 0x05 }, + { CCI_REG8(0x0473), 0x05 }, + { CCI_REG8(0x0475), 0x05 }, + { CCI_REG8(0x0477), 0x05 }, + { CCI_REG8(0x0479), 0x04 }, + { CCI_REG8(0x047b), 0x04 }, + { CCI_REG8(0x047d), 0x04 }, + { CCI_REG8(0x047f), 0x04 }, +}; + +struct gc08a3_mode { + u32 width; + u32 height; + const struct gc08a3_reg_list reg_list; + + u32 hts; /* Horizontal timining size */ + u32 vts_def; /* Default vertical timining size */ + u32 vts_min; /* Min vertical timining size */ +}; + +/* Declare modes in order, from biggest to smallest height. */ +static const struct gc08a3_mode gc08a3_modes[] = { + { + /* 3264*2448@30fps */ + .width = GC08A3_NATIVE_WIDTH, + .height = GC08A3_NATIVE_HEIGHT, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_3264x2448), + .regs = mode_3264x2448, + }, + .hts = 3640, + .vts_def = 2548, + .vts_min = 2548, + }, + { + /* 1920*1080@60fps */ + .width = 1920, + .height = 1080, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1920x1080), + .regs = mode_1920x1080, + }, + .hts = 3640, + .vts_def = 1276, + .vts_min = 1276, + }, +}; + +static inline struct gc08a3 *to_gc08a3(struct v4l2_subdev *sd) +{ + return container_of(sd, struct gc08a3, sd); +} + +static int gc08a3_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct gc08a3 *gc08a3 = to_gc08a3(sd); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(gc08a3_supply_name), + gc08a3->supplies); + if (ret < 0) { + dev_err(gc08a3->dev, "failed to enable regulators: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(gc08a3->xclk); + if (ret < 0) { + regulator_bulk_disable(ARRAY_SIZE(gc08a3_supply_name), + gc08a3->supplies); + dev_err(gc08a3->dev, "clk prepare enable failed\n"); + return ret; + } + + fsleep(GC08A3_SLEEP_US); + + gpiod_set_value_cansleep(gc08a3->reset_gpio, 0); + fsleep(GC08A3_SLEEP_US); + + return 0; +} + +static int gc08a3_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct gc08a3 *gc08a3 = to_gc08a3(sd); + + clk_disable_unprepare(gc08a3->xclk); + gpiod_set_value_cansleep(gc08a3->reset_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(gc08a3_supply_name), + gc08a3->supplies); + + return 0; +} + +static int gc08a3_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = GC08A3_MBUS_CODE; + + return 0; +} + +static int gc08a3_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->code != GC08A3_MBUS_CODE) + return -EINVAL; + + if (fse->index >= ARRAY_SIZE(gc08a3_modes)) + return -EINVAL; + + fse->min_width = gc08a3_modes[fse->index].width; + fse->max_width = gc08a3_modes[fse->index].width; + fse->min_height = gc08a3_modes[fse->index].height; + fse->max_height = gc08a3_modes[fse->index].height; + + return 0; +} + +static int gc08a3_update_cur_mode_controls(struct gc08a3 *gc08a3, + const struct gc08a3_mode *mode) +{ + s64 exposure_max, h_blank; + int ret; + + ret = __v4l2_ctrl_modify_range(gc08a3->vblank, + mode->vts_min - mode->height, + GC08A3_VTS_MAX - mode->height, 1, + mode->vts_def - mode->height); + if (ret) { + dev_err(gc08a3->dev, "VB ctrl range update failed\n"); + return ret; + } + + h_blank = mode->hts - mode->width; + ret = __v4l2_ctrl_modify_range(gc08a3->hblank, h_blank, h_blank, 1, + h_blank); + if (ret) { + dev_err(gc08a3->dev, "HB ctrl range update failed\n"); + return ret; + } + + exposure_max = mode->vts_def - GC08A3_EXP_MARGIN; + ret = __v4l2_ctrl_modify_range(gc08a3->exposure, GC08A3_EXP_MIN, + exposure_max, GC08A3_EXP_STEP, + exposure_max); + if (ret) { + dev_err(gc08a3->dev, "exposure ctrl range update failed\n"); + return ret; + } + + return 0; +} + +static void gc08a3_update_pad_format(struct gc08a3 *gc08a3, + const struct gc08a3_mode *mode, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->width = mode->width; + fmt->height = mode->height; + fmt->code = GC08A3_MBUS_CODE; + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_RAW; + fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); + fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + fmt->xfer_func = V4L2_XFER_FUNC_NONE; +} + +static int gc08a3_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt) +{ + struct gc08a3 *gc08a3 = to_gc08a3(sd); + struct v4l2_mbus_framefmt *mbus_fmt; + struct v4l2_rect *crop; + const struct gc08a3_mode *mode; + + mode = v4l2_find_nearest_size(gc08a3_modes, ARRAY_SIZE(gc08a3_modes), + width, height, fmt->format.width, + fmt->format.height); + + /* update crop info to subdev state */ + crop = v4l2_subdev_state_get_crop(state, 0); + crop->width = mode->width; + crop->height = mode->height; + + /* update fmt info to subdev state */ + gc08a3_update_pad_format(gc08a3, mode, &fmt->format); + mbus_fmt = v4l2_subdev_state_get_format(state, 0); + *mbus_fmt = fmt->format; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + gc08a3->cur_mode = mode; + gc08a3_update_cur_mode_controls(gc08a3, mode); + + return 0; +} + +static int gc08a3_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(state, 0); + break; + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = GC08A3_NATIVE_WIDTH; + sel->r.height = GC08A3_NATIVE_HEIGHT; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int gc08a3_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .pad = 0, + .format = { + .code = GC08A3_MBUS_CODE, + .width = gc08a3_modes[0].width, + .height = gc08a3_modes[0].height, + }, + }; + + gc08a3_set_format(sd, state, &fmt); + + return 0; +} + +static int gc08a3_set_ctrl_hflip(struct gc08a3 *gc08a3, u32 ctrl_val) +{ + int ret; + u64 val; + + ret = cci_read(gc08a3->regmap, GC08A3_FLIP_REG, &val, NULL); + if (ret) { + dev_err(gc08a3->dev, "read hflip register failed: %d\n", ret); + return ret; + } + + return cci_update_bits(gc08a3->regmap, GC08A3_FLIP_REG, + GC08A3_FLIP_H_MASK, + ctrl_val ? GC08A3_FLIP_H_MASK : 0, NULL); +} + +static int gc08a3_set_ctrl_vflip(struct gc08a3 *gc08a3, u32 ctrl_val) +{ + int ret; + u64 val; + + ret = cci_read(gc08a3->regmap, GC08A3_FLIP_REG, &val, NULL); + if (ret) { + dev_err(gc08a3->dev, "read vflip register failed: %d\n", ret); + return ret; + } + + return cci_update_bits(gc08a3->regmap, GC08A3_FLIP_REG, + GC08A3_FLIP_V_MASK, + ctrl_val ? GC08A3_FLIP_V_MASK : 0, NULL); +} + +static int gc08a3_test_pattern(struct gc08a3 *gc08a3, u32 pattern_menu) +{ + u32 pattern; + int ret; + + if (pattern_menu) { + switch (pattern_menu) { + case 1: + pattern = 0x00; + break; + case 2: + pattern = 0x10; + break; + case 3: + case 4: + case 5: + case 6: + case 7: + pattern = pattern_menu + 1; + break; + default: + pattern = 0x00; + break; + } + + ret = cci_write(gc08a3->regmap, GC08A3_REG_TEST_PATTERN_IDX, + pattern, NULL); + if (ret) + return ret; + + return cci_write(gc08a3->regmap, GC08A3_REG_TEST_PATTERN_EN, + GC08A3_TEST_PATTERN_EN, NULL); + } else { + return cci_write(gc08a3->regmap, GC08A3_REG_TEST_PATTERN_EN, + 0x00, NULL); + } +} + +static int gc08a3_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gc08a3 *gc08a3 = + container_of(ctrl->handler, struct gc08a3, ctrls); + int ret = 0; + s64 exposure_max; + struct v4l2_subdev_state *state; + const struct v4l2_mbus_framefmt *format; + + state = v4l2_subdev_get_locked_active_state(&gc08a3->sd); + format = v4l2_subdev_state_get_format(state, 0); + + if (ctrl->id == V4L2_CID_VBLANK) { + /* Update max exposure while meeting expected vblanking */ + exposure_max = format->height + ctrl->val - GC08A3_EXP_MARGIN; + __v4l2_ctrl_modify_range(gc08a3->exposure, + gc08a3->exposure->minimum, + exposure_max, gc08a3->exposure->step, + exposure_max); + } + + /* + * Applying V4L2 control value only happens + * when power is on for streaming. + */ + if (!pm_runtime_get_if_active(gc08a3->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + ret = cci_write(gc08a3->regmap, GC08A3_EXP_REG, + ctrl->val, NULL); + break; + + case V4L2_CID_ANALOGUE_GAIN: + ret = cci_write(gc08a3->regmap, GC08A3_AGAIN_REG, + ctrl->val, NULL); + break; + + case V4L2_CID_VBLANK: + ret = cci_write(gc08a3->regmap, GC08A3_FRAME_LENGTH_REG, + gc08a3->cur_mode->height + ctrl->val, NULL); + break; + + case V4L2_CID_HFLIP: + ret = gc08a3_set_ctrl_hflip(gc08a3, ctrl->val); + break; + + case V4L2_CID_VFLIP: + ret = gc08a3_set_ctrl_vflip(gc08a3, ctrl->val); + break; + + case V4L2_CID_TEST_PATTERN: + ret = gc08a3_test_pattern(gc08a3, ctrl->val); + break; + + default: + break; + } + + pm_runtime_put(gc08a3->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops gc08a3_ctrl_ops = { + .s_ctrl = gc08a3_set_ctrl, +}; + +static int gc08a3_start_streaming(struct gc08a3 *gc08a3) +{ + const struct gc08a3_mode *mode; + const struct gc08a3_reg_list *reg_list; + int ret; + + ret = pm_runtime_resume_and_get(gc08a3->dev); + if (ret < 0) + return ret; + + ret = cci_multi_reg_write(gc08a3->regmap, + mode_table_common, + ARRAY_SIZE(mode_table_common), NULL); + if (ret) + goto err_rpm_put; + + mode = gc08a3->cur_mode; + reg_list = &mode->reg_list; + ret = cci_multi_reg_write(gc08a3->regmap, + reg_list->regs, reg_list->num_of_regs, NULL); + if (ret < 0) + goto err_rpm_put; + + ret = __v4l2_ctrl_handler_setup(&gc08a3->ctrls); + if (ret < 0) { + dev_err(gc08a3->dev, "could not sync v4l2 controls\n"); + goto err_rpm_put; + } + + ret = cci_write(gc08a3->regmap, GC08A3_STREAMING_REG, 1, NULL); + if (ret < 0) { + dev_err(gc08a3->dev, "write STRAEMING_REG failed: %d\n", ret); + goto err_rpm_put; + } + + return 0; + +err_rpm_put: + pm_runtime_put(gc08a3->dev); + return ret; +} + +static int gc08a3_stop_streaming(struct gc08a3 *gc08a3) +{ + int ret; + + ret = cci_write(gc08a3->regmap, GC08A3_STREAMING_REG, 0, NULL); + if (ret < 0) + dev_err(gc08a3->dev, "could not sent stop streaming %d\n", ret); + + pm_runtime_put(gc08a3->dev); + return ret; +} + +static int gc08a3_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct gc08a3 *gc08a3 = to_gc08a3(subdev); + struct v4l2_subdev_state *state; + int ret; + + state = v4l2_subdev_lock_and_get_active_state(subdev); + + if (enable) + ret = gc08a3_start_streaming(gc08a3); + else + ret = gc08a3_stop_streaming(gc08a3); + + v4l2_subdev_unlock_state(state); + + return ret; +} + +static const struct v4l2_subdev_video_ops gc08a3_video_ops = { + .s_stream = gc08a3_s_stream, +}; + +static const struct v4l2_subdev_pad_ops gc08a3_subdev_pad_ops = { + .enum_mbus_code = gc08a3_enum_mbus_code, + .enum_frame_size = gc08a3_enum_frame_size, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = gc08a3_set_format, + .get_selection = gc08a3_get_selection, +}; + +static const struct v4l2_subdev_core_ops gc08a3_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_ops gc08a3_subdev_ops = { + .core = &gc08a3_core_ops, + .video = &gc08a3_video_ops, + .pad = &gc08a3_subdev_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops gc08a3_internal_ops = { + .init_state = gc08a3_init_state, +}; + +static int gc08a3_get_regulators(struct device *dev, struct gc08a3 *gc08a3) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(gc08a3_supply_name); i++) + gc08a3->supplies[i].supply = gc08a3_supply_name[i]; + + return devm_regulator_bulk_get(dev, ARRAY_SIZE(gc08a3_supply_name), + gc08a3->supplies); +} + +static int gc08a3_parse_fwnode(struct gc08a3 *gc08a3) +{ + struct fwnode_handle *endpoint; + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + int ret; + struct device *dev = gc08a3->dev; + + 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_alloc_parse(endpoint, &bus_cfg); + if (ret) { + dev_err(dev, "parsing endpoint node failed\n"); + goto done; + } + + ret = v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies, + bus_cfg.nr_of_link_frequencies, + gc08a3_link_freq_menu_items, + ARRAY_SIZE(gc08a3_link_freq_menu_items), + &gc08a3->link_freq_bitmap); + if (ret) + goto done; + +done: + v4l2_fwnode_endpoint_free(&bus_cfg); + fwnode_handle_put(endpoint); + return ret; +} + +static u64 gc08a3_to_pixel_rate(u32 f_index) +{ + u64 pixel_rate = + gc08a3_link_freq_menu_items[f_index] * 2 * GC08A3_DATA_LANES; + + return div_u64(pixel_rate, GC08A3_RGB_DEPTH); +} + +static int gc08a3_init_controls(struct gc08a3 *gc08a3) +{ + struct i2c_client *client = v4l2_get_subdevdata(&gc08a3->sd); + const struct gc08a3_mode *mode = &gc08a3_modes[0]; + const struct v4l2_ctrl_ops *ops = &gc08a3_ctrl_ops; + struct v4l2_fwnode_device_properties props; + struct v4l2_ctrl_handler *ctrl_hdlr; + s64 exposure_max, h_blank; + int ret; + + ctrl_hdlr = &gc08a3->ctrls; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 9); + if (ret) + return ret; + + gc08a3->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &gc08a3_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + gc08a3->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &gc08a3_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_cluster(2, &gc08a3->hflip); + + gc08a3->link_freq = + v4l2_ctrl_new_int_menu(ctrl_hdlr, + &gc08a3_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(gc08a3_link_freq_menu_items) - 1, + 0, + gc08a3_link_freq_menu_items); + if (gc08a3->link_freq) + gc08a3->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + gc08a3->pixel_rate = + v4l2_ctrl_new_std(ctrl_hdlr, + &gc08a3_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, + gc08a3_to_pixel_rate(0), + 1, + gc08a3_to_pixel_rate(0)); + + gc08a3->vblank = + v4l2_ctrl_new_std(ctrl_hdlr, + &gc08a3_ctrl_ops, V4L2_CID_VBLANK, + mode->vts_min - mode->height, + GC08A3_VTS_MAX - mode->height, 1, + mode->vts_def - mode->height); + + h_blank = mode->hts - mode->width; + gc08a3->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &gc08a3_ctrl_ops, + V4L2_CID_HBLANK, h_blank, h_blank, 1, + h_blank); + if (gc08a3->hblank) + gc08a3->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(ctrl_hdlr, &gc08a3_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, GC08A3_AGAIN_MIN, + GC08A3_AGAIN_MAX, GC08A3_AGAIN_STEP, + GC08A3_AGAIN_MIN); + + exposure_max = mode->vts_def - GC08A3_EXP_MARGIN; + gc08a3->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &gc08a3_ctrl_ops, + V4L2_CID_EXPOSURE, GC08A3_EXP_MIN, + exposure_max, GC08A3_EXP_STEP, + exposure_max); + + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &gc08a3_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(gc08a3_test_pattern_menu) - 1, + 0, 0, gc08a3_test_pattern_menu); + + /* register properties to fwnode (e.g. rotation, orientation) */ + ret = v4l2_fwnode_device_parse(&client->dev, &props); + if (ret) + goto error_ctrls; + + ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, ops, &props); + if (ret) + goto error_ctrls; + + if (ctrl_hdlr->error) { + ret = ctrl_hdlr->error; + goto error_ctrls; + } + + gc08a3->sd.ctrl_handler = ctrl_hdlr; + + return 0; + +error_ctrls: + v4l2_ctrl_handler_free(ctrl_hdlr); + + return ret; +} + +static int gc08a3_identify_module(struct gc08a3 *gc08a3) +{ + u64 val; + int ret; + + ret = cci_read(gc08a3->regmap, GC08A3_REG_CHIP_ID, &val, NULL); + if (ret) { + dev_err(gc08a3->dev, "failed to read chip id"); + return ret; + } + + if (val != GC08A3_CHIP_ID) { + dev_err(gc08a3->dev, "chip id mismatch: 0x%x!=0x%llx", + GC08A3_CHIP_ID, val); + return -ENXIO; + } + + return 0; +} + +static int gc08a3_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct gc08a3 *gc08a3; + int ret; + + gc08a3 = devm_kzalloc(dev, sizeof(*gc08a3), GFP_KERNEL); + if (!gc08a3) + return -ENOMEM; + + gc08a3->dev = dev; + + ret = gc08a3_parse_fwnode(gc08a3); + if (ret) + return ret; + + gc08a3->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(gc08a3->regmap)) + return dev_err_probe(dev, PTR_ERR(gc08a3->regmap), + "failed to init CCI\n"); + + gc08a3->xclk = devm_clk_get(dev, NULL); + if (IS_ERR(gc08a3->xclk)) + return dev_err_probe(dev, PTR_ERR(gc08a3->xclk), + "failed to get xclk\n"); + + ret = clk_set_rate(gc08a3->xclk, GC08A3_DEFAULT_CLK_FREQ); + if (ret) + return dev_err_probe(dev, ret, + "failed to set xclk frequency\n"); + + ret = gc08a3_get_regulators(dev, gc08a3); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to get regulators\n"); + + gc08a3->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(gc08a3->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(gc08a3->reset_gpio), + "failed to get gpio\n"); + + v4l2_i2c_subdev_init(&gc08a3->sd, client, &gc08a3_subdev_ops); + gc08a3->sd.internal_ops = &gc08a3_internal_ops; + gc08a3->cur_mode = &gc08a3_modes[0]; + + ret = gc08a3_power_on(gc08a3->dev); + if (ret) + return dev_err_probe(dev, ret, + "failed to sensor power on\n"); + + ret = gc08a3_identify_module(gc08a3); + if (ret) { + dev_err(&client->dev, "failed to find sensor: %d\n", ret); + goto err_power_off; + } + + ret = gc08a3_init_controls(gc08a3); + if (ret) { + dev_err(&client->dev, "failed to init controls: %d", ret); + goto err_power_off; + } + + gc08a3->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; + gc08a3->pad.flags = MEDIA_PAD_FL_SOURCE; + gc08a3->sd.dev = &client->dev; + gc08a3->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + ret = media_entity_pads_init(&gc08a3->sd.entity, 1, &gc08a3->pad); + if (ret < 0) { + dev_err(dev, "could not register media entity\n"); + goto err_v4l2_ctrl_handler_free; + } + + gc08a3->sd.state_lock = gc08a3->ctrls.lock; + ret = v4l2_subdev_init_finalize(&gc08a3->sd); + if (ret < 0) { + dev_err(dev, "v4l2 subdev init error: %d\n", ret); + goto err_media_entity_cleanup; + } + + pm_runtime_set_active(gc08a3->dev); + pm_runtime_enable(gc08a3->dev); + pm_runtime_set_autosuspend_delay(gc08a3->dev, 1000); + pm_runtime_use_autosuspend(gc08a3->dev); + pm_runtime_idle(gc08a3->dev); + + ret = v4l2_async_register_subdev_sensor(&gc08a3->sd); + if (ret < 0) { + dev_err(dev, "could not register v4l2 device\n"); + goto err_rpm; + } + + return 0; + +err_rpm: + pm_runtime_disable(gc08a3->dev); + v4l2_subdev_cleanup(&gc08a3->sd); + +err_media_entity_cleanup: + media_entity_cleanup(&gc08a3->sd.entity); + +err_v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(gc08a3->sd.ctrl_handler); + +err_power_off: + gc08a3_power_off(gc08a3->dev); + + return ret; +} + +static void gc08a3_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc08a3 *gc08a3 = to_gc08a3(sd); + + v4l2_async_unregister_subdev(&gc08a3->sd); + v4l2_subdev_cleanup(sd); + media_entity_cleanup(&gc08a3->sd.entity); + v4l2_ctrl_handler_free(&gc08a3->ctrls); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + gc08a3_power_off(gc08a3->dev); + pm_runtime_set_suspended(&client->dev); +} + +static const struct of_device_id gc08a3_of_match[] = { + { .compatible = "galaxycore,gc08a3" }, + {} +}; +MODULE_DEVICE_TABLE(of, gc08a3_of_match); + +static DEFINE_RUNTIME_DEV_PM_OPS(gc08a3_pm_ops, + gc08a3_power_off, + gc08a3_power_on, + NULL); + +static struct i2c_driver gc08a3_i2c_driver = { + .driver = { + .of_match_table = gc08a3_of_match, + .pm = pm_ptr(&gc08a3_pm_ops), + .name = "gc08a3", + }, + .probe = gc08a3_probe, + .remove = gc08a3_remove, +}; +module_i2c_driver(gc08a3_i2c_driver); + +MODULE_DESCRIPTION("GalaxyCore gc08a3 Camera driver"); +MODULE_AUTHOR("Zhi Mao <zhi.mao@mediatek.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/gc2145.c b/drivers/media/i2c/gc2145.c index bef7b0e056a8..667bb756d056 100644 --- a/drivers/media/i2c/gc2145.c +++ b/drivers/media/i2c/gc2145.c @@ -68,8 +68,7 @@ #define GC2145_DPHY_CLK_DELAY BIT(4) #define GC2145_DPHY_LANE0_DELAY BIT(5) #define GC2145_DPHY_LANE1_DELAY BIT(6) -#define GC2145_REG_FIFO_FULL_LVL_LOW CCI_REG8(0x04) -#define GC2145_REG_FIFO_FULL_LVL_HIGH CCI_REG8(0x05) +#define GC2145_REG_FIFO_FULL_LVL CCI_REG16_LE(0x04) #define GC2145_REG_FIFO_MODE CCI_REG8(0x06) #define GC2145_FIFO_MODE_READ_GATE BIT(3) #define GC2145_FIFO_MODE_MIPI_CLK_MODULE BIT(7) @@ -79,8 +78,7 @@ #define GC2145_CSI2_MODE_MIPI_EN BIT(4) #define GC2145_CSI2_MODE_EN BIT(7) #define GC2145_REG_MIPI_DT CCI_REG8(0x11) -#define GC2145_REG_LWC_LOW CCI_REG8(0x12) -#define GC2145_REG_LWC_HIGH CCI_REG8(0x13) +#define GC2145_REG_LWC CCI_REG16_LE(0x12) #define GC2145_REG_DPHY_MODE CCI_REG8(0x15) #define GC2145_DPHY_MODE_TRIGGER_PROG BIT(4) #define GC2145_REG_FIFO_GATE_MODE CCI_REG8(0x17) @@ -542,45 +540,82 @@ static const struct gc2145_mode supported_modes[] = { /** * struct gc2145_format - GC2145 pixel format description * @code: media bus (MBUS) associated code + * @colorspace: V4L2 colorspace * @datatype: MIPI CSI2 data type * @output_fmt: GC2145 output format * @switch_bit: GC2145 first/second switch + * @row_col_switch: GC2145 switch row and/or column */ struct gc2145_format { unsigned int code; + unsigned int colorspace; unsigned char datatype; unsigned char output_fmt; bool switch_bit; + unsigned char row_col_switch; }; /* All supported formats */ static const struct gc2145_format supported_formats[] = { { .code = MEDIA_BUS_FMT_UYVY8_1X16, + .colorspace = V4L2_COLORSPACE_SRGB, .datatype = MIPI_CSI2_DT_YUV422_8B, .output_fmt = 0x00, }, { .code = MEDIA_BUS_FMT_VYUY8_1X16, + .colorspace = V4L2_COLORSPACE_SRGB, .datatype = MIPI_CSI2_DT_YUV422_8B, .output_fmt = 0x01, }, { .code = MEDIA_BUS_FMT_YUYV8_1X16, + .colorspace = V4L2_COLORSPACE_SRGB, .datatype = MIPI_CSI2_DT_YUV422_8B, .output_fmt = 0x02, }, { .code = MEDIA_BUS_FMT_YVYU8_1X16, + .colorspace = V4L2_COLORSPACE_SRGB, .datatype = MIPI_CSI2_DT_YUV422_8B, .output_fmt = 0x03, }, { .code = MEDIA_BUS_FMT_RGB565_1X16, + .colorspace = V4L2_COLORSPACE_SRGB, .datatype = MIPI_CSI2_DT_RGB565, .output_fmt = 0x06, .switch_bit = true, }, + { + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .colorspace = V4L2_COLORSPACE_RAW, + .datatype = MIPI_CSI2_DT_RAW8, + .output_fmt = 0x17, + .row_col_switch = GC2145_SYNC_MODE_COL_SWITCH, + }, + { + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .colorspace = V4L2_COLORSPACE_RAW, + .datatype = MIPI_CSI2_DT_RAW8, + .output_fmt = 0x17, + .row_col_switch = GC2145_SYNC_MODE_COL_SWITCH | GC2145_SYNC_MODE_ROW_SWITCH, + }, + { + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .colorspace = V4L2_COLORSPACE_RAW, + .datatype = MIPI_CSI2_DT_RAW8, + .output_fmt = 0x17, + .row_col_switch = 0, + }, + { + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .colorspace = V4L2_COLORSPACE_RAW, + .datatype = MIPI_CSI2_DT_RAW8, + .output_fmt = 0x17, + .row_col_switch = GC2145_SYNC_MODE_ROW_SWITCH, + }, }; struct gc2145_ctrls { @@ -641,7 +676,8 @@ gc2145_get_format_code(struct gc2145 *gc2145, u32 code) static void gc2145_update_pad_format(struct gc2145 *gc2145, const struct gc2145_mode *mode, - struct v4l2_mbus_framefmt *fmt, u32 code) + struct v4l2_mbus_framefmt *fmt, u32 code, + u32 colorspace) { fmt->code = code; fmt->width = mode->width; @@ -663,7 +699,8 @@ static int gc2145_init_state(struct v4l2_subdev *sd, /* Initialize pad format */ format = v4l2_subdev_state_get_format(state, 0); gc2145_update_pad_format(gc2145, &supported_modes[0], format, - MEDIA_BUS_FMT_RGB565_1X16); + MEDIA_BUS_FMT_RGB565_1X16, + V4L2_COLORSPACE_SRGB); /* Initialize crop rectangle. */ crop = v4l2_subdev_state_get_crop(state, 0); @@ -754,7 +791,13 @@ static int gc2145_set_pad_format(struct v4l2_subdev *sd, width, height, fmt->format.width, fmt->format.height); - gc2145_update_pad_format(gc2145, mode, &fmt->format, gc2145_fmt->code); + /* In RAW mode, VGA is not possible so use 720p instead */ + if (gc2145_fmt->colorspace == V4L2_COLORSPACE_RAW && + mode == &supported_modes[GC2145_MODE_640X480]) + mode = &supported_modes[GC2145_MODE_1280X720]; + + gc2145_update_pad_format(gc2145, mode, &fmt->format, gc2145_fmt->code, + gc2145_fmt->colorspace); framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { gc2145->mode = mode; @@ -811,9 +854,12 @@ static int gc2145_config_mipi_mode(struct gc2145 *gc2145, * For RAW8, LWC = image width * For RAW10, LWC = image width * 1.25 */ - lwc = gc2145->mode->width * 2; - cci_write(gc2145->regmap, GC2145_REG_LWC_HIGH, lwc >> 8, &ret); - cci_write(gc2145->regmap, GC2145_REG_LWC_LOW, lwc & 0xff, &ret); + if (gc2145_format->colorspace != V4L2_COLORSPACE_RAW) + lwc = gc2145->mode->width * 2; + else + lwc = gc2145->mode->width; + + cci_write(gc2145->regmap, GC2145_REG_LWC, lwc, &ret); /* * Adjust the MIPI FIFO Full Level @@ -821,21 +867,25 @@ static int gc2145_config_mipi_mode(struct gc2145 *gc2145, * 1280x720 / 1600x1200 (aka no scaler) non RAW: 0x0001 * 1600x1200 RAW: 0x0190 */ - if (gc2145->mode->width == 1280 || gc2145->mode->width == 1600) - fifo_full_lvl = 0x0001; - else + if (gc2145_format->colorspace != V4L2_COLORSPACE_RAW) { + if (gc2145->mode->width == 1280 || gc2145->mode->width == 1600) + fifo_full_lvl = 0x0001; + else + fifo_full_lvl = 0x0190; + } else { fifo_full_lvl = 0x0190; + } - cci_write(gc2145->regmap, GC2145_REG_FIFO_FULL_LVL_HIGH, - fifo_full_lvl >> 8, &ret); - cci_write(gc2145->regmap, GC2145_REG_FIFO_FULL_LVL_LOW, - fifo_full_lvl & 0xff, &ret); + cci_write(gc2145->regmap, GC2145_REG_FIFO_FULL_LVL, + fifo_full_lvl, &ret); /* * Set the FIFO gate mode / MIPI wdiv set: * 0xf1 in case of RAW mode and 0xf0 otherwise */ - cci_write(gc2145->regmap, GC2145_REG_FIFO_GATE_MODE, 0xf0, &ret); + cci_write(gc2145->regmap, GC2145_REG_FIFO_GATE_MODE, + gc2145_format->colorspace == V4L2_COLORSPACE_RAW ? + 0xf1 : 0xf0, &ret); /* Set the MIPI data type */ cci_write(gc2145->regmap, GC2145_REG_MIPI_DT, @@ -883,6 +933,10 @@ static int gc2145_start_streaming(struct gc2145 *gc2145, GC2145_BYPASS_MODE_SWITCH, gc2145_format->switch_bit ? GC2145_BYPASS_MODE_SWITCH : 0, &ret); + cci_update_bits(gc2145->regmap, GC2145_REG_SYNC_MODE, + GC2145_SYNC_MODE_COL_SWITCH | + GC2145_SYNC_MODE_ROW_SWITCH, + gc2145_format->row_col_switch, &ret); if (ret) { dev_err(&client->dev, "%s failed to write regs\n", __func__); goto err_rpm_put; diff --git a/drivers/media/i2c/hi846.c b/drivers/media/i2c/hi846.c index 9c565ec033d4..52d9ca68a86c 100644 --- a/drivers/media/i2c/hi846.c +++ b/drivers/media/i2c/hi846.c @@ -1851,7 +1851,7 @@ static int hi846_get_selection(struct v4l2_subdev *sd, mutex_lock(&hi846->mutex); switch (sel->which) { case V4L2_SUBDEV_FORMAT_TRY: - v4l2_subdev_state_get_crop(sd_state, sel->pad); + sel->r = *v4l2_subdev_state_get_crop(sd_state, sel->pad); break; case V4L2_SUBDEV_FORMAT_ACTIVE: sel->r = hi846->cur_mode->crop; diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 51ebf5453fce..e78a80b2bb2e 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -162,8 +162,8 @@ static const struct cci_reg_sequence imx219_common_regs[] = { { IMX219_REG_MODE_SELECT, 0x00 }, /* Mode Select */ /* To Access Addresses 3000-5fff, send the following commands */ - { CCI_REG8(0x30eb), 0x0c }, { CCI_REG8(0x30eb), 0x05 }, + { CCI_REG8(0x30eb), 0x0c }, { CCI_REG8(0x300a), 0xff }, { CCI_REG8(0x300b), 0xff }, { CCI_REG8(0x30eb), 0x05 }, diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index a577afb530b7..1a99eaaff21a 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -7,97 +7,150 @@ #include <linux/i2c.h> #include <linux/module.h> #include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> +#include <media/v4l2-cci.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-fwnode.h> #include <asm/unaligned.h> -#define IMX258_REG_VALUE_08BIT 1 -#define IMX258_REG_VALUE_16BIT 2 - -#define IMX258_REG_MODE_SELECT 0x0100 +#define IMX258_REG_MODE_SELECT CCI_REG8(0x0100) #define IMX258_MODE_STANDBY 0x00 #define IMX258_MODE_STREAMING 0x01 +#define IMX258_REG_RESET CCI_REG8(0x0103) + /* Chip ID */ -#define IMX258_REG_CHIP_ID 0x0016 +#define IMX258_REG_CHIP_ID CCI_REG16(0x0016) #define IMX258_CHIP_ID 0x0258 /* V_TIMING internal */ #define IMX258_VTS_30FPS 0x0c50 #define IMX258_VTS_30FPS_2K 0x0638 #define IMX258_VTS_30FPS_VGA 0x034c -#define IMX258_VTS_MAX 0xffff - -/*Frame Length Line*/ -#define IMX258_FLL_MIN 0x08a6 -#define IMX258_FLL_MAX 0xffff -#define IMX258_FLL_STEP 1 -#define IMX258_FLL_DEFAULT 0x0c98 +#define IMX258_VTS_MAX 65525 /* HBLANK control - read only */ #define IMX258_PPL_DEFAULT 5352 /* Exposure control */ -#define IMX258_REG_EXPOSURE 0x0202 +#define IMX258_REG_EXPOSURE CCI_REG16(0x0202) +#define IMX258_EXPOSURE_OFFSET 10 #define IMX258_EXPOSURE_MIN 4 #define IMX258_EXPOSURE_STEP 1 #define IMX258_EXPOSURE_DEFAULT 0x640 -#define IMX258_EXPOSURE_MAX 65535 +#define IMX258_EXPOSURE_MAX (IMX258_VTS_MAX - IMX258_EXPOSURE_OFFSET) /* Analog gain control */ -#define IMX258_REG_ANALOG_GAIN 0x0204 +#define IMX258_REG_ANALOG_GAIN CCI_REG16(0x0204) #define IMX258_ANA_GAIN_MIN 0 #define IMX258_ANA_GAIN_MAX 480 #define IMX258_ANA_GAIN_STEP 1 #define IMX258_ANA_GAIN_DEFAULT 0x0 /* Digital gain control */ -#define IMX258_REG_GR_DIGITAL_GAIN 0x020e -#define IMX258_REG_R_DIGITAL_GAIN 0x0210 -#define IMX258_REG_B_DIGITAL_GAIN 0x0212 -#define IMX258_REG_GB_DIGITAL_GAIN 0x0214 +#define IMX258_REG_GR_DIGITAL_GAIN CCI_REG16(0x020e) +#define IMX258_REG_R_DIGITAL_GAIN CCI_REG16(0x0210) +#define IMX258_REG_B_DIGITAL_GAIN CCI_REG16(0x0212) +#define IMX258_REG_GB_DIGITAL_GAIN CCI_REG16(0x0214) #define IMX258_DGTL_GAIN_MIN 0 #define IMX258_DGTL_GAIN_MAX 4096 /* Max = 0xFFF */ #define IMX258_DGTL_GAIN_DEFAULT 1024 #define IMX258_DGTL_GAIN_STEP 1 /* HDR control */ -#define IMX258_REG_HDR 0x0220 +#define IMX258_REG_HDR CCI_REG8(0x0220) #define IMX258_HDR_ON BIT(0) -#define IMX258_REG_HDR_RATIO 0x0222 +#define IMX258_REG_HDR_RATIO CCI_REG8(0x0222) #define IMX258_HDR_RATIO_MIN 0 #define IMX258_HDR_RATIO_MAX 5 #define IMX258_HDR_RATIO_STEP 1 #define IMX258_HDR_RATIO_DEFAULT 0x0 /* Test Pattern Control */ -#define IMX258_REG_TEST_PATTERN 0x0600 +#define IMX258_REG_TEST_PATTERN CCI_REG16(0x0600) + +#define IMX258_CLK_BLANK_STOP CCI_REG8(0x4040) /* Orientation */ -#define REG_MIRROR_FLIP_CONTROL 0x0101 -#define REG_CONFIG_MIRROR_FLIP 0x03 -#define REG_CONFIG_FLIP_TEST_PATTERN 0x02 +#define REG_MIRROR_FLIP_CONTROL CCI_REG8(0x0101) +#define REG_CONFIG_MIRROR_HFLIP 0x01 +#define REG_CONFIG_MIRROR_VFLIP 0x02 + +/* IMX258 native and active pixel array size. */ +#define IMX258_NATIVE_WIDTH 4224U +#define IMX258_NATIVE_HEIGHT 3192U +#define IMX258_PIXEL_ARRAY_LEFT 8U +#define IMX258_PIXEL_ARRAY_TOP 16U +#define IMX258_PIXEL_ARRAY_WIDTH 4208U +#define IMX258_PIXEL_ARRAY_HEIGHT 3120U + +/* regs */ +#define IMX258_REG_PLL_MULT_DRIV CCI_REG8(0x0310) +#define IMX258_REG_IVTPXCK_DIV CCI_REG8(0x0301) +#define IMX258_REG_IVTSYCK_DIV CCI_REG8(0x0303) +#define IMX258_REG_PREPLLCK_VT_DIV CCI_REG8(0x0305) +#define IMX258_REG_IOPPXCK_DIV CCI_REG8(0x0309) +#define IMX258_REG_IOPSYCK_DIV CCI_REG8(0x030b) +#define IMX258_REG_PREPLLCK_OP_DIV CCI_REG8(0x030d) +#define IMX258_REG_PHASE_PIX_OUTEN CCI_REG8(0x3030) +#define IMX258_REG_PDPIX_DATA_RATE CCI_REG8(0x3032) +#define IMX258_REG_SCALE_MODE CCI_REG8(0x0401) +#define IMX258_REG_SCALE_MODE_EXT CCI_REG8(0x3038) +#define IMX258_REG_AF_WINDOW_MODE CCI_REG8(0x7bcd) +#define IMX258_REG_FRM_LENGTH_CTL CCI_REG8(0x0350) +#define IMX258_REG_CSI_LANE_MODE CCI_REG8(0x0114) +#define IMX258_REG_X_EVN_INC CCI_REG8(0x0381) +#define IMX258_REG_X_ODD_INC CCI_REG8(0x0383) +#define IMX258_REG_Y_EVN_INC CCI_REG8(0x0385) +#define IMX258_REG_Y_ODD_INC CCI_REG8(0x0387) +#define IMX258_REG_BINNING_MODE CCI_REG8(0x0900) +#define IMX258_REG_BINNING_TYPE_V CCI_REG8(0x0901) +#define IMX258_REG_FORCE_FD_SUM CCI_REG8(0x300d) +#define IMX258_REG_DIG_CROP_X_OFFSET CCI_REG16(0x0408) +#define IMX258_REG_DIG_CROP_Y_OFFSET CCI_REG16(0x040a) +#define IMX258_REG_DIG_CROP_IMAGE_WIDTH CCI_REG16(0x040c) +#define IMX258_REG_DIG_CROP_IMAGE_HEIGHT CCI_REG16(0x040e) +#define IMX258_REG_SCALE_M CCI_REG16(0x0404) +#define IMX258_REG_X_OUT_SIZE CCI_REG16(0x034c) +#define IMX258_REG_Y_OUT_SIZE CCI_REG16(0x034e) +#define IMX258_REG_X_ADD_STA CCI_REG16(0x0344) +#define IMX258_REG_Y_ADD_STA CCI_REG16(0x0346) +#define IMX258_REG_X_ADD_END CCI_REG16(0x0348) +#define IMX258_REG_Y_ADD_END CCI_REG16(0x034a) +#define IMX258_REG_EXCK_FREQ CCI_REG16(0x0136) +#define IMX258_REG_CSI_DT_FMT CCI_REG16(0x0112) +#define IMX258_REG_LINE_LENGTH_PCK CCI_REG16(0x0342) +#define IMX258_REG_SCALE_M_EXT CCI_REG16(0x303a) +#define IMX258_REG_FRM_LENGTH_LINES CCI_REG16(0x0340) +#define IMX258_REG_FINE_INTEG_TIME CCI_REG8(0x0200) +#define IMX258_REG_PLL_IVT_MPY CCI_REG16(0x0306) +#define IMX258_REG_PLL_IOP_MPY CCI_REG16(0x030e) +#define IMX258_REG_REQ_LINK_BIT_RATE_MBPS_H CCI_REG16(0x0820) +#define IMX258_REG_REQ_LINK_BIT_RATE_MBPS_L CCI_REG16(0x0822) -/* Input clock frequency in Hz */ -#define IMX258_INPUT_CLOCK_FREQ 19200000 +struct imx258_reg_list { + u32 num_of_regs; + const struct cci_reg_sequence *regs; +}; -struct imx258_reg { - u16 address; - u8 val; +struct imx258_link_cfg { + unsigned int lf_to_pix_rate_factor; + struct imx258_reg_list reg_list; }; -struct imx258_reg_list { - u32 num_of_regs; - const struct imx258_reg *regs; +enum { + IMX258_2_LANE_MODE, + IMX258_4_LANE_MODE, + IMX258_LANE_CONFIGS, }; /* Link frequency config */ struct imx258_link_freq_config { u32 pixels_per_line; - /* PLL registers for this link frequency */ - struct imx258_reg_list reg_list; + /* Configuration for this link frequency / num lanes selection */ + struct imx258_link_cfg link_cfg[IMX258_LANE_CONFIGS]; }; /* Mode : resolution and related config&values */ @@ -115,400 +168,307 @@ struct imx258_mode { u32 link_freq_index; /* Default register values */ struct imx258_reg_list reg_list; + + /* Analog crop rectangle */ + struct v4l2_rect crop; +}; + +/* + * 4208x3120 @ 30 fps needs 1267Mbps/lane, 4 lanes. + * To avoid further computation of clock settings, adopt the same per + * lane data rate when using 2 lanes, thus allowing a maximum of 15fps. + */ +static const struct cci_reg_sequence mipi_1267mbps_19_2mhz_2l[] = { + { IMX258_REG_EXCK_FREQ, 0x1333 }, + { IMX258_REG_IVTPXCK_DIV, 10 }, + { IMX258_REG_IVTSYCK_DIV, 2 }, + { IMX258_REG_PREPLLCK_VT_DIV, 3 }, + { IMX258_REG_PLL_IVT_MPY, 198 }, + { IMX258_REG_IOPPXCK_DIV, 10 }, + { IMX258_REG_IOPSYCK_DIV, 1 }, + { IMX258_REG_PREPLLCK_OP_DIV, 2 }, + { IMX258_REG_PLL_IOP_MPY, 216 }, + { IMX258_REG_PLL_MULT_DRIV, 0 }, + + { IMX258_REG_CSI_LANE_MODE, 1 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_H, 1267 * 2 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_L, 0 }, +}; + +static const struct cci_reg_sequence mipi_1267mbps_19_2mhz_4l[] = { + { IMX258_REG_EXCK_FREQ, 0x1333 }, + { IMX258_REG_IVTPXCK_DIV, 5 }, + { IMX258_REG_IVTSYCK_DIV, 2 }, + { IMX258_REG_PREPLLCK_VT_DIV, 3 }, + { IMX258_REG_PLL_IVT_MPY, 198 }, + { IMX258_REG_IOPPXCK_DIV, 10 }, + { IMX258_REG_IOPSYCK_DIV, 1 }, + { IMX258_REG_PREPLLCK_OP_DIV, 2 }, + { IMX258_REG_PLL_IOP_MPY, 216 }, + { IMX258_REG_PLL_MULT_DRIV, 0 }, + + { IMX258_REG_CSI_LANE_MODE, 3 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_H, 1267 * 4 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_L, 0 }, +}; + +static const struct cci_reg_sequence mipi_1272mbps_24mhz_2l[] = { + { IMX258_REG_EXCK_FREQ, 0x1800 }, + { IMX258_REG_IVTPXCK_DIV, 10 }, + { IMX258_REG_IVTSYCK_DIV, 2 }, + { IMX258_REG_PREPLLCK_VT_DIV, 4 }, + { IMX258_REG_PLL_IVT_MPY, 212 }, + { IMX258_REG_IOPPXCK_DIV, 10 }, + { IMX258_REG_IOPSYCK_DIV, 1 }, + { IMX258_REG_PREPLLCK_OP_DIV, 2 }, + { IMX258_REG_PLL_IOP_MPY, 216 }, + { IMX258_REG_PLL_MULT_DRIV, 0 }, + + { IMX258_REG_CSI_LANE_MODE, 1 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_H, 1272 * 2 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_L, 0 }, +}; + +static const struct cci_reg_sequence mipi_1272mbps_24mhz_4l[] = { + { IMX258_REG_EXCK_FREQ, 0x1800 }, + { IMX258_REG_IVTPXCK_DIV, 5 }, + { IMX258_REG_IVTSYCK_DIV, 2 }, + { IMX258_REG_PREPLLCK_VT_DIV, 4 }, + { IMX258_REG_PLL_IVT_MPY, 212 }, + { IMX258_REG_IOPPXCK_DIV, 10 }, + { IMX258_REG_IOPSYCK_DIV, 1 }, + { IMX258_REG_PREPLLCK_OP_DIV, 2 }, + { IMX258_REG_PLL_IOP_MPY, 216 }, + { IMX258_REG_PLL_MULT_DRIV, 0 }, + + { IMX258_REG_CSI_LANE_MODE, 3 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_H, 1272 * 4 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_L, 0 }, +}; + +static const struct cci_reg_sequence mipi_640mbps_19_2mhz_2l[] = { + { IMX258_REG_EXCK_FREQ, 0x1333 }, + { IMX258_REG_IVTPXCK_DIV, 5 }, + { IMX258_REG_IVTSYCK_DIV, 2 }, + { IMX258_REG_PREPLLCK_VT_DIV, 3 }, + { IMX258_REG_PLL_IVT_MPY, 100 }, + { IMX258_REG_IOPPXCK_DIV, 10 }, + { IMX258_REG_IOPSYCK_DIV, 1 }, + { IMX258_REG_PREPLLCK_OP_DIV, 2 }, + { IMX258_REG_PLL_IOP_MPY, 216 }, + { IMX258_REG_PLL_MULT_DRIV, 0 }, + + { IMX258_REG_CSI_LANE_MODE, 1 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_H, 640 * 2 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_L, 0 }, +}; + +static const struct cci_reg_sequence mipi_640mbps_19_2mhz_4l[] = { + { IMX258_REG_EXCK_FREQ, 0x1333 }, + { IMX258_REG_IVTPXCK_DIV, 5 }, + { IMX258_REG_IVTSYCK_DIV, 2 }, + { IMX258_REG_PREPLLCK_VT_DIV, 3 }, + { IMX258_REG_PLL_IVT_MPY, 100 }, + { IMX258_REG_IOPPXCK_DIV, 10 }, + { IMX258_REG_IOPSYCK_DIV, 1 }, + { IMX258_REG_PREPLLCK_OP_DIV, 2 }, + { IMX258_REG_PLL_IOP_MPY, 216 }, + { IMX258_REG_PLL_MULT_DRIV, 0 }, + + { IMX258_REG_CSI_LANE_MODE, 3 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_H, 640 * 4 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_L, 0 }, }; -/* 4208x3118 needs 1267Mbps/lane, 4 lanes */ -static const struct imx258_reg mipi_data_rate_1267mbps[] = { - { 0x0301, 0x05 }, - { 0x0303, 0x02 }, - { 0x0305, 0x03 }, - { 0x0306, 0x00 }, - { 0x0307, 0xC6 }, - { 0x0309, 0x0A }, - { 0x030B, 0x01 }, - { 0x030D, 0x02 }, - { 0x030E, 0x00 }, - { 0x030F, 0xD8 }, - { 0x0310, 0x00 }, - { 0x0820, 0x13 }, - { 0x0821, 0x4C }, - { 0x0822, 0xCC }, - { 0x0823, 0xCC }, +static const struct cci_reg_sequence mipi_642mbps_24mhz_2l[] = { + { IMX258_REG_EXCK_FREQ, 0x1800 }, + { IMX258_REG_IVTPXCK_DIV, 5 }, + { IMX258_REG_IVTSYCK_DIV, 2 }, + { IMX258_REG_PREPLLCK_VT_DIV, 4 }, + { IMX258_REG_PLL_IVT_MPY, 107 }, + { IMX258_REG_IOPPXCK_DIV, 10 }, + { IMX258_REG_IOPSYCK_DIV, 1 }, + { IMX258_REG_PREPLLCK_OP_DIV, 2 }, + { IMX258_REG_PLL_IOP_MPY, 216 }, + { IMX258_REG_PLL_MULT_DRIV, 0 }, + + { IMX258_REG_CSI_LANE_MODE, 1 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_H, 642 * 2 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_L, 0 }, }; -static const struct imx258_reg mipi_data_rate_640mbps[] = { - { 0x0301, 0x05 }, - { 0x0303, 0x02 }, - { 0x0305, 0x03 }, - { 0x0306, 0x00 }, - { 0x0307, 0x64 }, - { 0x0309, 0x0A }, - { 0x030B, 0x01 }, - { 0x030D, 0x02 }, - { 0x030E, 0x00 }, - { 0x030F, 0xD8 }, - { 0x0310, 0x00 }, - { 0x0820, 0x0A }, - { 0x0821, 0x00 }, - { 0x0822, 0x00 }, - { 0x0823, 0x00 }, +static const struct cci_reg_sequence mipi_642mbps_24mhz_4l[] = { + { IMX258_REG_EXCK_FREQ, 0x1800 }, + { IMX258_REG_IVTPXCK_DIV, 5 }, + { IMX258_REG_IVTSYCK_DIV, 2 }, + { IMX258_REG_PREPLLCK_VT_DIV, 4 }, + { IMX258_REG_PLL_IVT_MPY, 107 }, + { IMX258_REG_IOPPXCK_DIV, 10 }, + { IMX258_REG_IOPSYCK_DIV, 1 }, + { IMX258_REG_PREPLLCK_OP_DIV, 2 }, + { IMX258_REG_PLL_IOP_MPY, 216 }, + { IMX258_REG_PLL_MULT_DRIV, 0 }, + + { IMX258_REG_CSI_LANE_MODE, 3 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_H, 642 * 4 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_L, 0 }, }; -static const struct imx258_reg mode_4208x3118_regs[] = { - { 0x0136, 0x13 }, - { 0x0137, 0x33 }, - { 0x3051, 0x00 }, - { 0x3052, 0x00 }, - { 0x4E21, 0x14 }, - { 0x6B11, 0xCF }, - { 0x7FF0, 0x08 }, - { 0x7FF1, 0x0F }, - { 0x7FF2, 0x08 }, - { 0x7FF3, 0x1B }, - { 0x7FF4, 0x23 }, - { 0x7FF5, 0x60 }, - { 0x7FF6, 0x00 }, - { 0x7FF7, 0x01 }, - { 0x7FF8, 0x00 }, - { 0x7FF9, 0x78 }, - { 0x7FFA, 0x00 }, - { 0x7FFB, 0x00 }, - { 0x7FFC, 0x00 }, - { 0x7FFD, 0x00 }, - { 0x7FFE, 0x00 }, - { 0x7FFF, 0x03 }, - { 0x7F76, 0x03 }, - { 0x7F77, 0xFE }, - { 0x7FA8, 0x03 }, - { 0x7FA9, 0xFE }, - { 0x7B24, 0x81 }, - { 0x7B25, 0x00 }, - { 0x6564, 0x07 }, - { 0x6B0D, 0x41 }, - { 0x653D, 0x04 }, - { 0x6B05, 0x8C }, - { 0x6B06, 0xF9 }, - { 0x6B08, 0x65 }, - { 0x6B09, 0xFC }, - { 0x6B0A, 0xCF }, - { 0x6B0B, 0xD2 }, - { 0x6700, 0x0E }, - { 0x6707, 0x0E }, - { 0x9104, 0x00 }, - { 0x4648, 0x7F }, - { 0x7420, 0x00 }, - { 0x7421, 0x1C }, - { 0x7422, 0x00 }, - { 0x7423, 0xD7 }, - { 0x5F04, 0x00 }, - { 0x5F05, 0xED }, - { 0x0112, 0x0A }, - { 0x0113, 0x0A }, - { 0x0114, 0x03 }, - { 0x0342, 0x14 }, - { 0x0343, 0xE8 }, - { 0x0340, 0x0C }, - { 0x0341, 0x50 }, - { 0x0344, 0x00 }, - { 0x0345, 0x00 }, - { 0x0346, 0x00 }, - { 0x0347, 0x00 }, - { 0x0348, 0x10 }, - { 0x0349, 0x6F }, - { 0x034A, 0x0C }, - { 0x034B, 0x2E }, - { 0x0381, 0x01 }, - { 0x0383, 0x01 }, - { 0x0385, 0x01 }, - { 0x0387, 0x01 }, - { 0x0900, 0x00 }, - { 0x0901, 0x11 }, - { 0x0401, 0x00 }, - { 0x0404, 0x00 }, - { 0x0405, 0x10 }, - { 0x0408, 0x00 }, - { 0x0409, 0x00 }, - { 0x040A, 0x00 }, - { 0x040B, 0x00 }, - { 0x040C, 0x10 }, - { 0x040D, 0x70 }, - { 0x040E, 0x0C }, - { 0x040F, 0x30 }, - { 0x3038, 0x00 }, - { 0x303A, 0x00 }, - { 0x303B, 0x10 }, - { 0x300D, 0x00 }, - { 0x034C, 0x10 }, - { 0x034D, 0x70 }, - { 0x034E, 0x0C }, - { 0x034F, 0x30 }, - { 0x0350, 0x01 }, - { 0x0202, 0x0C }, - { 0x0203, 0x46 }, - { 0x0204, 0x00 }, - { 0x0205, 0x00 }, - { 0x020E, 0x01 }, - { 0x020F, 0x00 }, - { 0x0210, 0x01 }, - { 0x0211, 0x00 }, - { 0x0212, 0x01 }, - { 0x0213, 0x00 }, - { 0x0214, 0x01 }, - { 0x0215, 0x00 }, - { 0x7BCD, 0x00 }, - { 0x94DC, 0x20 }, - { 0x94DD, 0x20 }, - { 0x94DE, 0x20 }, - { 0x95DC, 0x20 }, - { 0x95DD, 0x20 }, - { 0x95DE, 0x20 }, - { 0x7FB0, 0x00 }, - { 0x9010, 0x3E }, - { 0x9419, 0x50 }, - { 0x941B, 0x50 }, - { 0x9519, 0x50 }, - { 0x951B, 0x50 }, - { 0x3030, 0x00 }, - { 0x3032, 0x00 }, - { 0x0220, 0x00 }, +static const struct cci_reg_sequence mode_common_regs[] = { + { CCI_REG8(0x3051), 0x00 }, + { CCI_REG8(0x6B11), 0xCF }, + { CCI_REG8(0x7FF0), 0x08 }, + { CCI_REG8(0x7FF1), 0x0F }, + { CCI_REG8(0x7FF2), 0x08 }, + { CCI_REG8(0x7FF3), 0x1B }, + { CCI_REG8(0x7FF4), 0x23 }, + { CCI_REG8(0x7FF5), 0x60 }, + { CCI_REG8(0x7FF6), 0x00 }, + { CCI_REG8(0x7FF7), 0x01 }, + { CCI_REG8(0x7FF8), 0x00 }, + { CCI_REG8(0x7FF9), 0x78 }, + { CCI_REG8(0x7FFA), 0x00 }, + { CCI_REG8(0x7FFB), 0x00 }, + { CCI_REG8(0x7FFC), 0x00 }, + { CCI_REG8(0x7FFD), 0x00 }, + { CCI_REG8(0x7FFE), 0x00 }, + { CCI_REG8(0x7FFF), 0x03 }, + { CCI_REG8(0x7F76), 0x03 }, + { CCI_REG8(0x7F77), 0xFE }, + { CCI_REG8(0x7FA8), 0x03 }, + { CCI_REG8(0x7FA9), 0xFE }, + { CCI_REG8(0x7B24), 0x81 }, + { CCI_REG8(0x6564), 0x07 }, + { CCI_REG8(0x6B0D), 0x41 }, + { CCI_REG8(0x653D), 0x04 }, + { CCI_REG8(0x6B05), 0x8C }, + { CCI_REG8(0x6B06), 0xF9 }, + { CCI_REG8(0x6B08), 0x65 }, + { CCI_REG8(0x6B09), 0xFC }, + { CCI_REG8(0x6B0A), 0xCF }, + { CCI_REG8(0x6B0B), 0xD2 }, + { CCI_REG8(0x6700), 0x0E }, + { CCI_REG8(0x6707), 0x0E }, + { CCI_REG8(0x9104), 0x00 }, + { CCI_REG8(0x4648), 0x7F }, + { CCI_REG8(0x7420), 0x00 }, + { CCI_REG8(0x7421), 0x1C }, + { CCI_REG8(0x7422), 0x00 }, + { CCI_REG8(0x7423), 0xD7 }, + { CCI_REG8(0x5F04), 0x00 }, + { CCI_REG8(0x5F05), 0xED }, + {IMX258_REG_CSI_DT_FMT, 0x0a0a}, + {IMX258_REG_LINE_LENGTH_PCK, 5352}, + {IMX258_REG_X_ADD_STA, 0}, + {IMX258_REG_Y_ADD_STA, 0}, + {IMX258_REG_X_ADD_END, 4207}, + {IMX258_REG_Y_ADD_END, 3119}, + {IMX258_REG_X_EVN_INC, 1}, + {IMX258_REG_X_ODD_INC, 1}, + {IMX258_REG_Y_EVN_INC, 1}, + {IMX258_REG_Y_ODD_INC, 1}, + {IMX258_REG_DIG_CROP_X_OFFSET, 0}, + {IMX258_REG_DIG_CROP_Y_OFFSET, 0}, + {IMX258_REG_DIG_CROP_IMAGE_WIDTH, 4208}, + {IMX258_REG_SCALE_MODE_EXT, 0}, + {IMX258_REG_SCALE_M_EXT, 16}, + {IMX258_REG_FORCE_FD_SUM, 0}, + {IMX258_REG_FRM_LENGTH_CTL, 0}, + {IMX258_REG_ANALOG_GAIN, 0}, + {IMX258_REG_GR_DIGITAL_GAIN, 256}, + {IMX258_REG_R_DIGITAL_GAIN, 256}, + {IMX258_REG_B_DIGITAL_GAIN, 256}, + {IMX258_REG_GB_DIGITAL_GAIN, 256}, + {IMX258_REG_AF_WINDOW_MODE, 0}, + { CCI_REG8(0x94DC), 0x20 }, + { CCI_REG8(0x94DD), 0x20 }, + { CCI_REG8(0x94DE), 0x20 }, + { CCI_REG8(0x95DC), 0x20 }, + { CCI_REG8(0x95DD), 0x20 }, + { CCI_REG8(0x95DE), 0x20 }, + { CCI_REG8(0x7FB0), 0x00 }, + { CCI_REG8(0x9010), 0x3E }, + { CCI_REG8(0x9419), 0x50 }, + { CCI_REG8(0x941B), 0x50 }, + { CCI_REG8(0x9519), 0x50 }, + { CCI_REG8(0x951B), 0x50 }, + {IMX258_REG_PHASE_PIX_OUTEN, 0}, + {IMX258_REG_PDPIX_DATA_RATE, 0}, + {IMX258_REG_HDR, 0}, }; -static const struct imx258_reg mode_2104_1560_regs[] = { - { 0x0136, 0x13 }, - { 0x0137, 0x33 }, - { 0x3051, 0x00 }, - { 0x3052, 0x00 }, - { 0x4E21, 0x14 }, - { 0x6B11, 0xCF }, - { 0x7FF0, 0x08 }, - { 0x7FF1, 0x0F }, - { 0x7FF2, 0x08 }, - { 0x7FF3, 0x1B }, - { 0x7FF4, 0x23 }, - { 0x7FF5, 0x60 }, - { 0x7FF6, 0x00 }, - { 0x7FF7, 0x01 }, - { 0x7FF8, 0x00 }, - { 0x7FF9, 0x78 }, - { 0x7FFA, 0x00 }, - { 0x7FFB, 0x00 }, - { 0x7FFC, 0x00 }, - { 0x7FFD, 0x00 }, - { 0x7FFE, 0x00 }, - { 0x7FFF, 0x03 }, - { 0x7F76, 0x03 }, - { 0x7F77, 0xFE }, - { 0x7FA8, 0x03 }, - { 0x7FA9, 0xFE }, - { 0x7B24, 0x81 }, - { 0x7B25, 0x00 }, - { 0x6564, 0x07 }, - { 0x6B0D, 0x41 }, - { 0x653D, 0x04 }, - { 0x6B05, 0x8C }, - { 0x6B06, 0xF9 }, - { 0x6B08, 0x65 }, - { 0x6B09, 0xFC }, - { 0x6B0A, 0xCF }, - { 0x6B0B, 0xD2 }, - { 0x6700, 0x0E }, - { 0x6707, 0x0E }, - { 0x9104, 0x00 }, - { 0x4648, 0x7F }, - { 0x7420, 0x00 }, - { 0x7421, 0x1C }, - { 0x7422, 0x00 }, - { 0x7423, 0xD7 }, - { 0x5F04, 0x00 }, - { 0x5F05, 0xED }, - { 0x0112, 0x0A }, - { 0x0113, 0x0A }, - { 0x0114, 0x03 }, - { 0x0342, 0x14 }, - { 0x0343, 0xE8 }, - { 0x0340, 0x06 }, - { 0x0341, 0x38 }, - { 0x0344, 0x00 }, - { 0x0345, 0x00 }, - { 0x0346, 0x00 }, - { 0x0347, 0x00 }, - { 0x0348, 0x10 }, - { 0x0349, 0x6F }, - { 0x034A, 0x0C }, - { 0x034B, 0x2E }, - { 0x0381, 0x01 }, - { 0x0383, 0x01 }, - { 0x0385, 0x01 }, - { 0x0387, 0x01 }, - { 0x0900, 0x01 }, - { 0x0901, 0x12 }, - { 0x0401, 0x01 }, - { 0x0404, 0x00 }, - { 0x0405, 0x20 }, - { 0x0408, 0x00 }, - { 0x0409, 0x02 }, - { 0x040A, 0x00 }, - { 0x040B, 0x00 }, - { 0x040C, 0x10 }, - { 0x040D, 0x6A }, - { 0x040E, 0x06 }, - { 0x040F, 0x18 }, - { 0x3038, 0x00 }, - { 0x303A, 0x00 }, - { 0x303B, 0x10 }, - { 0x300D, 0x00 }, - { 0x034C, 0x08 }, - { 0x034D, 0x38 }, - { 0x034E, 0x06 }, - { 0x034F, 0x18 }, - { 0x0350, 0x01 }, - { 0x0202, 0x06 }, - { 0x0203, 0x2E }, - { 0x0204, 0x00 }, - { 0x0205, 0x00 }, - { 0x020E, 0x01 }, - { 0x020F, 0x00 }, - { 0x0210, 0x01 }, - { 0x0211, 0x00 }, - { 0x0212, 0x01 }, - { 0x0213, 0x00 }, - { 0x0214, 0x01 }, - { 0x0215, 0x00 }, - { 0x7BCD, 0x01 }, - { 0x94DC, 0x20 }, - { 0x94DD, 0x20 }, - { 0x94DE, 0x20 }, - { 0x95DC, 0x20 }, - { 0x95DD, 0x20 }, - { 0x95DE, 0x20 }, - { 0x7FB0, 0x00 }, - { 0x9010, 0x3E }, - { 0x9419, 0x50 }, - { 0x941B, 0x50 }, - { 0x9519, 0x50 }, - { 0x951B, 0x50 }, - { 0x3030, 0x00 }, - { 0x3032, 0x00 }, - { 0x0220, 0x00 }, +static const struct cci_reg_sequence mode_4208x3120_regs[] = { + {IMX258_REG_BINNING_MODE, 0}, + {IMX258_REG_BINNING_TYPE_V, 0x11}, + {IMX258_REG_SCALE_MODE, 0}, + {IMX258_REG_SCALE_M, 16}, + {IMX258_REG_DIG_CROP_IMAGE_HEIGHT, 3120}, + {IMX258_REG_X_OUT_SIZE, 4208}, + {IMX258_REG_Y_OUT_SIZE, 3120}, }; -static const struct imx258_reg mode_1048_780_regs[] = { - { 0x0136, 0x13 }, - { 0x0137, 0x33 }, - { 0x3051, 0x00 }, - { 0x3052, 0x00 }, - { 0x4E21, 0x14 }, - { 0x6B11, 0xCF }, - { 0x7FF0, 0x08 }, - { 0x7FF1, 0x0F }, - { 0x7FF2, 0x08 }, - { 0x7FF3, 0x1B }, - { 0x7FF4, 0x23 }, - { 0x7FF5, 0x60 }, - { 0x7FF6, 0x00 }, - { 0x7FF7, 0x01 }, - { 0x7FF8, 0x00 }, - { 0x7FF9, 0x78 }, - { 0x7FFA, 0x00 }, - { 0x7FFB, 0x00 }, - { 0x7FFC, 0x00 }, - { 0x7FFD, 0x00 }, - { 0x7FFE, 0x00 }, - { 0x7FFF, 0x03 }, - { 0x7F76, 0x03 }, - { 0x7F77, 0xFE }, - { 0x7FA8, 0x03 }, - { 0x7FA9, 0xFE }, - { 0x7B24, 0x81 }, - { 0x7B25, 0x00 }, - { 0x6564, 0x07 }, - { 0x6B0D, 0x41 }, - { 0x653D, 0x04 }, - { 0x6B05, 0x8C }, - { 0x6B06, 0xF9 }, - { 0x6B08, 0x65 }, - { 0x6B09, 0xFC }, - { 0x6B0A, 0xCF }, - { 0x6B0B, 0xD2 }, - { 0x6700, 0x0E }, - { 0x6707, 0x0E }, - { 0x9104, 0x00 }, - { 0x4648, 0x7F }, - { 0x7420, 0x00 }, - { 0x7421, 0x1C }, - { 0x7422, 0x00 }, - { 0x7423, 0xD7 }, - { 0x5F04, 0x00 }, - { 0x5F05, 0xED }, - { 0x0112, 0x0A }, - { 0x0113, 0x0A }, - { 0x0114, 0x03 }, - { 0x0342, 0x14 }, - { 0x0343, 0xE8 }, - { 0x0340, 0x03 }, - { 0x0341, 0x4C }, - { 0x0344, 0x00 }, - { 0x0345, 0x00 }, - { 0x0346, 0x00 }, - { 0x0347, 0x00 }, - { 0x0348, 0x10 }, - { 0x0349, 0x6F }, - { 0x034A, 0x0C }, - { 0x034B, 0x2E }, - { 0x0381, 0x01 }, - { 0x0383, 0x01 }, - { 0x0385, 0x01 }, - { 0x0387, 0x01 }, - { 0x0900, 0x01 }, - { 0x0901, 0x14 }, - { 0x0401, 0x01 }, - { 0x0404, 0x00 }, - { 0x0405, 0x40 }, - { 0x0408, 0x00 }, - { 0x0409, 0x06 }, - { 0x040A, 0x00 }, - { 0x040B, 0x00 }, - { 0x040C, 0x10 }, - { 0x040D, 0x64 }, - { 0x040E, 0x03 }, - { 0x040F, 0x0C }, - { 0x3038, 0x00 }, - { 0x303A, 0x00 }, - { 0x303B, 0x10 }, - { 0x300D, 0x00 }, - { 0x034C, 0x04 }, - { 0x034D, 0x18 }, - { 0x034E, 0x03 }, - { 0x034F, 0x0C }, - { 0x0350, 0x01 }, - { 0x0202, 0x03 }, - { 0x0203, 0x42 }, - { 0x0204, 0x00 }, - { 0x0205, 0x00 }, - { 0x020E, 0x01 }, - { 0x020F, 0x00 }, - { 0x0210, 0x01 }, - { 0x0211, 0x00 }, - { 0x0212, 0x01 }, - { 0x0213, 0x00 }, - { 0x0214, 0x01 }, - { 0x0215, 0x00 }, - { 0x7BCD, 0x00 }, - { 0x94DC, 0x20 }, - { 0x94DD, 0x20 }, - { 0x94DE, 0x20 }, - { 0x95DC, 0x20 }, - { 0x95DD, 0x20 }, - { 0x95DE, 0x20 }, - { 0x7FB0, 0x00 }, - { 0x9010, 0x3E }, - { 0x9419, 0x50 }, - { 0x941B, 0x50 }, - { 0x9519, 0x50 }, - { 0x951B, 0x50 }, - { 0x3030, 0x00 }, - { 0x3032, 0x00 }, - { 0x0220, 0x00 }, +static const struct cci_reg_sequence mode_2104_1560_regs[] = { + {IMX258_REG_BINNING_MODE, 1}, + {IMX258_REG_BINNING_TYPE_V, 0x12}, + {IMX258_REG_SCALE_MODE, 1}, + {IMX258_REG_SCALE_M, 32}, + {IMX258_REG_DIG_CROP_IMAGE_HEIGHT, 1560}, + {IMX258_REG_X_OUT_SIZE, 2104}, + {IMX258_REG_Y_OUT_SIZE, 1560}, +}; + +static const struct cci_reg_sequence mode_1048_780_regs[] = { + {IMX258_REG_BINNING_MODE, 1}, + {IMX258_REG_BINNING_TYPE_V, 0x14}, + {IMX258_REG_SCALE_MODE, 1}, + {IMX258_REG_SCALE_M, 64}, + {IMX258_REG_DIG_CROP_IMAGE_HEIGHT, 780}, + {IMX258_REG_X_OUT_SIZE, 1048}, + {IMX258_REG_Y_OUT_SIZE, 780}, +}; + +struct imx258_variant_cfg { + const struct cci_reg_sequence *regs; + unsigned int num_regs; +}; + +static const struct cci_reg_sequence imx258_cfg_regs[] = { + { CCI_REG8(0x3052), 0x00 }, + { CCI_REG8(0x4E21), 0x14 }, + { CCI_REG8(0x7B25), 0x00 }, +}; + +static const struct imx258_variant_cfg imx258_cfg = { + .regs = imx258_cfg_regs, + .num_regs = ARRAY_SIZE(imx258_cfg_regs), +}; + +static const struct cci_reg_sequence imx258_pdaf_cfg_regs[] = { + { CCI_REG8(0x3052), 0x01 }, + { CCI_REG8(0x4E21), 0x10 }, + { CCI_REG8(0x7B25), 0x01 }, +}; + +static const struct imx258_variant_cfg imx258_pdaf_cfg = { + .regs = imx258_pdaf_cfg_regs, + .num_regs = ARRAY_SIZE(imx258_pdaf_cfg_regs), +}; + +/* + * The supported formats. + * This table MUST contain 4 entries per format, to cover the various flip + * combinations in the order + * - no flip + * - h flip + * - v flip + * - h&v flips + */ +static const u32 codes[] = { + /* 10-bit modes. */ + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SBGGR10_1X10 }; static const char * const imx258_test_pattern_menu[] = { @@ -519,9 +479,15 @@ static const char * const imx258_test_pattern_menu[] = { "Pseudorandom Sequence (PN9)", }; -/* Configurations for supported link frequencies */ -#define IMX258_LINK_FREQ_634MHZ 633600000ULL -#define IMX258_LINK_FREQ_320MHZ 320000000ULL +/* regulator supplies */ +static const char * const imx258_supply_name[] = { + /* Supplies can be enabled in any order */ + "vana", /* Analog (2.8V) supply */ + "vdig", /* Digital Core (1.2V) supply */ + "vif", /* IF (1.8V) supply */ +}; + +#define IMX258_NUM_SUPPLIES ARRAY_SIZE(imx258_supply_name) enum { IMX258_LINK_FREQ_1267MBPS, @@ -529,37 +495,96 @@ enum { }; /* - * pixel_rate = link_freq * data-rate * nr_of_lanes / bits_per_sample - * data rate => double data rate; number of lanes => 4; bits per pixel => 10 + * Pixel rate does not necessarily relate to link frequency on this sensor as + * there is a FIFO between the pixel array pipeline and the MIPI serializer. + * The recommendation from Sony is that the pixel array is always run with a + * line length of 5352 pixels, which means that there is a large amount of + * blanking time for the 1048x780 mode. There is no need to replicate this + * blanking on the CSI2 bus, and the configuration of register 0x0301 allows the + * divider to be altered. + * + * The actual factor between link frequency and pixel rate is in the + * imx258_link_cfg, so use this to convert between the two. + * bits per pixel being 10, and D-PHY being DDR is assumed by this function, so + * the value is only the combination of number of lanes and pixel clock divider. */ -static u64 link_freq_to_pixel_rate(u64 f) +static u64 link_freq_to_pixel_rate(u64 f, const struct imx258_link_cfg *link_cfg) { - f *= 2 * 4; + f *= 2 * link_cfg->lf_to_pix_rate_factor; do_div(f, 10); return f; } /* Menu items for LINK_FREQ V4L2 control */ -static const s64 link_freq_menu_items[] = { - IMX258_LINK_FREQ_634MHZ, - IMX258_LINK_FREQ_320MHZ, +/* Configurations for supported link frequencies */ +static const s64 link_freq_menu_items_19_2[] = { + 633600000ULL, + 320000000ULL, }; +static const s64 link_freq_menu_items_24[] = { + 636000000ULL, + 321000000ULL, +}; + +#define REGS(_list) { .num_of_regs = ARRAY_SIZE(_list), .regs = _list, } + /* Link frequency configs */ -static const struct imx258_link_freq_config link_freq_configs[] = { +static const struct imx258_link_freq_config link_freq_configs_19_2[] = { [IMX258_LINK_FREQ_1267MBPS] = { .pixels_per_line = IMX258_PPL_DEFAULT, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mipi_data_rate_1267mbps), - .regs = mipi_data_rate_1267mbps, + .link_cfg = { + [IMX258_2_LANE_MODE] = { + .lf_to_pix_rate_factor = 2 * 2, + .reg_list = REGS(mipi_1267mbps_19_2mhz_2l), + }, + [IMX258_4_LANE_MODE] = { + .lf_to_pix_rate_factor = 4, + .reg_list = REGS(mipi_1267mbps_19_2mhz_4l), + }, } }, [IMX258_LINK_FREQ_640MBPS] = { .pixels_per_line = IMX258_PPL_DEFAULT, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mipi_data_rate_640mbps), - .regs = mipi_data_rate_640mbps, + .link_cfg = { + [IMX258_2_LANE_MODE] = { + .lf_to_pix_rate_factor = 2, + .reg_list = REGS(mipi_640mbps_19_2mhz_2l), + }, + [IMX258_4_LANE_MODE] = { + .lf_to_pix_rate_factor = 4, + .reg_list = REGS(mipi_640mbps_19_2mhz_4l), + }, + } + }, +}; + +static const struct imx258_link_freq_config link_freq_configs_24[] = { + [IMX258_LINK_FREQ_1267MBPS] = { + .pixels_per_line = IMX258_PPL_DEFAULT, + .link_cfg = { + [IMX258_2_LANE_MODE] = { + .lf_to_pix_rate_factor = 2, + .reg_list = REGS(mipi_1272mbps_24mhz_2l), + }, + [IMX258_4_LANE_MODE] = { + .lf_to_pix_rate_factor = 4, + .reg_list = REGS(mipi_1272mbps_24mhz_4l), + }, + } + }, + [IMX258_LINK_FREQ_640MBPS] = { + .pixels_per_line = IMX258_PPL_DEFAULT, + .link_cfg = { + [IMX258_2_LANE_MODE] = { + .lf_to_pix_rate_factor = 2 * 2, + .reg_list = REGS(mipi_642mbps_24mhz_2l), + }, + [IMX258_4_LANE_MODE] = { + .lf_to_pix_rate_factor = 4, + .reg_list = REGS(mipi_642mbps_24mhz_4l), + }, } }, }; @@ -568,14 +593,20 @@ static const struct imx258_link_freq_config link_freq_configs[] = { static const struct imx258_mode supported_modes[] = { { .width = 4208, - .height = 3118, + .height = 3120, .vts_def = IMX258_VTS_30FPS, .vts_min = IMX258_VTS_30FPS, .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_4208x3118_regs), - .regs = mode_4208x3118_regs, + .num_of_regs = ARRAY_SIZE(mode_4208x3120_regs), + .regs = mode_4208x3120_regs, }, .link_freq_index = IMX258_LINK_FREQ_1267MBPS, + .crop = { + .left = IMX258_PIXEL_ARRAY_LEFT, + .top = IMX258_PIXEL_ARRAY_TOP, + .width = 4208, + .height = 3120, + }, }, { .width = 2104, @@ -587,6 +618,12 @@ static const struct imx258_mode supported_modes[] = { .regs = mode_2104_1560_regs, }, .link_freq_index = IMX258_LINK_FREQ_640MBPS, + .crop = { + .left = IMX258_PIXEL_ARRAY_LEFT, + .top = IMX258_PIXEL_ARRAY_TOP, + .width = 4208, + .height = 3120, + }, }, { .width = 1048, @@ -598,12 +635,21 @@ static const struct imx258_mode supported_modes[] = { .regs = mode_1048_780_regs, }, .link_freq_index = IMX258_LINK_FREQ_640MBPS, + .crop = { + .left = IMX258_PIXEL_ARRAY_LEFT, + .top = IMX258_PIXEL_ARRAY_TOP, + .width = 4208, + .height = 3120, + }, }, }; struct imx258 { struct v4l2_subdev sd; struct media_pad pad; + struct regmap *regmap; + + const struct imx258_variant_cfg *variant_cfg; struct v4l2_ctrl_handler ctrl_handler; /* V4L2 Controls */ @@ -612,10 +658,18 @@ struct imx258 { struct v4l2_ctrl *vblank; struct v4l2_ctrl *hblank; struct v4l2_ctrl *exposure; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; /* Current mode */ const struct imx258_mode *cur_mode; + unsigned long link_freq_bitmap; + const struct imx258_link_freq_config *link_freq_configs; + const s64 *link_freq_menu_items; + unsigned int lane_mode_idx; + unsigned int csi2_flags; + /* * Mutex for serialized access: * Protect sensor module set pad format and start/stop streaming safely. @@ -623,6 +677,7 @@ struct imx258 { struct mutex mutex; struct clk *clk; + struct regulator_bulk_data supplies[IMX258_NUM_SUPPLIES]; }; static inline struct imx258 *to_imx258(struct v4l2_subdev *_sd) @@ -630,120 +685,66 @@ static inline struct imx258 *to_imx258(struct v4l2_subdev *_sd) return container_of(_sd, struct imx258, sd); } -/* Read registers up to 2 at a time */ -static int imx258_read_reg(struct imx258 *imx258, u16 reg, u32 len, u32 *val) +/* Get bayer order based on flip setting. */ +static u32 imx258_get_format_code(const struct imx258 *imx258) { - struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); - struct i2c_msg msgs[2]; - u8 addr_buf[2] = { reg >> 8, reg & 0xff }; - u8 data_buf[4] = { 0, }; - int ret; - - if (len > 4) - return -EINVAL; - - /* Write register address */ - msgs[0].addr = client->addr; - msgs[0].flags = 0; - msgs[0].len = ARRAY_SIZE(addr_buf); - msgs[0].buf = addr_buf; - - /* Read data from register */ - msgs[1].addr = client->addr; - msgs[1].flags = I2C_M_RD; - msgs[1].len = len; - msgs[1].buf = &data_buf[4 - len]; - - ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); - if (ret != ARRAY_SIZE(msgs)) - return -EIO; - - *val = get_unaligned_be32(data_buf); - - return 0; -} - -/* Write registers up to 2 at a time */ -static int imx258_write_reg(struct imx258 *imx258, u16 reg, u32 len, u32 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); - u8 buf[6]; - - if (len > 4) - return -EINVAL; - - put_unaligned_be16(reg, buf); - put_unaligned_be32(val << (8 * (4 - len)), buf + 2); - if (i2c_master_send(client, buf, len + 2) != len + 2) - return -EIO; - - return 0; -} - -/* Write a list of registers */ -static int imx258_write_regs(struct imx258 *imx258, - const struct imx258_reg *regs, u32 len) -{ - struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); unsigned int i; - int ret; - for (i = 0; i < len; i++) { - ret = imx258_write_reg(imx258, regs[i].address, 1, - regs[i].val); - if (ret) { - dev_err_ratelimited( - &client->dev, - "Failed to write reg 0x%4.4x. error = %d\n", - regs[i].address, ret); + lockdep_assert_held(&imx258->mutex); - return ret; - } - } + i = (imx258->vflip->val ? 2 : 0) | + (imx258->hflip->val ? 1 : 0); - return 0; + return codes[i]; } /* Open sub-device */ static int imx258_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { + struct imx258 *imx258 = to_imx258(sd); struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_state_get_format(fh->state, 0); + struct v4l2_rect *try_crop; /* Initialize try_fmt */ try_fmt->width = supported_modes[0].width; try_fmt->height = supported_modes[0].height; - try_fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; + try_fmt->code = imx258_get_format_code(imx258); try_fmt->field = V4L2_FIELD_NONE; + /* Initialize try_crop */ + try_crop = v4l2_subdev_state_get_crop(fh->state, 0); + try_crop->left = IMX258_PIXEL_ARRAY_LEFT; + try_crop->top = IMX258_PIXEL_ARRAY_TOP; + try_crop->width = IMX258_PIXEL_ARRAY_WIDTH; + try_crop->height = IMX258_PIXEL_ARRAY_HEIGHT; + return 0; } -static int imx258_update_digital_gain(struct imx258 *imx258, u32 len, u32 val) +static int imx258_update_digital_gain(struct imx258 *imx258, u32 val) { - int ret; + int ret = 0; - ret = imx258_write_reg(imx258, IMX258_REG_GR_DIGITAL_GAIN, - IMX258_REG_VALUE_16BIT, - val); - if (ret) - return ret; - ret = imx258_write_reg(imx258, IMX258_REG_GB_DIGITAL_GAIN, - IMX258_REG_VALUE_16BIT, - val); - if (ret) - return ret; - ret = imx258_write_reg(imx258, IMX258_REG_R_DIGITAL_GAIN, - IMX258_REG_VALUE_16BIT, - val); - if (ret) - return ret; - ret = imx258_write_reg(imx258, IMX258_REG_B_DIGITAL_GAIN, - IMX258_REG_VALUE_16BIT, - val); - if (ret) - return ret; - return 0; + cci_write(imx258->regmap, IMX258_REG_GR_DIGITAL_GAIN, val, &ret); + cci_write(imx258->regmap, IMX258_REG_GB_DIGITAL_GAIN, val, &ret); + cci_write(imx258->regmap, IMX258_REG_R_DIGITAL_GAIN, val, &ret); + cci_write(imx258->regmap, IMX258_REG_B_DIGITAL_GAIN, val, &ret); + + return ret; +} + +static void imx258_adjust_exposure_range(struct imx258 *imx258) +{ + int exposure_max, exposure_def; + + /* Honour the VBLANK limits when setting exposure. */ + exposure_max = imx258->cur_mode->height + imx258->vblank->val - + IMX258_EXPOSURE_OFFSET; + exposure_def = min(exposure_max, imx258->exposure->val); + __v4l2_ctrl_modify_range(imx258->exposure, imx258->exposure->minimum, + exposure_max, imx258->exposure->step, + exposure_def); } static int imx258_set_ctrl(struct v4l2_ctrl *ctrl) @@ -754,6 +755,13 @@ static int imx258_set_ctrl(struct v4l2_ctrl *ctrl) int ret = 0; /* + * The VBLANK control may change the limits of usable exposure, so check + * and adjust if necessary. + */ + if (ctrl->id == V4L2_CID_VBLANK) + imx258_adjust_exposure_range(imx258); + + /* * Applying V4L2 control value only happens * when power is up for streaming */ @@ -762,44 +770,46 @@ static int imx258_set_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_ANALOGUE_GAIN: - ret = imx258_write_reg(imx258, IMX258_REG_ANALOG_GAIN, - IMX258_REG_VALUE_16BIT, - ctrl->val); + ret = cci_write(imx258->regmap, IMX258_REG_ANALOG_GAIN, + ctrl->val, NULL); break; case V4L2_CID_EXPOSURE: - ret = imx258_write_reg(imx258, IMX258_REG_EXPOSURE, - IMX258_REG_VALUE_16BIT, - ctrl->val); + ret = cci_write(imx258->regmap, IMX258_REG_EXPOSURE, + ctrl->val, NULL); break; case V4L2_CID_DIGITAL_GAIN: - ret = imx258_update_digital_gain(imx258, IMX258_REG_VALUE_16BIT, - ctrl->val); + ret = imx258_update_digital_gain(imx258, ctrl->val); break; case V4L2_CID_TEST_PATTERN: - ret = imx258_write_reg(imx258, IMX258_REG_TEST_PATTERN, - IMX258_REG_VALUE_16BIT, - ctrl->val); - ret = imx258_write_reg(imx258, REG_MIRROR_FLIP_CONTROL, - IMX258_REG_VALUE_08BIT, - !ctrl->val ? REG_CONFIG_MIRROR_FLIP : - REG_CONFIG_FLIP_TEST_PATTERN); + ret = cci_write(imx258->regmap, IMX258_REG_TEST_PATTERN, + ctrl->val, NULL); break; case V4L2_CID_WIDE_DYNAMIC_RANGE: if (!ctrl->val) { - ret = imx258_write_reg(imx258, IMX258_REG_HDR, - IMX258_REG_VALUE_08BIT, - IMX258_HDR_RATIO_MIN); + ret = cci_write(imx258->regmap, IMX258_REG_HDR, + IMX258_HDR_RATIO_MIN, NULL); } else { - ret = imx258_write_reg(imx258, IMX258_REG_HDR, - IMX258_REG_VALUE_08BIT, - IMX258_HDR_ON); + ret = cci_write(imx258->regmap, IMX258_REG_HDR, + IMX258_HDR_ON, NULL); if (ret) break; - ret = imx258_write_reg(imx258, IMX258_REG_HDR_RATIO, - IMX258_REG_VALUE_08BIT, - BIT(IMX258_HDR_RATIO_MAX)); + ret = cci_write(imx258->regmap, IMX258_REG_HDR_RATIO, + BIT(IMX258_HDR_RATIO_MAX), NULL); } break; + case V4L2_CID_VBLANK: + ret = cci_write(imx258->regmap, IMX258_REG_FRM_LENGTH_LINES, + imx258->cur_mode->height + ctrl->val, NULL); + break; + case V4L2_CID_VFLIP: + case V4L2_CID_HFLIP: + ret = cci_write(imx258->regmap, REG_MIRROR_FLIP_CONTROL, + (imx258->hflip->val ? + REG_CONFIG_MIRROR_HFLIP : 0) | + (imx258->vflip->val ? + REG_CONFIG_MIRROR_VFLIP : 0), + NULL); + break; default: dev_info(&client->dev, "ctrl(id:0x%x,val:0x%x) is not handled\n", @@ -821,11 +831,13 @@ static int imx258_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { - /* Only one bayer order(GRBG) is supported */ + struct imx258 *imx258 = to_imx258(sd); + + /* Only one bayer format (10 bit) is supported */ if (code->index > 0) return -EINVAL; - code->code = MEDIA_BUS_FMT_SGRBG10_1X10; + code->code = imx258_get_format_code(imx258); return 0; } @@ -834,10 +846,11 @@ static int imx258_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { + struct imx258 *imx258 = to_imx258(sd); if (fse->index >= ARRAY_SIZE(supported_modes)) return -EINVAL; - if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) + if (fse->code != imx258_get_format_code(imx258)) return -EINVAL; fse->min_width = supported_modes[fse->index].width; @@ -848,12 +861,13 @@ static int imx258_enum_frame_size(struct v4l2_subdev *sd, return 0; } -static void imx258_update_pad_format(const struct imx258_mode *mode, +static void imx258_update_pad_format(struct imx258 *imx258, + const struct imx258_mode *mode, struct v4l2_subdev_format *fmt) { fmt->format.width = mode->width; fmt->format.height = mode->height; - fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; + fmt->format.code = imx258_get_format_code(imx258); fmt->format.field = V4L2_FIELD_NONE; } @@ -865,7 +879,7 @@ static int __imx258_get_pad_format(struct imx258 *imx258, fmt->format = *v4l2_subdev_state_get_format(sd_state, fmt->pad); else - imx258_update_pad_format(imx258->cur_mode, fmt); + imx258_update_pad_format(imx258, imx258->cur_mode, fmt); return 0; } @@ -889,8 +903,10 @@ static int imx258_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_format *fmt) { struct imx258 *imx258 = to_imx258(sd); - const struct imx258_mode *mode; + const struct imx258_link_freq_config *link_freq_cfgs; + const struct imx258_link_cfg *link_cfg; struct v4l2_mbus_framefmt *framefmt; + const struct imx258_mode *mode; s32 vblank_def; s32 vblank_min; s64 h_blank; @@ -899,13 +915,12 @@ static int imx258_set_pad_format(struct v4l2_subdev *sd, mutex_lock(&imx258->mutex); - /* Only one raw bayer(GBRG) order is supported */ - fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; + fmt->format.code = imx258_get_format_code(imx258); mode = v4l2_find_nearest_size(supported_modes, ARRAY_SIZE(supported_modes), width, height, fmt->format.width, fmt->format.height); - imx258_update_pad_format(mode, fmt); + imx258_update_pad_format(imx258, mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; @@ -913,9 +928,14 @@ static int imx258_set_pad_format(struct v4l2_subdev *sd, imx258->cur_mode = mode; __v4l2_ctrl_s_ctrl(imx258->link_freq, mode->link_freq_index); - link_freq = link_freq_menu_items[mode->link_freq_index]; - pixel_rate = link_freq_to_pixel_rate(link_freq); - __v4l2_ctrl_s_ctrl_int64(imx258->pixel_rate, pixel_rate); + link_freq = imx258->link_freq_menu_items[mode->link_freq_index]; + link_freq_cfgs = + &imx258->link_freq_configs[mode->link_freq_index]; + + link_cfg = &link_freq_cfgs->link_cfg[imx258->lane_mode_idx]; + pixel_rate = link_freq_to_pixel_rate(link_freq, link_cfg); + __v4l2_ctrl_modify_range(imx258->pixel_rate, pixel_rate, + pixel_rate, 1, pixel_rate); /* Update limits and set FPS to default */ vblank_def = imx258->cur_mode->vts_def - imx258->cur_mode->height; @@ -927,7 +947,7 @@ static int imx258_set_pad_format(struct v4l2_subdev *sd, vblank_def); __v4l2_ctrl_s_ctrl(imx258->vblank, vblank_def); h_blank = - link_freq_configs[mode->link_freq_index].pixels_per_line + imx258->link_freq_configs[mode->link_freq_index].pixels_per_line - imx258->cur_mode->width; __v4l2_ctrl_modify_range(imx258->hblank, h_blank, h_blank, 1, h_blank); @@ -938,48 +958,125 @@ static int imx258_set_pad_format(struct v4l2_subdev *sd, return 0; } +static const struct v4l2_rect * +__imx258_get_pad_crop(struct imx258 *imx258, + struct v4l2_subdev_state *sd_state, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_state_get_crop(sd_state, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &imx258->cur_mode->crop; + } + + return NULL; +} + +static int imx258_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: { + struct imx258 *imx258 = to_imx258(sd); + + mutex_lock(&imx258->mutex); + sel->r = *__imx258_get_pad_crop(imx258, sd_state, sel->pad, + sel->which); + mutex_unlock(&imx258->mutex); + + return 0; + } + + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = IMX258_NATIVE_WIDTH; + sel->r.height = IMX258_NATIVE_HEIGHT; + + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.left = IMX258_PIXEL_ARRAY_LEFT; + sel->r.top = IMX258_PIXEL_ARRAY_TOP; + sel->r.width = IMX258_PIXEL_ARRAY_WIDTH; + sel->r.height = IMX258_PIXEL_ARRAY_HEIGHT; + + return 0; + } + + return -EINVAL; +} + /* Start streaming */ static int imx258_start_streaming(struct imx258 *imx258) { struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); const struct imx258_reg_list *reg_list; + const struct imx258_link_freq_config *link_freq_cfg; int ret, link_freq_index; + ret = cci_write(imx258->regmap, IMX258_REG_RESET, 0x01, NULL); + if (ret) { + dev_err(&client->dev, "%s failed to reset sensor\n", __func__); + return ret; + } + + /* 12ms is required from poweron to standby */ + fsleep(12000); + /* Setup PLL */ link_freq_index = imx258->cur_mode->link_freq_index; - reg_list = &link_freq_configs[link_freq_index].reg_list; - ret = imx258_write_regs(imx258, reg_list->regs, reg_list->num_of_regs); + link_freq_cfg = &imx258->link_freq_configs[link_freq_index]; + + reg_list = &link_freq_cfg->link_cfg[imx258->lane_mode_idx].reg_list; + ret = cci_multi_reg_write(imx258->regmap, reg_list->regs, reg_list->num_of_regs, NULL); if (ret) { dev_err(&client->dev, "%s failed to set plls\n", __func__); return ret; } - /* Apply default values of current mode */ - reg_list = &imx258->cur_mode->reg_list; - ret = imx258_write_regs(imx258, reg_list->regs, reg_list->num_of_regs); + ret = cci_multi_reg_write(imx258->regmap, mode_common_regs, + ARRAY_SIZE(mode_common_regs), NULL); if (ret) { - dev_err(&client->dev, "%s failed to set mode\n", __func__); + dev_err(&client->dev, "%s failed to set common regs\n", __func__); return ret; } - /* Set Orientation be 180 degree */ - ret = imx258_write_reg(imx258, REG_MIRROR_FLIP_CONTROL, - IMX258_REG_VALUE_08BIT, REG_CONFIG_MIRROR_FLIP); + ret = cci_multi_reg_write(imx258->regmap, imx258->variant_cfg->regs, + imx258->variant_cfg->num_regs, NULL); if (ret) { - dev_err(&client->dev, "%s failed to set orientation\n", + dev_err(&client->dev, "%s failed to set variant config\n", __func__); return ret; } + ret = cci_write(imx258->regmap, IMX258_CLK_BLANK_STOP, + !!(imx258->csi2_flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK), + NULL); + if (ret) { + dev_err(&client->dev, "%s failed to set clock lane mode\n", __func__); + return ret; + } + + /* Apply default values of current mode */ + reg_list = &imx258->cur_mode->reg_list; + ret = cci_multi_reg_write(imx258->regmap, reg_list->regs, reg_list->num_of_regs, NULL); + if (ret) { + dev_err(&client->dev, "%s failed to set mode\n", __func__); + return ret; + } + /* Apply customized values from user */ ret = __v4l2_ctrl_handler_setup(imx258->sd.ctrl_handler); if (ret) return ret; /* set stream on register */ - return imx258_write_reg(imx258, IMX258_REG_MODE_SELECT, - IMX258_REG_VALUE_08BIT, - IMX258_MODE_STREAMING); + return cci_write(imx258->regmap, IMX258_REG_MODE_SELECT, + IMX258_MODE_STREAMING, NULL); } /* Stop streaming */ @@ -989,8 +1086,8 @@ static int imx258_stop_streaming(struct imx258 *imx258) int ret; /* set stream off register */ - ret = imx258_write_reg(imx258, IMX258_REG_MODE_SELECT, - IMX258_REG_VALUE_08BIT, IMX258_MODE_STANDBY); + ret = cci_write(imx258->regmap, IMX258_REG_MODE_SELECT, + IMX258_MODE_STANDBY, NULL); if (ret) dev_err(&client->dev, "%s failed to set stream\n", __func__); @@ -1007,9 +1104,19 @@ static int imx258_power_on(struct device *dev) struct imx258 *imx258 = to_imx258(sd); int ret; + ret = regulator_bulk_enable(IMX258_NUM_SUPPLIES, + imx258->supplies); + if (ret) { + dev_err(dev, "%s: failed to enable regulators\n", + __func__); + return ret; + } + ret = clk_prepare_enable(imx258->clk); - if (ret) + if (ret) { dev_err(dev, "failed to enable clock\n"); + regulator_bulk_disable(IMX258_NUM_SUPPLIES, imx258->supplies); + } return ret; } @@ -1020,6 +1127,7 @@ static int imx258_power_off(struct device *dev) struct imx258 *imx258 = to_imx258(sd); clk_disable_unprepare(imx258->clk); + regulator_bulk_disable(IMX258_NUM_SUPPLIES, imx258->supplies); return 0; } @@ -1066,10 +1174,10 @@ static int imx258_identify_module(struct imx258 *imx258) { struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); int ret; - u32 val; + u64 val; - ret = imx258_read_reg(imx258, IMX258_REG_CHIP_ID, - IMX258_REG_VALUE_16BIT, &val); + ret = cci_read(imx258->regmap, IMX258_REG_CHIP_ID, + &val, NULL); if (ret) { dev_err(&client->dev, "failed to read chip id %x\n", IMX258_CHIP_ID); @@ -1077,7 +1185,7 @@ static int imx258_identify_module(struct imx258 *imx258) } if (val != IMX258_CHIP_ID) { - dev_err(&client->dev, "chip id mismatch: %x!=%x\n", + dev_err(&client->dev, "chip id mismatch: %x!=%llx\n", IMX258_CHIP_ID, val); return -EIO; } @@ -1094,6 +1202,7 @@ static const struct v4l2_subdev_pad_ops imx258_pad_ops = { .get_fmt = imx258_get_pad_format, .set_fmt = imx258_set_pad_format, .enum_frame_size = imx258_enum_frame_size, + .get_selection = imx258_get_selection, }; static const struct v4l2_subdev_ops imx258_subdev_ops = { @@ -1109,13 +1218,13 @@ static const struct v4l2_subdev_internal_ops imx258_internal_ops = { static int imx258_init_controls(struct imx258 *imx258) { struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); + const struct imx258_link_freq_config *link_freq_cfgs; struct v4l2_fwnode_device_properties props; struct v4l2_ctrl_handler *ctrl_hdlr; - struct v4l2_ctrl *vflip, *hflip; + const struct imx258_link_cfg *link_cfg; s64 vblank_def; s64 vblank_min; - s64 pixel_rate_min; - s64 pixel_rate_max; + s64 pixel_rate; int ret; ctrl_hdlr = &imx258->ctrl_handler; @@ -1128,32 +1237,33 @@ static int imx258_init_controls(struct imx258 *imx258) imx258->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_LINK_FREQ, - ARRAY_SIZE(link_freq_menu_items) - 1, + ARRAY_SIZE(link_freq_menu_items_19_2) - 1, 0, - link_freq_menu_items); + imx258->link_freq_menu_items); if (imx258->link_freq) imx258->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; - /* The driver only supports one bayer order and flips by default. */ - hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops, - V4L2_CID_HFLIP, 1, 1, 1, 1); - if (hflip) - hflip->flags |= V4L2_CTRL_FLAG_READ_ONLY; + imx258->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 1); + if (imx258->hflip) + imx258->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + imx258->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 1); + if (imx258->vflip) + imx258->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; - vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops, - V4L2_CID_VFLIP, 1, 1, 1, 1); - if (vflip) - vflip->flags |= V4L2_CTRL_FLAG_READ_ONLY; + link_freq_cfgs = &imx258->link_freq_configs[0]; + link_cfg = link_freq_cfgs[imx258->lane_mode_idx].link_cfg; + pixel_rate = link_freq_to_pixel_rate(imx258->link_freq_menu_items[0], + link_cfg); - pixel_rate_max = link_freq_to_pixel_rate(link_freq_menu_items[0]); - pixel_rate_min = link_freq_to_pixel_rate(link_freq_menu_items[1]); /* By default, PIXEL_RATE is read only */ imx258->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_PIXEL_RATE, - pixel_rate_min, pixel_rate_max, - 1, pixel_rate_max); - + pixel_rate, pixel_rate, + 1, pixel_rate); vblank_def = imx258->cur_mode->vts_def - imx258->cur_mode->height; vblank_min = imx258->cur_mode->vts_min - imx258->cur_mode->height; @@ -1163,9 +1273,6 @@ static int imx258_init_controls(struct imx258 *imx258) IMX258_VTS_MAX - imx258->cur_mode->height, 1, vblank_def); - if (imx258->vblank) - imx258->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; - imx258->hblank = v4l2_ctrl_new_std( ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_HBLANK, IMX258_PPL_DEFAULT - imx258->cur_mode->width, @@ -1232,9 +1339,25 @@ static void imx258_free_controls(struct imx258 *imx258) mutex_destroy(&imx258->mutex); } +static int imx258_get_regulators(struct imx258 *imx258, + struct i2c_client *client) +{ + unsigned int i; + + for (i = 0; i < IMX258_NUM_SUPPLIES; i++) + imx258->supplies[i].supply = imx258_supply_name[i]; + + return devm_regulator_bulk_get(&client->dev, + IMX258_NUM_SUPPLIES, imx258->supplies); +} + static int imx258_probe(struct i2c_client *client) { struct imx258 *imx258; + struct fwnode_handle *endpoint; + struct v4l2_fwnode_endpoint ep = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; int ret; u32 val = 0; @@ -1242,6 +1365,18 @@ static int imx258_probe(struct i2c_client *client) if (!imx258) return -ENOMEM; + imx258->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(imx258->regmap)) { + ret = PTR_ERR(imx258->regmap); + dev_err(&client->dev, "failed to initialize CCI: %d\n", ret); + return ret; + } + + ret = imx258_get_regulators(imx258, client); + if (ret) + return dev_err_probe(&client->dev, ret, + "failed to get regulators\n"); + imx258->clk = devm_clk_get_optional(&client->dev, NULL); if (IS_ERR(imx258->clk)) return dev_err_probe(&client->dev, PTR_ERR(imx258->clk), @@ -1254,18 +1389,74 @@ static int imx258_probe(struct i2c_client *client) } else { val = clk_get_rate(imx258->clk); } - if (val != IMX258_INPUT_CLOCK_FREQ) { - dev_err(&client->dev, "input clock frequency not supported\n"); + + switch (val) { + case 19200000: + imx258->link_freq_configs = link_freq_configs_19_2; + imx258->link_freq_menu_items = link_freq_menu_items_19_2; + break; + case 24000000: + imx258->link_freq_configs = link_freq_configs_24; + imx258->link_freq_menu_items = link_freq_menu_items_24; + break; + default: + dev_err(&client->dev, "input clock frequency of %u not supported\n", + val); return -EINVAL; } + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL); + if (!endpoint) { + dev_err(&client->dev, "Endpoint node not found\n"); + return -EINVAL; + } + + ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep); + fwnode_handle_put(endpoint); + if (ret) { + dev_err(&client->dev, "Parsing endpoint node failed\n"); + return ret; + } + + ret = v4l2_link_freq_to_bitmap(&client->dev, + ep.link_frequencies, + ep.nr_of_link_frequencies, + imx258->link_freq_menu_items, + ARRAY_SIZE(link_freq_menu_items_19_2), + &imx258->link_freq_bitmap); + if (ret) { + dev_err(&client->dev, "Link frequency not supported\n"); + goto error_endpoint_free; + } + + /* Get number of data lanes */ + switch (ep.bus.mipi_csi2.num_data_lanes) { + case 2: + imx258->lane_mode_idx = IMX258_2_LANE_MODE; + break; + case 4: + imx258->lane_mode_idx = IMX258_4_LANE_MODE; + break; + default: + dev_err(&client->dev, "Invalid data lanes: %u\n", + ep.bus.mipi_csi2.num_data_lanes); + ret = -EINVAL; + goto error_endpoint_free; + } + + imx258->csi2_flags = ep.bus.mipi_csi2.flags; + + imx258->variant_cfg = device_get_match_data(&client->dev); + if (!imx258->variant_cfg) + imx258->variant_cfg = &imx258_cfg; + /* Initialize subdev */ v4l2_i2c_subdev_init(&imx258->sd, client, &imx258_subdev_ops); /* Will be powered off via pm_runtime_idle */ ret = imx258_power_on(&client->dev); if (ret) - return ret; + goto error_endpoint_free; /* Check module identity */ ret = imx258_identify_module(imx258); @@ -1298,6 +1489,7 @@ static int imx258_probe(struct i2c_client *client) pm_runtime_set_active(&client->dev); pm_runtime_enable(&client->dev); pm_runtime_idle(&client->dev); + v4l2_fwnode_endpoint_free(&ep); return 0; @@ -1310,6 +1502,9 @@ error_handler_free: error_identify: imx258_power_off(&client->dev); +error_endpoint_free: + v4l2_fwnode_endpoint_free(&ep); + return ret; } @@ -1342,7 +1537,8 @@ MODULE_DEVICE_TABLE(acpi, imx258_acpi_ids); #endif static const struct of_device_id imx258_dt_ids[] = { - { .compatible = "sony,imx258" }, + { .compatible = "sony,imx258", .data = &imx258_cfg }, + { .compatible = "sony,imx258-pdaf", .data = &imx258_pdaf_cfg }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, imx258_dt_ids); diff --git a/drivers/media/i2c/imx283.c b/drivers/media/i2c/imx283.c new file mode 100644 index 000000000000..8490618c5071 --- /dev/null +++ b/drivers/media/i2c/imx283.c @@ -0,0 +1,1612 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * V4L2 Support for the IMX283 + * + * Diagonal 15.86 mm (Type 1) CMOS Image Sensor with Square Pixel for Color + * Cameras. + * + * Copyright (C) 2024 Ideas on Board Oy. + * + * Based on Sony IMX283 driver prepared by Will Whang + * + * Based on Sony imx477 camera driver + * Copyright (C) 2019-2020 Raspberry Pi (Trading) Ltd + */ + +#include <linux/array_size.h> +#include <linux/bitops.h> +#include <linux/container_of.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/minmax.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/pm_runtime.h> +#include <linux/property.h> +#include <linux/regulator/consumer.h> +#include <linux/types.h> +#include <linux/units.h> +#include <media/v4l2-cci.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-mediabus.h> + +/* Chip ID */ +#define IMX283_REG_CHIP_ID CCI_REG8(0x3000) +#define IMX283_CHIP_ID 0x0b // Default power on state + +#define IMX283_REG_STANDBY CCI_REG8(0x3000) +#define IMX283_ACTIVE 0 +#define IMX283_STANDBY BIT(0) +#define IMX283_STBLOGIC BIT(1) +#define IMX283_STBMIPI BIT(2) +#define IMX283_STBDV BIT(3) +#define IMX283_SLEEP BIT(4) + +#define IMX283_REG_CLAMP CCI_REG8(0x3001) +#define IMX283_CLPSQRST BIT(4) + +#define IMX283_REG_PLSTMG08 CCI_REG8(0x3003) +#define IMX283_PLSTMG08_VAL 0x77 + +#define IMX283_REG_MDSEL1 CCI_REG8(0x3004) +#define IMX283_REG_MDSEL2 CCI_REG8(0x3005) +#define IMX283_REG_MDSEL3 CCI_REG8(0x3006) +#define IMX283_MDSEL3_VCROP_EN BIT(5) +#define IMX283_REG_MDSEL4 CCI_REG8(0x3007) +#define IMX283_MDSEL4_VCROP_EN (BIT(4) | BIT(6)) + +#define IMX283_REG_SVR CCI_REG16_LE(0x3009) + +#define IMX283_REG_HTRIMMING CCI_REG8(0x300b) +#define IMX283_MDVREV BIT(0) /* VFLIP */ +#define IMX283_HTRIMMING_EN BIT(4) + +#define IMX283_REG_VWINPOS CCI_REG16_LE(0x300f) +#define IMX283_REG_VWIDCUT CCI_REG16_LE(0x3011) + +#define IMX283_REG_MDSEL7 CCI_REG16_LE(0x3013) + +/* CSI Clock Configuration */ +#define IMX283_REG_TCLKPOST CCI_REG8(0x3018) +#define IMX283_REG_THSPREPARE CCI_REG8(0x301a) +#define IMX283_REG_THSZERO CCI_REG8(0x301c) +#define IMX283_REG_THSTRAIL CCI_REG8(0x301e) +#define IMX283_REG_TCLKTRAIL CCI_REG8(0x3020) +#define IMX283_REG_TCLKPREPARE CCI_REG8(0x3022) +#define IMX283_REG_TCLKZERO CCI_REG16_LE(0x3024) +#define IMX283_REG_TLPX CCI_REG8(0x3026) +#define IMX283_REG_THSEXIT CCI_REG8(0x3028) +#define IMX283_REG_TCLKPRE CCI_REG8(0x302a) +#define IMX283_REG_SYSMODE CCI_REG8(0x3104) + +#define IMX283_REG_Y_OUT_SIZE CCI_REG16_LE(0x302f) +#define IMX283_REG_WRITE_VSIZE CCI_REG16_LE(0x3031) +#define IMX283_REG_OB_SIZE_V CCI_REG8(0x3033) + +/* HMAX internal HBLANK */ +#define IMX283_REG_HMAX CCI_REG16_LE(0x3036) +#define IMX283_HMAX_MAX (BIT(16) - 1) + +/* VMAX internal VBLANK */ +#define IMX283_REG_VMAX CCI_REG24_LE(0x3038) +#define IMX283_VMAX_MAX (BIT(16) - 1) + +/* SHR internal */ +#define IMX283_REG_SHR CCI_REG16_LE(0x303b) +#define IMX283_SHR_MIN 11 + +/* + * Analog gain control + * Gain [dB] = -20log{(2048 - value [10:0]) /2048} + * Range: 0dB to approximately +27dB + */ +#define IMX283_REG_ANALOG_GAIN CCI_REG16_LE(0x3042) +#define IMX283_ANA_GAIN_MIN 0 +#define IMX283_ANA_GAIN_MAX 1957 +#define IMX283_ANA_GAIN_STEP 1 +#define IMX283_ANA_GAIN_DEFAULT 0x0 + +/* + * Digital gain control + * Gain [dB] = value * 6 + * Range: 0dB to +18db + */ +#define IMX283_REG_DIGITAL_GAIN CCI_REG8(0x3044) +#define IMX283_DGTL_GAIN_MIN 0 +#define IMX283_DGTL_GAIN_MAX 3 +#define IMX283_DGTL_GAIN_DEFAULT 0 +#define IMX283_DGTL_GAIN_STEP 1 + +#define IMX283_REG_HTRIMMING_START CCI_REG16_LE(0x3058) +#define IMX283_REG_HTRIMMING_END CCI_REG16_LE(0x305a) + +#define IMX283_REG_MDSEL18 CCI_REG16_LE(0x30f6) + +/* Master Mode Operation Control */ +#define IMX283_REG_XMSTA CCI_REG8(0x3105) +#define IMX283_XMSTA BIT(0) + +#define IMX283_REG_SYNCDRV CCI_REG8(0x3107) +#define IMX283_SYNCDRV_XHS_XVS (0xa0 | 0x02) +#define IMX283_SYNCDRV_HIZ (0xa0 | 0x03) + +/* PLL Standby */ +#define IMX283_REG_STBPL CCI_REG8(0x320b) +#define IMX283_STBPL_NORMAL 0x00 +#define IMX283_STBPL_STANDBY 0x03 + +/* Input Frequency Setting */ +#define IMX283_REG_PLRD1 CCI_REG8(0x36c1) +#define IMX283_REG_PLRD2 CCI_REG16_LE(0x36c2) +#define IMX283_REG_PLRD3 CCI_REG8(0x36f7) +#define IMX283_REG_PLRD4 CCI_REG8(0x36f8) + +#define IMX283_REG_PLSTMG02 CCI_REG8(0x36aa) +#define IMX283_PLSTMG02_VAL 0x00 + +#define IMX283_REG_EBD_X_OUT_SIZE CCI_REG16_LE(0x3a54) + +/* Test pattern generator */ +#define IMX283_REG_TPG_CTRL CCI_REG8(0x3156) +#define IMX283_TPG_CTRL_CLKEN BIT(0) +#define IMX283_TPG_CTRL_PATEN BIT(4) + +#define IMX283_REG_TPG_PAT CCI_REG8(0x3157) +#define IMX283_TPG_PAT_ALL_000 0x00 +#define IMX283_TPG_PAT_ALL_FFF 0x01 +#define IMX283_TPG_PAT_ALL_555 0x02 +#define IMX283_TPG_PAT_ALL_AAA 0x03 +#define IMX283_TPG_PAT_H_COLOR_BARS 0x0a +#define IMX283_TPG_PAT_V_COLOR_BARS 0x0b + +/* Exposure control */ +#define IMX283_EXPOSURE_MIN 52 +#define IMX283_EXPOSURE_STEP 1 +#define IMX283_EXPOSURE_DEFAULT 1000 +#define IMX283_EXPOSURE_MAX 49865 + +#define IMAGE_PAD 0 + +#define IMX283_XCLR_MIN_DELAY_US (1 * USEC_PER_MSEC) +#define IMX283_XCLR_DELAY_RANGE_US (1 * USEC_PER_MSEC) + +/* IMX283 native and active pixel array size. */ +static const struct v4l2_rect imx283_native_area = { + .top = 0, + .left = 0, + .width = 5592, + .height = 3710, +}; + +static const struct v4l2_rect imx283_active_area = { + .top = 40, + .left = 108, + .width = 5472, + .height = 3648, +}; + +struct imx283_reg_list { + unsigned int num_of_regs; + const struct cci_reg_sequence *regs; +}; + +/* Mode : resolution and related config values */ +struct imx283_mode { + unsigned int mode; + + /* Bits per pixel */ + unsigned int bpp; + + /* Frame width */ + unsigned int width; + + /* Frame height */ + unsigned int height; + + /* + * Minimum horizontal timing in pixel-units + * + * Note that HMAX is written in 72MHz units, and the datasheet assumes a + * 720MHz link frequency. Convert datasheet values with the following: + * + * For 12 bpp modes (480Mbps) convert with: + * hmax = [hmax in 72MHz units] * 480 / 72 + * + * For 10 bpp modes (576Mbps) convert with: + * hmax = [hmax in 72MHz units] * 576 / 72 + */ + u32 min_hmax; + + /* minimum V-timing in lines */ + u32 min_vmax; + + /* default H-timing */ + u32 default_hmax; + + /* default V-timing */ + u32 default_vmax; + + /* minimum SHR */ + u32 min_shr; + + /* + * Per-mode vertical crop constants used to calculate values + * of IMX283REG_WIDCUT and IMX283_REG_VWINPOS. + */ + u32 veff; + u32 vst; + u32 vct; + + /* Horizontal and vertical binning ratio */ + u8 hbin_ratio; + u8 vbin_ratio; + + /* Optical Blanking */ + u32 horizontal_ob; + u32 vertical_ob; + + /* Analog crop rectangle. */ + struct v4l2_rect crop; +}; + +struct imx283_input_frequency { + unsigned int mhz; + unsigned int reg_count; + struct cci_reg_sequence regs[4]; +}; + +static const struct imx283_input_frequency imx283_frequencies[] = { + { + .mhz = 6 * HZ_PER_MHZ, + .reg_count = 4, + .regs = { + { IMX283_REG_PLRD1, 0x00 }, + { IMX283_REG_PLRD2, 0x00f0 }, + { IMX283_REG_PLRD3, 0x00 }, + { IMX283_REG_PLRD4, 0xc0 }, + }, + }, + { + .mhz = 12 * HZ_PER_MHZ, + .reg_count = 4, + .regs = { + { IMX283_REG_PLRD1, 0x01 }, + { IMX283_REG_PLRD2, 0x00f0 }, + { IMX283_REG_PLRD3, 0x01 }, + { IMX283_REG_PLRD4, 0xc0 }, + }, + }, + { + .mhz = 18 * HZ_PER_MHZ, + .reg_count = 4, + .regs = { + { IMX283_REG_PLRD1, 0x01 }, + { IMX283_REG_PLRD2, 0x00a0 }, + { IMX283_REG_PLRD3, 0x01 }, + { IMX283_REG_PLRD4, 0x80 }, + }, + }, + { + .mhz = 24 * HZ_PER_MHZ, + .reg_count = 4, + .regs = { + { IMX283_REG_PLRD1, 0x02 }, + { IMX283_REG_PLRD2, 0x00f0 }, + { IMX283_REG_PLRD3, 0x02 }, + { IMX283_REG_PLRD4, 0xc0 }, + }, + }, +}; + +enum imx283_modes { + IMX283_MODE_0, + IMX283_MODE_1, + IMX283_MODE_1A, + IMX283_MODE_1S, + IMX283_MODE_2, + IMX283_MODE_2A, + IMX283_MODE_3, + IMX283_MODE_4, + IMX283_MODE_5, + IMX283_MODE_6, +}; + +struct imx283_readout_mode { + u8 mdsel1; + u8 mdsel2; + u8 mdsel3; + u8 mdsel4; +}; + +static const struct imx283_readout_mode imx283_readout_modes[] = { + /* All pixel scan modes */ + [IMX283_MODE_0] = { 0x04, 0x03, 0x10, 0x00 }, /* 12 bit */ + [IMX283_MODE_1] = { 0x04, 0x01, 0x00, 0x00 }, /* 10 bit */ + [IMX283_MODE_1A] = { 0x04, 0x01, 0x20, 0x50 }, /* 10 bit */ + [IMX283_MODE_1S] = { 0x04, 0x41, 0x20, 0x50 }, /* 10 bit */ + + /* Horizontal / Vertical 2/2-line binning */ + [IMX283_MODE_2] = { 0x0d, 0x11, 0x50, 0x00 }, /* 12 bit */ + [IMX283_MODE_2A] = { 0x0d, 0x11, 0x70, 0x50 }, /* 12 bit */ + + /* Horizontal / Vertical 3/3-line binning */ + [IMX283_MODE_3] = { 0x1e, 0x18, 0x10, 0x00 }, /* 12 bit */ + + /* Vertical 2/9 subsampling, horizontal 3 binning cropping */ + [IMX283_MODE_4] = { 0x29, 0x18, 0x30, 0x50 }, /* 12 bit */ + + /* Vertical 2/19 subsampling binning, horizontal 3 binning */ + [IMX283_MODE_5] = { 0x2d, 0x18, 0x10, 0x00 }, /* 12 bit */ + + /* Vertical 2 binning horizontal 2/4, subsampling 16:9 cropping */ + [IMX283_MODE_6] = { 0x18, 0x21, 0x00, 0x09 }, /* 10 bit */ + + /* + * New modes should make sure the offset period is complied. + * See imx283_exposure() for reference. + */ +}; + +static const struct cci_reg_sequence mipi_data_rate_1440Mbps[] = { + /* The default register settings provide the 1440Mbps rate */ + { CCI_REG8(0x36c5), 0x00 }, /* Undocumented */ + { CCI_REG8(0x3ac4), 0x00 }, /* Undocumented */ + + { IMX283_REG_STBPL, 0x00 }, + { IMX283_REG_TCLKPOST, 0xa7 }, + { IMX283_REG_THSPREPARE, 0x6f }, + { IMX283_REG_THSZERO, 0x9f }, + { IMX283_REG_THSTRAIL, 0x5f }, + { IMX283_REG_TCLKTRAIL, 0x5f }, + { IMX283_REG_TCLKPREPARE, 0x6f }, + { IMX283_REG_TCLKZERO, 0x017f }, + { IMX283_REG_TLPX, 0x4f }, + { IMX283_REG_THSEXIT, 0x47 }, + { IMX283_REG_TCLKPRE, 0x07 }, + { IMX283_REG_SYSMODE, 0x02 }, +}; + +static const struct cci_reg_sequence mipi_data_rate_720Mbps[] = { + /* Undocumented Additions "For 720MBps" Setting */ + { CCI_REG8(0x36c5), 0x01 }, /* Undocumented */ + { CCI_REG8(0x3ac4), 0x01 }, /* Undocumented */ + + { IMX283_REG_STBPL, 0x00 }, + { IMX283_REG_TCLKPOST, 0x77 }, + { IMX283_REG_THSPREPARE, 0x37 }, + { IMX283_REG_THSZERO, 0x67 }, + { IMX283_REG_THSTRAIL, 0x37 }, + { IMX283_REG_TCLKTRAIL, 0x37 }, + { IMX283_REG_TCLKPREPARE, 0x37 }, + { IMX283_REG_TCLKZERO, 0xdf }, + { IMX283_REG_TLPX, 0x2f }, + { IMX283_REG_THSEXIT, 0x47 }, + { IMX283_REG_TCLKPRE, 0x0f }, + { IMX283_REG_SYSMODE, 0x02 }, +}; + +static const s64 link_frequencies[] = { + 720 * HZ_PER_MHZ, /* 1440 Mbps lane data rate */ + 360 * HZ_PER_MHZ, /* 720 Mbps data lane rate */ +}; + +static const struct imx283_reg_list link_freq_reglist[] = { + { /* 720 MHz */ + .num_of_regs = ARRAY_SIZE(mipi_data_rate_1440Mbps), + .regs = mipi_data_rate_1440Mbps, + }, + { /* 360 MHz */ + .num_of_regs = ARRAY_SIZE(mipi_data_rate_720Mbps), + .regs = mipi_data_rate_720Mbps, + }, +}; + +/* Mode configs */ +static const struct imx283_mode supported_modes_12bit[] = { + { + /* 20MPix 21.40 fps readout mode 0 */ + .mode = IMX283_MODE_0, + .bpp = 12, + .width = 5472, + .height = 3648, + .min_hmax = 5914, /* 887 @ 480MHz/72MHz */ + .min_vmax = 3793, /* Lines */ + + .veff = 3694, + .vst = 0, + .vct = 0, + + .hbin_ratio = 1, + .vbin_ratio = 1, + + /* 20.00 FPS */ + .default_hmax = 6000, /* 900 @ 480MHz/72MHz */ + .default_vmax = 4000, + + .min_shr = 11, + .horizontal_ob = 96, + .vertical_ob = 16, + .crop = { + .top = 40, + .left = 108, + .width = 5472, + .height = 3648, + }, + }, + { + /* + * Readout mode 2 : 2/2 binned mode (2736x1824) + */ + .mode = IMX283_MODE_2, + .bpp = 12, + .width = 2736, + .height = 1824, + .min_hmax = 2414, /* Pixels (362 * 480MHz/72MHz + padding) */ + .min_vmax = 3840, /* Lines */ + + /* 50.00 FPS */ + .default_hmax = 2500, /* 375 @ 480MHz/72Mhz */ + .default_vmax = 3840, + + .veff = 1824, + .vst = 0, + .vct = 0, + + .hbin_ratio = 2, + .vbin_ratio = 2, + + .min_shr = 12, + .horizontal_ob = 48, + .vertical_ob = 4, + + .crop = { + .top = 40, + .left = 108, + .width = 5472, + .height = 3648, + }, + }, +}; + +static const struct imx283_mode supported_modes_10bit[] = { + { + /* 20MPix 25.48 fps readout mode 1 */ + .mode = IMX283_MODE_1, + .bpp = 10, + .width = 5472, + .height = 3648, + .min_hmax = 5960, /* 745 @ 576MHz / 72MHz */ + .min_vmax = 3793, + + /* 25.00 FPS */ + .default_hmax = 6000, /* 750 @ 576MHz / 72MHz */ + .default_vmax = 3840, + + .min_shr = 10, + .horizontal_ob = 96, + .vertical_ob = 16, + .crop = { + .top = 40, + .left = 108, + .width = 5472, + .height = 3648, + }, + }, +}; + +static const u32 imx283_mbus_codes[] = { + MEDIA_BUS_FMT_SRGGB12_1X12, + MEDIA_BUS_FMT_SRGGB10_1X10, +}; + +/* regulator supplies */ +static const char *const imx283_supply_name[] = { + "vadd", /* Analog (2.9V) supply */ + "vdd1", /* Supply Voltage 2 (1.8V) supply */ + "vdd2", /* Supply Voltage 3 (1.2V) supply */ +}; + +struct imx283 { + struct device *dev; + struct regmap *cci; + + const struct imx283_input_frequency *freq; + + struct v4l2_subdev sd; + struct media_pad pad; + + struct clk *xclk; + + struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[ARRAY_SIZE(imx283_supply_name)]; + + /* V4L2 Controls */ + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vflip; + + unsigned long link_freq_bitmap; + + u16 hmax; + u32 vmax; +}; + +static inline struct imx283 *to_imx283(struct v4l2_subdev *sd) +{ + return container_of_const(sd, struct imx283, sd); +} + +static inline void get_mode_table(unsigned int code, + const struct imx283_mode **mode_list, + unsigned int *num_modes) +{ + switch (code) { + case MEDIA_BUS_FMT_SRGGB12_1X12: + case MEDIA_BUS_FMT_SGRBG12_1X12: + case MEDIA_BUS_FMT_SGBRG12_1X12: + case MEDIA_BUS_FMT_SBGGR12_1X12: + *mode_list = supported_modes_12bit; + *num_modes = ARRAY_SIZE(supported_modes_12bit); + break; + + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SBGGR10_1X10: + *mode_list = supported_modes_10bit; + *num_modes = ARRAY_SIZE(supported_modes_10bit); + break; + default: + *mode_list = NULL; + *num_modes = 0; + break; + } +} + +/* Calculate the Pixel Rate based on the current mode */ +static u64 imx283_pixel_rate(struct imx283 *imx283, + const struct imx283_mode *mode) +{ + u64 link_frequency = link_frequencies[__ffs(imx283->link_freq_bitmap)]; + unsigned int bpp = mode->bpp; + const unsigned int ddr = 2; /* Double Data Rate */ + const unsigned int lanes = 4; /* Only 4 lane support */ + u64 numerator = link_frequency * ddr * lanes; + + do_div(numerator, bpp); + + return numerator; +} + +/* Convert from a variable pixel_rate to 72 MHz clock cycles */ +static u64 imx283_internal_clock(unsigned int pixel_rate, unsigned int pixels) +{ + /* + * Determine the following operation without overflow: + * pixels = 72 Mhz / pixel_rate + * + * The internal clock at 72MHz and Pixel Rate (between 240 and 576MHz) + * can easily overflow this calculation, so pre-divide to simplify. + */ + const u32 iclk_pre = 72; + const u32 pclk_pre = pixel_rate / HZ_PER_MHZ; + u64 numerator = pixels * iclk_pre; + + do_div(numerator, pclk_pre); + + return numerator; +} + +/* Internal clock (72MHz) to Pixel Rate clock (Variable) */ +static u64 imx283_iclk_to_pix(unsigned int pixel_rate, unsigned int cycles) +{ + /* + * Determine the following operation without overflow: + * cycles * pixel_rate / 72 MHz + * + * The internal clock at 72MHz and Pixel Rate (between 240 and 576MHz) + * can easily overflow this calculation, so pre-divide to simplify. + */ + const u32 iclk_pre = 72; + const u32 pclk_pre = pixel_rate / HZ_PER_MHZ; + u64 numerator = cycles * pclk_pre; + + do_div(numerator, iclk_pre); + + return numerator; +} + +/* Determine the exposure based on current hmax, vmax and a given SHR */ +static u32 imx283_exposure(struct imx283 *imx283, + const struct imx283_mode *mode, u64 shr) +{ + u32 svr = 0; /* SVR feature is not currently supported */ + u32 offset; + u64 numerator; + + /* Number of clocks per internal offset period */ + offset = mode->mode == IMX283_MODE_0 ? 209 : 157; + numerator = (imx283->vmax * (svr + 1) - shr) * imx283->hmax + offset; + + do_div(numerator, imx283->hmax); + + return clamp(numerator, 0, U32_MAX); +} + +static void imx283_exposure_limits(struct imx283 *imx283, + const struct imx283_mode *mode, + s64 *min_exposure, s64 *max_exposure) +{ + u32 svr = 0; /* SVR feature is not currently supported */ + u64 min_shr = mode->min_shr; + /* Global Shutter is not supported */ + u64 max_shr = (svr + 1) * imx283->vmax - 4; + + max_shr = min(max_shr, BIT(16) - 1); + + *min_exposure = imx283_exposure(imx283, mode, max_shr); + *max_exposure = imx283_exposure(imx283, mode, min_shr); +} + +/* + * Integration Time [s] = [ {VMAX x (SVR + 1) – (SHR)} x HMAX + offset ] + * / [ 72 x 10^6 ] + */ +static u32 imx283_shr(struct imx283 *imx283, const struct imx283_mode *mode, + u32 exposure) +{ + u32 svr = 0; /* SVR feature is not currently supported */ + u32 offset; + u64 temp; + + /* Number of clocks per internal offset period */ + offset = mode->mode == IMX283_MODE_0 ? 209 : 157; + temp = ((u64)exposure * imx283->hmax - offset); + do_div(temp, imx283->hmax); + + return (imx283->vmax * (svr + 1) - temp); +} + +static const char * const imx283_tpg_menu[] = { + "Disabled", + "All 000h", + "All FFFh", + "All 555h", + "All AAAh", + "Horizontal color bars", + "Vertical color bars", +}; + +static const int imx283_tpg_val[] = { + IMX283_TPG_PAT_ALL_000, + IMX283_TPG_PAT_ALL_000, + IMX283_TPG_PAT_ALL_FFF, + IMX283_TPG_PAT_ALL_555, + IMX283_TPG_PAT_ALL_AAA, + IMX283_TPG_PAT_H_COLOR_BARS, + IMX283_TPG_PAT_V_COLOR_BARS, +}; + +static int imx283_update_test_pattern(struct imx283 *imx283, u32 pattern_index) +{ + int ret; + + if (pattern_index >= ARRAY_SIZE(imx283_tpg_val)) + return -EINVAL; + + if (!pattern_index) + return cci_write(imx283->cci, IMX283_REG_TPG_CTRL, 0x00, NULL); + + ret = cci_write(imx283->cci, IMX283_REG_TPG_PAT, + imx283_tpg_val[pattern_index], NULL); + if (ret) + return ret; + + return cci_write(imx283->cci, IMX283_REG_TPG_CTRL, + IMX283_TPG_CTRL_CLKEN | IMX283_TPG_CTRL_PATEN, NULL); +} + +static int imx283_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct imx283 *imx283 = container_of(ctrl->handler, struct imx283, + ctrl_handler); + const struct imx283_mode *mode; + struct v4l2_mbus_framefmt *fmt; + const struct imx283_mode *mode_list; + struct v4l2_subdev_state *state; + unsigned int num_modes; + u64 shr, pixel_rate; + int ret = 0; + + state = v4l2_subdev_get_locked_active_state(&imx283->sd); + fmt = v4l2_subdev_state_get_format(state, 0); + + get_mode_table(fmt->code, &mode_list, &num_modes); + mode = v4l2_find_nearest_size(mode_list, num_modes, width, height, + fmt->width, fmt->height); + + /* + * The VBLANK control may change the limits of usable exposure, so check + * and adjust if necessary. + */ + if (ctrl->id == V4L2_CID_VBLANK) { + /* Honour the VBLANK limits when setting exposure. */ + s64 current_exposure, max_exposure, min_exposure; + + imx283->vmax = mode->height + ctrl->val; + + imx283_exposure_limits(imx283, mode, + &min_exposure, &max_exposure); + + current_exposure = imx283->exposure->val; + current_exposure = clamp(current_exposure, min_exposure, + max_exposure); + + __v4l2_ctrl_modify_range(imx283->exposure, min_exposure, + max_exposure, 1, current_exposure); + } + + /* + * Applying V4L2 control value only happens + * when power is up for streaming + */ + if (!pm_runtime_get_if_active(imx283->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + shr = imx283_shr(imx283, mode, ctrl->val); + dev_dbg(imx283->dev, "V4L2_CID_EXPOSURE : %d - SHR: %lld\n", + ctrl->val, shr); + ret = cci_write(imx283->cci, IMX283_REG_SHR, shr, NULL); + break; + + case V4L2_CID_HBLANK: + pixel_rate = imx283_pixel_rate(imx283, mode); + imx283->hmax = imx283_internal_clock(pixel_rate, mode->width + ctrl->val); + dev_dbg(imx283->dev, "V4L2_CID_HBLANK : %d HMAX : %u\n", + ctrl->val, imx283->hmax); + ret = cci_write(imx283->cci, IMX283_REG_HMAX, imx283->hmax, NULL); + break; + + case V4L2_CID_VBLANK: + imx283->vmax = mode->height + ctrl->val; + dev_dbg(imx283->dev, "V4L2_CID_VBLANK : %d VMAX : %u\n", + ctrl->val, imx283->vmax); + ret = cci_write(imx283->cci, IMX283_REG_VMAX, imx283->vmax, NULL); + break; + + case V4L2_CID_ANALOGUE_GAIN: + ret = cci_write(imx283->cci, IMX283_REG_ANALOG_GAIN, ctrl->val, NULL); + break; + + case V4L2_CID_DIGITAL_GAIN: + ret = cci_write(imx283->cci, IMX283_REG_DIGITAL_GAIN, ctrl->val, NULL); + break; + + case V4L2_CID_VFLIP: + /* + * VFLIP is managed by BIT(0) of IMX283_REG_HTRIMMING address, hence + * both need to be set simultaneously. + */ + if (ctrl->val) { + cci_write(imx283->cci, IMX283_REG_HTRIMMING, + IMX283_HTRIMMING_EN | IMX283_MDVREV, &ret); + } else { + cci_write(imx283->cci, IMX283_REG_HTRIMMING, + IMX283_HTRIMMING_EN, &ret); + } + break; + + case V4L2_CID_TEST_PATTERN: + ret = imx283_update_test_pattern(imx283, ctrl->val); + break; + + default: + dev_err(imx283->dev, "ctrl(id:0x%x, val:0x%x) is not handled\n", + ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(imx283->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops imx283_ctrl_ops = { + .s_ctrl = imx283_set_ctrl, +}; + +static int imx283_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(imx283_mbus_codes)) + return -EINVAL; + + code->code = imx283_mbus_codes[code->index]; + + return 0; +} + +static int imx283_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + const struct imx283_mode *mode_list; + unsigned int num_modes; + + get_mode_table(fse->code, &mode_list, &num_modes); + + if (fse->index >= num_modes) + return -EINVAL; + + fse->min_width = mode_list[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = mode_list[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static void imx283_update_image_pad_format(struct imx283 *imx283, + const struct imx283_mode *mode, + struct v4l2_mbus_framefmt *format) +{ + format->width = mode->width; + format->height = mode->height; + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_RAW; + format->ycbcr_enc = V4L2_YCBCR_ENC_601; + format->quantization = V4L2_QUANTIZATION_FULL_RANGE; + format->xfer_func = V4L2_XFER_FUNC_NONE; +} + +static int imx283_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct imx283 *imx283 = to_imx283(sd); + struct v4l2_mbus_framefmt *format; + const struct imx283_mode *mode; + struct v4l2_rect *crop; + + /* Initialize try_fmt */ + format = v4l2_subdev_state_get_format(state, IMAGE_PAD); + + mode = &supported_modes_12bit[0]; + format->code = MEDIA_BUS_FMT_SRGGB12_1X12; + imx283_update_image_pad_format(imx283, mode, format); + + /* Initialize crop rectangle to mode default */ + crop = v4l2_subdev_state_get_crop(state, IMAGE_PAD); + *crop = mode->crop; + + return 0; +} + +static void imx283_set_framing_limits(struct imx283 *imx283, + const struct imx283_mode *mode) +{ + u64 pixel_rate = imx283_pixel_rate(imx283, mode); + u64 min_hblank, max_hblank, def_hblank; + + /* Initialise hmax and vmax for exposure calculations */ + imx283->hmax = imx283_internal_clock(pixel_rate, mode->default_hmax); + imx283->vmax = mode->default_vmax; + + /* + * Horizontal Blanking + * Convert the HMAX_MAX (72MHz) to Pixel rate values for HBLANK_MAX + */ + min_hblank = mode->min_hmax - mode->width; + max_hblank = imx283_iclk_to_pix(pixel_rate, IMX283_HMAX_MAX) - mode->width; + def_hblank = mode->default_hmax - mode->width; + __v4l2_ctrl_modify_range(imx283->hblank, min_hblank, max_hblank, 1, + def_hblank); + __v4l2_ctrl_s_ctrl(imx283->hblank, def_hblank); + + /* Vertical Blanking */ + __v4l2_ctrl_modify_range(imx283->vblank, mode->min_vmax - mode->height, + IMX283_VMAX_MAX - mode->height, 1, + mode->default_vmax - mode->height); + __v4l2_ctrl_s_ctrl(imx283->vblank, mode->default_vmax - mode->height); +} + +static int imx283_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *format; + const struct imx283_mode *mode; + struct imx283 *imx283 = to_imx283(sd); + const struct imx283_mode *mode_list; + unsigned int num_modes; + + get_mode_table(fmt->format.code, &mode_list, &num_modes); + + mode = v4l2_find_nearest_size(mode_list, num_modes, width, height, + fmt->format.width, fmt->format.height); + + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + fmt->format.colorspace = V4L2_COLORSPACE_RAW; + fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_601; + fmt->format.quantization = V4L2_QUANTIZATION_FULL_RANGE; + fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; + + format = v4l2_subdev_state_get_format(sd_state, 0); + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + imx283_set_framing_limits(imx283, mode); + + *format = fmt->format; + + return 0; +} + +static int imx283_standby_cancel(struct imx283 *imx283) +{ + unsigned int link_freq_idx; + int ret = 0; + + cci_write(imx283->cci, IMX283_REG_STANDBY, + IMX283_STBLOGIC | IMX283_STBDV, &ret); + + /* Configure PLL clocks based on the xclk */ + cci_multi_reg_write(imx283->cci, imx283->freq->regs, + imx283->freq->reg_count, &ret); + + dev_dbg(imx283->dev, "Using clk freq %ld MHz", + imx283->freq->mhz / HZ_PER_MHZ); + + /* Initialise communication */ + cci_write(imx283->cci, IMX283_REG_PLSTMG08, IMX283_PLSTMG08_VAL, &ret); + cci_write(imx283->cci, IMX283_REG_PLSTMG02, IMX283_PLSTMG02_VAL, &ret); + + /* Enable PLL */ + cci_write(imx283->cci, IMX283_REG_STBPL, IMX283_STBPL_NORMAL, &ret); + + /* Configure the MIPI link speed */ + link_freq_idx = __ffs(imx283->link_freq_bitmap); + cci_multi_reg_write(imx283->cci, link_freq_reglist[link_freq_idx].regs, + link_freq_reglist[link_freq_idx].num_of_regs, + &ret); + + /* 1st Stabilisation period of 1 ms or more */ + usleep_range(1000, 2000); + + /* Activate */ + cci_write(imx283->cci, IMX283_REG_STANDBY, IMX283_ACTIVE, &ret); + + /* 2nd Stabilisation period of 19ms or more */ + usleep_range(19000, 20000); + + cci_write(imx283->cci, IMX283_REG_CLAMP, IMX283_CLPSQRST, &ret); + cci_write(imx283->cci, IMX283_REG_XMSTA, 0, &ret); + cci_write(imx283->cci, IMX283_REG_SYNCDRV, IMX283_SYNCDRV_XHS_XVS, &ret); + + return ret; +} + +/* Start streaming */ +static int imx283_start_streaming(struct imx283 *imx283, + struct v4l2_subdev_state *state) +{ + const struct imx283_readout_mode *readout; + const struct imx283_mode *mode; + const struct v4l2_mbus_framefmt *fmt; + const struct imx283_mode *mode_list; + unsigned int num_modes; + u32 v_widcut; + s32 v_pos; + u32 write_v_size; + u32 y_out_size; + int ret = 0; + + fmt = v4l2_subdev_state_get_format(state, 0); + get_mode_table(fmt->code, &mode_list, &num_modes); + mode = v4l2_find_nearest_size(mode_list, num_modes, width, height, + fmt->width, fmt->height); + + ret = imx283_standby_cancel(imx283); + if (ret) { + dev_err(imx283->dev, "failed to cancel standby\n"); + return ret; + } + + /* + * Set the readout mode registers. + * MDSEL3 and MDSEL4 are updated to enable Arbitrary Vertical Cropping. + */ + readout = &imx283_readout_modes[mode->mode]; + cci_write(imx283->cci, IMX283_REG_MDSEL1, readout->mdsel1, &ret); + cci_write(imx283->cci, IMX283_REG_MDSEL2, readout->mdsel2, &ret); + cci_write(imx283->cci, IMX283_REG_MDSEL3, + readout->mdsel3 | IMX283_MDSEL3_VCROP_EN, &ret); + cci_write(imx283->cci, IMX283_REG_MDSEL4, + readout->mdsel4 | IMX283_MDSEL4_VCROP_EN, &ret); + + /* Mode 1S specific entries from the Readout Drive Mode Tables */ + if (mode->mode == IMX283_MODE_1S) { + cci_write(imx283->cci, IMX283_REG_MDSEL7, 0x01, &ret); + cci_write(imx283->cci, IMX283_REG_MDSEL18, 0x1098, &ret); + } + + if (ret) { + dev_err(imx283->dev, "failed to set readout\n"); + return ret; + } + + /* Initialise SVR. Unsupported for now - Always 0 */ + cci_write(imx283->cci, IMX283_REG_SVR, 0x00, &ret); + + dev_dbg(imx283->dev, "Mode: Size %d x %d\n", mode->width, mode->height); + dev_dbg(imx283->dev, "Analogue Crop (in the mode) %d,%d %dx%d\n", + mode->crop.left, + mode->crop.top, + mode->crop.width, + mode->crop.height); + + y_out_size = mode->crop.height / mode->vbin_ratio; + write_v_size = y_out_size + mode->vertical_ob; + /* + * cropping start position = (VWINPOS – Vst) × 2 + * cropping width = Veff – (VWIDCUT – Vct) × 2 + */ + v_pos = imx283->vflip->val ? + ((-mode->crop.top / mode->vbin_ratio) / 2) + mode->vst : + ((mode->crop.top / mode->vbin_ratio) / 2) + mode->vst; + v_widcut = ((mode->veff - y_out_size) / 2) + mode->vct; + + cci_write(imx283->cci, IMX283_REG_Y_OUT_SIZE, y_out_size, &ret); + cci_write(imx283->cci, IMX283_REG_WRITE_VSIZE, write_v_size, &ret); + cci_write(imx283->cci, IMX283_REG_VWIDCUT, v_widcut, &ret); + cci_write(imx283->cci, IMX283_REG_VWINPOS, v_pos, &ret); + + cci_write(imx283->cci, IMX283_REG_OB_SIZE_V, mode->vertical_ob, &ret); + + /* TODO: Validate mode->crop is fully contained within imx283_native_area */ + cci_write(imx283->cci, IMX283_REG_HTRIMMING_START, mode->crop.left, &ret); + cci_write(imx283->cci, IMX283_REG_HTRIMMING_END, + mode->crop.left + mode->crop.width, &ret); + + /* Disable embedded data */ + cci_write(imx283->cci, IMX283_REG_EBD_X_OUT_SIZE, 0, &ret); + + /* Apply customized values from controls (HMAX/VMAX/SHR) */ + ret = __v4l2_ctrl_handler_setup(imx283->sd.ctrl_handler); + + return ret; +} + +static int imx283_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct imx283 *imx283 = to_imx283(sd); + int ret; + + if (pad != IMAGE_PAD) + return -EINVAL; + + ret = pm_runtime_get_sync(imx283->dev); + if (ret < 0) { + pm_runtime_put_noidle(imx283->dev); + return ret; + } + + ret = imx283_start_streaming(imx283, state); + if (ret) + goto err_rpm_put; + + return 0; + +err_rpm_put: + pm_runtime_mark_last_busy(imx283->dev); + pm_runtime_put_autosuspend(imx283->dev); + + return ret; +} + +static int imx283_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct imx283 *imx283 = to_imx283(sd); + int ret; + + if (pad != IMAGE_PAD) + return -EINVAL; + + ret = cci_write(imx283->cci, IMX283_REG_STANDBY, IMX283_STBLOGIC, NULL); + if (ret) + dev_err(imx283->dev, "Failed to stop stream\n"); + + pm_runtime_mark_last_busy(imx283->dev); + pm_runtime_put_autosuspend(imx283->dev); + + return ret; +} + +/* Power/clock management functions */ +static int imx283_power_on(struct imx283 *imx283) +{ + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(imx283_supply_name), + imx283->supplies); + if (ret) { + dev_err(imx283->dev, "failed to enable regulators\n"); + return ret; + } + + ret = clk_prepare_enable(imx283->xclk); + if (ret) { + dev_err(imx283->dev, "failed to enable clock\n"); + goto reg_off; + } + + gpiod_set_value_cansleep(imx283->reset_gpio, 0); + + usleep_range(IMX283_XCLR_MIN_DELAY_US, + IMX283_XCLR_MIN_DELAY_US + IMX283_XCLR_DELAY_RANGE_US); + + return 0; + +reg_off: + regulator_bulk_disable(ARRAY_SIZE(imx283_supply_name), imx283->supplies); + return ret; +} + +static int imx283_power_off(struct imx283 *imx283) +{ + gpiod_set_value_cansleep(imx283->reset_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(imx283_supply_name), imx283->supplies); + clk_disable_unprepare(imx283->xclk); + + return 0; +} + +static int imx283_runtime_resume(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct imx283 *imx283 = to_imx283(sd); + + return imx283_power_on(imx283); +} + +static int imx283_runtime_suspend(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct imx283 *imx283 = to_imx283(sd); + + imx283_power_off(imx283); + + return 0; +} + +static int imx283_get_regulators(struct imx283 *imx283) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(imx283_supply_name); i++) + imx283->supplies[i].supply = imx283_supply_name[i]; + + return devm_regulator_bulk_get(imx283->dev, + ARRAY_SIZE(imx283_supply_name), + imx283->supplies); +} + +/* Verify chip ID */ +static int imx283_identify_module(struct imx283 *imx283) +{ + int ret; + u64 val; + + ret = cci_read(imx283->cci, IMX283_REG_CHIP_ID, &val, NULL); + if (ret) { + dev_err(imx283->dev, "failed to read chip id %x, with error %d\n", + IMX283_CHIP_ID, ret); + return ret; + } + + if (val != IMX283_CHIP_ID) { + dev_err(imx283->dev, "chip id mismatch: %x!=%llx\n", + IMX283_CHIP_ID, val); + return -EIO; + } + + return 0; +} + +static int imx283_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: { + sel->r = *v4l2_subdev_state_get_crop(sd_state, 0); + return 0; + } + + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r = imx283_native_area; + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r = imx283_active_area; + return 0; + default: + return -EINVAL; + } +} + +static const struct v4l2_subdev_core_ops imx283_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_video_ops imx283_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_pad_ops imx283_pad_ops = { + .enum_mbus_code = imx283_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = imx283_set_pad_format, + .get_selection = imx283_get_selection, + .enum_frame_size = imx283_enum_frame_size, + .enable_streams = imx283_enable_streams, + .disable_streams = imx283_disable_streams, +}; + +static const struct v4l2_subdev_internal_ops imx283_internal_ops = { + .init_state = imx283_init_state, +}; + +static const struct v4l2_subdev_ops imx283_subdev_ops = { + .core = &imx283_core_ops, + .video = &imx283_video_ops, + .pad = &imx283_pad_ops, +}; + +/* Initialize control handlers */ +static int imx283_init_controls(struct imx283 *imx283) +{ + struct v4l2_ctrl_handler *ctrl_hdlr; + struct v4l2_fwnode_device_properties props; + struct v4l2_ctrl *link_freq; + const struct imx283_mode *mode = &supported_modes_12bit[0]; + u64 min_hblank, max_hblank, def_hblank; + u64 pixel_rate; + int ret; + + ctrl_hdlr = &imx283->ctrl_handler; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 16); + if (ret) + return ret; + + /* + * Create the controls here, but mode specific limits are setup + * in the imx283_set_framing_limits() call below. + */ + + /* By default, PIXEL_RATE is read only */ + pixel_rate = imx283_pixel_rate(imx283, mode); + v4l2_ctrl_new_std(ctrl_hdlr, &imx283_ctrl_ops, + V4L2_CID_PIXEL_RATE, pixel_rate, + pixel_rate, 1, pixel_rate); + + link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx283_ctrl_ops, + V4L2_CID_LINK_FREQ, + __fls(imx283->link_freq_bitmap), + __ffs(imx283->link_freq_bitmap), + link_frequencies); + if (link_freq) + link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + /* Initialise vblank/hblank/exposure based on the current mode. */ + imx283->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx283_ctrl_ops, + V4L2_CID_VBLANK, + mode->min_vmax - mode->height, + IMX283_VMAX_MAX, 1, + mode->default_vmax - mode->height); + + min_hblank = mode->min_hmax - mode->width; + max_hblank = imx283_iclk_to_pix(pixel_rate, IMX283_HMAX_MAX) - mode->width; + def_hblank = mode->default_hmax - mode->width; + imx283->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx283_ctrl_ops, + V4L2_CID_HBLANK, min_hblank, max_hblank, + 1, def_hblank); + + imx283->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx283_ctrl_ops, + V4L2_CID_EXPOSURE, + IMX283_EXPOSURE_MIN, + IMX283_EXPOSURE_MAX, + IMX283_EXPOSURE_STEP, + IMX283_EXPOSURE_DEFAULT); + + v4l2_ctrl_new_std(ctrl_hdlr, &imx283_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + IMX283_ANA_GAIN_MIN, IMX283_ANA_GAIN_MAX, + IMX283_ANA_GAIN_STEP, IMX283_ANA_GAIN_DEFAULT); + + v4l2_ctrl_new_std(ctrl_hdlr, &imx283_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + IMX283_DGTL_GAIN_MIN, IMX283_DGTL_GAIN_MAX, + IMX283_DGTL_GAIN_STEP, IMX283_DGTL_GAIN_DEFAULT); + + imx283->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx283_ctrl_ops, V4L2_CID_VFLIP, + 0, 1, 1, 0); + if (imx283->vflip) + imx283->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx283_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(imx283_tpg_menu) - 1, + 0, 0, imx283_tpg_menu); + + if (ctrl_hdlr->error) { + ret = ctrl_hdlr->error; + dev_err(imx283->dev, "control init failed (%d)\n", ret); + goto error; + } + + ret = v4l2_fwnode_device_parse(imx283->dev, &props); + if (ret) + goto error; + + ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx283_ctrl_ops, + &props); + if (ret) + goto error; + + imx283->sd.ctrl_handler = ctrl_hdlr; + + mutex_lock(imx283->ctrl_handler.lock); + + /* Setup exposure and frame/line length limits. */ + imx283_set_framing_limits(imx283, mode); + + mutex_unlock(imx283->ctrl_handler.lock); + + return 0; + +error: + v4l2_ctrl_handler_free(ctrl_hdlr); + + return ret; +} + +static int imx283_parse_endpoint(struct imx283 *imx283) +{ + struct fwnode_handle *fwnode; + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + struct fwnode_handle *ep; + int ret; + + fwnode = dev_fwnode(imx283->dev); + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) { + dev_err(imx283->dev, "Failed to get next endpoint\n"); + return -ENXIO; + } + + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) + return ret; + + if (bus_cfg.bus.mipi_csi2.num_data_lanes != 4) { + dev_err(imx283->dev, + "number of CSI2 data lanes %d is not supported\n", + bus_cfg.bus.mipi_csi2.num_data_lanes); + ret = -EINVAL; + goto done_endpoint_free; + } + + ret = v4l2_link_freq_to_bitmap(imx283->dev, bus_cfg.link_frequencies, + bus_cfg.nr_of_link_frequencies, + link_frequencies, ARRAY_SIZE(link_frequencies), + &imx283->link_freq_bitmap); + +done_endpoint_free: + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +}; + +static int imx283_probe(struct i2c_client *client) +{ + struct imx283 *imx283; + unsigned int i; + unsigned int xclk_freq; + int ret; + + imx283 = devm_kzalloc(&client->dev, sizeof(*imx283), GFP_KERNEL); + if (!imx283) + return -ENOMEM; + + imx283->dev = &client->dev; + + v4l2_i2c_subdev_init(&imx283->sd, client, &imx283_subdev_ops); + + imx283->cci = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(imx283->cci)) { + ret = PTR_ERR(imx283->cci); + dev_err(imx283->dev, "failed to initialize CCI: %d\n", ret); + return ret; + } + + /* Get system clock (xclk) */ + imx283->xclk = devm_clk_get(imx283->dev, NULL); + if (IS_ERR(imx283->xclk)) { + return dev_err_probe(imx283->dev, PTR_ERR(imx283->xclk), + "failed to get xclk\n"); + } + + xclk_freq = clk_get_rate(imx283->xclk); + for (i = 0; i < ARRAY_SIZE(imx283_frequencies); i++) { + if (xclk_freq == imx283_frequencies[i].mhz) { + imx283->freq = &imx283_frequencies[i]; + break; + } + } + if (!imx283->freq) { + dev_err(imx283->dev, "xclk frequency unsupported: %d Hz\n", xclk_freq); + return -EINVAL; + } + + ret = imx283_get_regulators(imx283); + if (ret) { + return dev_err_probe(imx283->dev, ret, + "failed to get regulators\n"); + } + + ret = imx283_parse_endpoint(imx283); + if (ret) { + dev_err(imx283->dev, "failed to parse endpoint configuration\n"); + return ret; + } + + /* Request optional enable pin */ + imx283->reset_gpio = devm_gpiod_get_optional(imx283->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(imx283->reset_gpio)) + return dev_err_probe(imx283->dev, PTR_ERR(imx283->reset_gpio), + "failed to get reset GPIO\n"); + + /* + * The sensor must be powered for imx283_identify_module() + * to be able to read the CHIP_ID register + */ + ret = imx283_power_on(imx283); + if (ret) + return ret; + + ret = imx283_identify_module(imx283); + if (ret) + goto error_power_off; + + /* + * Enable runtime PM with autosuspend. As the device has been powered + * manually, mark it as active, and increase the usage count without + * resuming the device. + */ + pm_runtime_set_active(imx283->dev); + pm_runtime_get_noresume(imx283->dev); + pm_runtime_enable(imx283->dev); + pm_runtime_set_autosuspend_delay(imx283->dev, 1000); + pm_runtime_use_autosuspend(imx283->dev); + + /* This needs the pm runtime to be registered. */ + ret = imx283_init_controls(imx283); + if (ret) + goto error_pm; + + /* Initialize subdev */ + imx283->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; + imx283->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + imx283->sd.internal_ops = &imx283_internal_ops; + + /* Initialize source pads */ + imx283->pad.flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&imx283->sd.entity, 1, &imx283->pad); + if (ret) { + dev_err(imx283->dev, "failed to init entity pads: %d\n", ret); + goto error_handler_free; + } + + imx283->sd.state_lock = imx283->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&imx283->sd); + if (ret < 0) { + dev_err(imx283->dev, "subdev init error: %d\n", ret); + goto error_media_entity; + } + + ret = v4l2_async_register_subdev_sensor(&imx283->sd); + if (ret < 0) { + dev_err(imx283->dev, "failed to register sensor sub-device: %d\n", ret); + goto error_subdev_cleanup; + } + + /* + * Decrease the PM usage count. The device will get suspended after the + * autosuspend delay, turning the power off. + */ + pm_runtime_mark_last_busy(imx283->dev); + pm_runtime_put_autosuspend(imx283->dev); + + return 0; + +error_subdev_cleanup: + v4l2_subdev_cleanup(&imx283->sd); + +error_media_entity: + media_entity_cleanup(&imx283->sd.entity); + +error_handler_free: + v4l2_ctrl_handler_free(imx283->sd.ctrl_handler); + +error_pm: + pm_runtime_disable(imx283->dev); + pm_runtime_set_suspended(imx283->dev); +error_power_off: + imx283_power_off(imx283); + + return ret; +} + +static void imx283_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx283 *imx283 = to_imx283(sd); + + v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(&imx283->sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(imx283->sd.ctrl_handler); + + pm_runtime_disable(imx283->dev); + if (!pm_runtime_status_suspended(imx283->dev)) + imx283_power_off(imx283); + pm_runtime_set_suspended(imx283->dev); +} + +static DEFINE_RUNTIME_DEV_PM_OPS(imx283_pm_ops, imx283_runtime_suspend, + imx283_runtime_resume, NULL); + +static const struct of_device_id imx283_dt_ids[] = { + { .compatible = "sony,imx283" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx283_dt_ids); + +static struct i2c_driver imx283_i2c_driver = { + .driver = { + .name = "imx283", + .pm = pm_ptr(&imx283_pm_ops), + .of_match_table = imx283_dt_ids, + }, + .probe = imx283_probe, + .remove = imx283_remove, +}; +module_i2c_driver(imx283_i2c_driver); + +MODULE_AUTHOR("Will Whang <will@willwhang.com>"); +MODULE_AUTHOR("Kieran Bingham <kieran.bingham@ideasonboard.com>"); +MODULE_AUTHOR("Umang Jain <umang.jain@ideasonboard.com>"); +MODULE_DESCRIPTION("Sony IMX283 Sensor Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/imx412.c b/drivers/media/i2c/imx412.c index 0efce329525e..7d1f7af0a9df 100644 --- a/drivers/media/i2c/imx412.c +++ b/drivers/media/i2c/imx412.c @@ -542,14 +542,13 @@ static int imx412_update_controls(struct imx412 *imx412, */ static int imx412_update_exp_gain(struct imx412 *imx412, u32 exposure, u32 gain) { - u32 lpfr, shutter; + u32 lpfr; int ret; lpfr = imx412->vblank + imx412->cur_mode->height; - shutter = lpfr - exposure; - dev_dbg(imx412->dev, "Set exp %u, analog gain %u, shutter %u, lpfr %u", - exposure, gain, shutter, lpfr); + dev_dbg(imx412->dev, "Set exp %u, analog gain %u, lpfr %u", + exposure, gain, lpfr); ret = imx412_write_reg(imx412, IMX412_REG_HOLD, 1, 1); if (ret) @@ -559,7 +558,7 @@ static int imx412_update_exp_gain(struct imx412 *imx412, u32 exposure, u32 gain) if (ret) goto error_release_group_hold; - ret = imx412_write_reg(imx412, IMX412_REG_EXPOSURE_CIT, 2, shutter); + ret = imx412_write_reg(imx412, IMX412_REG_EXPOSURE_CIT, 2, exposure); if (ret) goto error_release_group_hold; diff --git a/drivers/media/i2c/ks0127.c b/drivers/media/i2c/ks0127.c index 5c583f57e3f3..9d0a763cd503 100644 --- a/drivers/media/i2c/ks0127.c +++ b/drivers/media/i2c/ks0127.c @@ -175,14 +175,6 @@ MODULE_LICENSE("GPL"); * mga_dev : represents one ks0127 chip. ****************************************************************************/ -struct adjust { - int contrast; - int bright; - int hue; - int ugain; - int vgain; -}; - struct ks0127 { struct v4l2_subdev sd; v4l2_std_id norm; diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c index dfcb3fc03350..9fc4e130a273 100644 --- a/drivers/media/i2c/max9286.c +++ b/drivers/media/i2c/max9286.c @@ -19,7 +19,6 @@ #include <linux/i2c.h> #include <linux/i2c-mux.h> #include <linux/module.h> -#include <linux/mutex.h> #include <linux/of_graph.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> @@ -198,12 +197,6 @@ struct max9286_priv { struct v4l2_ctrl *pixelrate_ctrl; unsigned int pixelrate; - struct v4l2_mbus_framefmt fmt[MAX9286_N_SINKS]; - struct v4l2_fract interval; - - /* Protects controls and fmt structures */ - struct mutex mutex; - unsigned int nsources; unsigned int source_mask; unsigned int route_mask; @@ -576,11 +569,14 @@ static void max9286_set_video_format(struct max9286_priv *priv, MAX9286_INVVS | MAX9286_HVSRC_D14); } -static void max9286_set_fsync_period(struct max9286_priv *priv) +static void max9286_set_fsync_period(struct max9286_priv *priv, + struct v4l2_subdev_state *state) { + const struct v4l2_fract *interval; u32 fsync; - if (!priv->interval.numerator || !priv->interval.denominator) { + interval = v4l2_subdev_state_get_interval(state, MAX9286_SRC_PAD); + if (!interval->numerator || !interval->denominator) { /* * Special case, a null interval enables automatic FRAMESYNC * mode. FRAMESYNC is taken from the slowest link. @@ -596,8 +592,8 @@ static void max9286_set_fsync_period(struct max9286_priv *priv) * The FRAMESYNC generator is configured with a period expressed as a * number of PCLK periods. */ - fsync = div_u64((u64)priv->pixelrate * priv->interval.numerator, - priv->interval.denominator); + fsync = div_u64((u64)priv->pixelrate * interval->numerator, + interval->denominator); dev_dbg(&priv->client->dev, "fsync period %u (pclk %u)\n", fsync, priv->pixelrate); @@ -788,22 +784,25 @@ static void max9286_v4l2_notifier_unregister(struct max9286_priv *priv) static int max9286_s_stream(struct v4l2_subdev *sd, int enable) { struct max9286_priv *priv = sd_to_max9286(sd); + struct v4l2_subdev_state *state; struct max9286_source *source; unsigned int i; bool sync = false; - int ret; + int ret = 0; + + state = v4l2_subdev_lock_and_get_active_state(sd); if (enable) { const struct v4l2_mbus_framefmt *format; /* - * Get the format from the first used sink pad, as all sink - * formats must be identical. + * Get the format from the source pad, as all formats must be + * identical. */ - format = &priv->fmt[__ffs(priv->bound_sources)]; + format = v4l2_subdev_state_get_format(state, MAX9286_SRC_PAD); max9286_set_video_format(priv, format); - max9286_set_fsync_period(priv); + max9286_set_fsync_period(priv, state); /* * The frame sync between cameras is transmitted across the @@ -816,12 +815,12 @@ static int max9286_s_stream(struct v4l2_subdev *sd, int enable) for_each_source(priv, source) { ret = v4l2_subdev_call(source->sd, video, s_stream, 1); if (ret) - return ret; + goto unlock; } ret = max9286_check_video_links(priv); if (ret) - return ret; + goto unlock; /* * Wait until frame synchronization is locked. @@ -842,7 +841,8 @@ static int max9286_s_stream(struct v4l2_subdev *sd, int enable) if (!sync) { dev_err(&priv->client->dev, "Failed to get frame synchronization\n"); - return -EXDEV; /* Invalid cross-device link */ + ret = -EXDEV; /* Invalid cross-device link */ + goto unlock; } /* @@ -865,26 +865,21 @@ static int max9286_s_stream(struct v4l2_subdev *sd, int enable) max9286_i2c_mux_close(priv); } - return 0; +unlock: + v4l2_subdev_unlock_state(state); + + return ret; } static int max9286_get_frame_interval(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval *interval) { - struct max9286_priv *priv = sd_to_max9286(sd); - - /* - * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 - * subdev active state API. - */ - if (interval->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - if (interval->pad != MAX9286_SRC_PAD) return -EINVAL; - interval->interval = priv->interval; + interval->interval = *v4l2_subdev_state_get_interval(sd_state, + interval->pad); return 0; } @@ -893,19 +888,11 @@ static int max9286_set_frame_interval(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval *interval) { - struct max9286_priv *priv = sd_to_max9286(sd); - - /* - * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 - * subdev active state API. - */ - if (interval->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - if (interval->pad != MAX9286_SRC_PAD) return -EINVAL; - priv->interval = interval->interval; + *v4l2_subdev_state_get_interval(sd_state, + interval->pad) = interval->interval; return 0; } @@ -914,39 +901,28 @@ static int max9286_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { - if (code->pad || code->index > 0) + if (code->pad || code->index >= ARRAY_SIZE(max9286_formats)) return -EINVAL; - code->code = MEDIA_BUS_FMT_UYVY8_1X16; + code->code = max9286_formats[code->index].code; return 0; } -static struct v4l2_mbus_framefmt * -max9286_get_pad_format(struct max9286_priv *priv, - struct v4l2_subdev_state *sd_state, - unsigned int pad, u32 which) -{ - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_state_get_format(sd_state, pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &priv->fmt[pad]; - default: - return NULL; - } -} - static int max9286_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_state *state, struct v4l2_subdev_format *format) { struct max9286_priv *priv = sd_to_max9286(sd); - struct v4l2_mbus_framefmt *cfg_fmt; + struct max9286_source *source; unsigned int i; + /* + * Disable setting format on the source pad: format is propagated + * from the sinks. + */ if (format->pad == MAX9286_SRC_PAD) - return -EINVAL; + return v4l2_subdev_get_fmt(sd, state, format); /* Validate the format. */ for (i = 0; i < ARRAY_SIZE(max9286_formats); ++i) { @@ -957,42 +933,17 @@ static int max9286_set_fmt(struct v4l2_subdev *sd, if (i == ARRAY_SIZE(max9286_formats)) format->format.code = max9286_formats[0].code; - cfg_fmt = max9286_get_pad_format(priv, sd_state, format->pad, - format->which); - if (!cfg_fmt) - return -EINVAL; - - mutex_lock(&priv->mutex); - *cfg_fmt = format->format; - mutex_unlock(&priv->mutex); - - return 0; -} - -static int max9286_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) -{ - struct max9286_priv *priv = sd_to_max9286(sd); - struct v4l2_mbus_framefmt *cfg_fmt; - unsigned int pad = format->pad; - /* - * Multiplexed Stream Support: Support link validation by returning the - * format of the first bound link. All links must have the same format, - * as we do not support mixing and matching of cameras connected to the - * max9286. + * Apply the same format on all the other pad as all links must have the + * same format. */ - if (pad == MAX9286_SRC_PAD) - pad = __ffs(priv->bound_sources); + for_each_source(priv, source) { + unsigned int index = to_index(priv, source); - cfg_fmt = max9286_get_pad_format(priv, sd_state, pad, format->which); - if (!cfg_fmt) - return -EINVAL; + *v4l2_subdev_state_get_format(state, index) = format->format; + } - mutex_lock(&priv->mutex); - format->format = *cfg_fmt; - mutex_unlock(&priv->mutex); + *v4l2_subdev_state_get_format(state, MAX9286_SRC_PAD) = format->format; return 0; } @@ -1003,7 +954,7 @@ static const struct v4l2_subdev_video_ops max9286_video_ops = { static const struct v4l2_subdev_pad_ops max9286_pad_ops = { .enum_mbus_code = max9286_enum_mbus_code, - .get_fmt = max9286_get_fmt, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = max9286_set_fmt, .get_frame_interval = max9286_get_frame_interval, .set_frame_interval = max9286_set_frame_interval, @@ -1025,26 +976,29 @@ static const struct v4l2_mbus_framefmt max9286_default_format = { .xfer_func = V4L2_XFER_FUNC_DEFAULT, }; -static void max9286_init_format(struct v4l2_mbus_framefmt *fmt) +static int max9286_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { - *fmt = max9286_default_format; -} + struct v4l2_fract *interval; -static int max9286_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) -{ - struct v4l2_mbus_framefmt *format; - unsigned int i; + for (unsigned int i = 0; i < MAX9286_N_PADS; i++) + *v4l2_subdev_state_get_format(state, i) = max9286_default_format; - for (i = 0; i < MAX9286_N_SINKS; i++) { - format = v4l2_subdev_state_get_format(fh->state, i); - max9286_init_format(format); - } + /* + * Special case: a null interval enables automatic FRAMESYNC mode. + * + * FRAMESYNC is taken from the slowest link. See register 0x01 + * configuration. + */ + interval = v4l2_subdev_state_get_interval(state, MAX9286_SRC_PAD); + interval->numerator = 0; + interval->denominator = 0; return 0; } static const struct v4l2_subdev_internal_ops max9286_subdev_internal_ops = { - .open = max9286_open, + .init_state = max9286_init_state, }; static const struct media_entity_operations max9286_media_ops = { @@ -1079,10 +1033,6 @@ static int max9286_v4l2_register(struct max9286_priv *priv) } /* Configure V4L2 for the MAX9286 itself */ - - for (i = 0; i < MAX9286_N_SINKS; i++) - max9286_init_format(&priv->fmt[i]); - v4l2_i2c_subdev_init(&priv->sd, priv->client, &max9286_subdev_ops); priv->sd.internal_ops = &max9286_subdev_internal_ops; priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; @@ -1109,14 +1059,21 @@ static int max9286_v4l2_register(struct max9286_priv *priv) if (ret) goto err_async; + priv->sd.state_lock = priv->ctrls.lock; + ret = v4l2_subdev_init_finalize(&priv->sd); + if (ret) + goto err_async; + ret = v4l2_async_register_subdev(&priv->sd); if (ret < 0) { dev_err(dev, "Unable to register subdevice\n"); - goto err_async; + goto err_subdev; } return 0; +err_subdev: + v4l2_subdev_cleanup(&priv->sd); err_async: v4l2_ctrl_handler_free(&priv->ctrls); max9286_v4l2_notifier_unregister(priv); @@ -1126,6 +1083,7 @@ err_async: static void max9286_v4l2_unregister(struct max9286_priv *priv) { + v4l2_subdev_cleanup(&priv->sd); v4l2_ctrl_handler_free(&priv->ctrls); v4l2_async_unregister_subdev(&priv->sd); max9286_v4l2_notifier_unregister(priv); @@ -1182,7 +1140,6 @@ static int max9286_setup(struct max9286_priv *priv) max9286_write(priv, 0x69, (0xf & ~priv->route_mask)); max9286_set_video_format(priv, &max9286_default_format); - max9286_set_fsync_period(priv); cfg = max9286_read(priv, 0x1c); if (cfg < 0) @@ -1629,8 +1586,6 @@ static int max9286_probe(struct i2c_client *client) if (!priv) return -ENOMEM; - mutex_init(&priv->mutex); - priv->client = client; /* GPIO values default to high */ diff --git a/drivers/media/i2c/max96714.c b/drivers/media/i2c/max96714.c new file mode 100644 index 000000000000..c97de66631e0 --- /dev/null +++ b/drivers/media/i2c/max96714.c @@ -0,0 +1,1024 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Maxim GMSL2 Deserializer Driver + * + * Copyright (C) 2024 Collabora Ltd. + */ + +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/fwnode.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/i2c-mux.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> + +#include <media/v4l2-cci.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> + +#define MAX96714_DEVICE_ID 0xc9 +#define MAX96714F_DEVICE_ID 0xca +#define MAX96714_NPORTS 2 +#define MAX96714_PAD_SINK 0 +#define MAX96714_PAD_SOURCE 1 + +/* DEV */ +#define MAX96714_REG13 CCI_REG8(0x0d) +#define MAX96714_DEV_REV CCI_REG8(0x0e) +#define MAX96714_DEV_REV_MASK GENMASK(3, 0) +#define MAX96714_LINK_LOCK CCI_REG8(0x13) +#define MAX96714_LINK_LOCK_BIT BIT(3) +#define MAX96714_IO_CHK0 CCI_REG8(0x38) +#define MAX96714_PATTERN_CLK_FREQ GENMASK(1, 0) +/* VID_RX */ +#define MAX96714_VIDEO_RX8 CCI_REG8(0x11a) +#define MAX96714_VID_LOCK BIT(6) + +/* VRX_PATGEN_0 */ +#define MAX96714_PATGEN_0 CCI_REG8(0x240) +#define MAX96714_PATGEN_1 CCI_REG8(0x241) +#define MAX96714_PATGEN_MODE GENMASK(5, 4) +#define MAX96714_PATGEN_VS_DLY CCI_REG24(0x242) +#define MAX96714_PATGEN_VS_HIGH CCI_REG24(0x245) +#define MAX96714_PATGEN_VS_LOW CCI_REG24(0x248) +#define MAX96714_PATGEN_V2H CCI_REG24(0x24b) +#define MAX96714_PATGEN_HS_HIGH CCI_REG16(0x24e) +#define MAX96714_PATGEN_HS_LOW CCI_REG16(0x250) +#define MAX96714_PATGEN_HS_CNT CCI_REG16(0x252) +#define MAX96714_PATGEN_V2D CCI_REG24(0x254) +#define MAX96714_PATGEN_DE_HIGH CCI_REG16(0x257) +#define MAX96714_PATGEN_DE_LOW CCI_REG16(0x259) +#define MAX96714_PATGEN_DE_CNT CCI_REG16(0x25B) +#define MAX96714_PATGEN_GRAD_INC CCI_REG8(0x25d) +#define MAX96714_PATGEN_CHKB_COLOR_A CCI_REG24(0x25E) +#define MAX96714_PATGEN_CHKB_COLOR_B CCI_REG24(0x261) +#define MAX96714_PATGEN_CHKB_RPT_CNT_A CCI_REG8(0x264) +#define MAX96714_PATGEN_CHKB_RPT_CNT_B CCI_REG8(0x265) +#define MAX96714_PATGEN_CHKB_ALT CCI_REG8(0x266) +/* BACKTOP */ +#define MAX96714_BACKTOP25 CCI_REG8(0x320) +#define CSI_DPLL_FREQ_MASK GENMASK(4, 0) + +/* MIPI_PHY */ +#define MAX96714_MIPI_PHY0 CCI_REG8(0x330) +#define MAX96714_FORCE_CSI_OUT BIT(7) +#define MAX96714_MIPI_STDBY_N CCI_REG8(0x332) +#define MAX96714_MIPI_STDBY_MASK GENMASK(5, 4) +#define MAX96714_MIPI_LANE_MAP CCI_REG8(0x333) +#define MAX96714_MIPI_POLARITY CCI_REG8(0x335) +#define MAX96714_MIPI_POLARITY_MASK GENMASK(5, 0) + +/* MIPI_TX */ +#define MAX96714_MIPI_LANE_CNT CCI_REG8(0x44a) +#define MAX96714_CSI2_LANE_CNT_MASK GENMASK(7, 6) +#define MAX96714_MIPI_TX52 CCI_REG8(0x474) +#define MAX96714_TUN_EN BIT(0) + +#define MHZ(v) ((u32)((v) * 1000000U)) + +enum max96714_vpg_mode { + MAX96714_VPG_DISABLED = 0, + MAX96714_VPG_CHECKERBOARD = 1, + MAX96714_VPG_GRADIENT = 2, +}; + +struct max96714_rxport { + struct { + struct v4l2_subdev *sd; + u16 pad; + struct fwnode_handle *ep_fwnode; + } source; + struct regulator *poc; +}; + +struct max96714_txport { + struct v4l2_fwnode_endpoint vep; +}; + +struct max96714_priv { + struct i2c_client *client; + struct regmap *regmap; + struct gpio_desc *pd_gpio; + struct max96714_rxport rxport; + struct i2c_mux_core *mux; + u64 enabled_source_streams; + struct v4l2_subdev sd; + struct media_pad pads[MAX96714_NPORTS]; + struct v4l2_mbus_config_mipi_csi2 mipi_csi2; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_async_notifier notifier; + s64 tx_link_freq; + enum max96714_vpg_mode pattern; +}; + +static inline struct max96714_priv *sd_to_max96714(struct v4l2_subdev *sd) +{ + return container_of(sd, struct max96714_priv, sd); +} + +static int max96714_enable_tx_port(struct max96714_priv *priv) +{ + return cci_update_bits(priv->regmap, MAX96714_MIPI_STDBY_N, + MAX96714_MIPI_STDBY_MASK, + MAX96714_MIPI_STDBY_MASK, NULL); +} + +static int max96714_disable_tx_port(struct max96714_priv *priv) +{ + return cci_update_bits(priv->regmap, MAX96714_MIPI_STDBY_N, + MAX96714_MIPI_STDBY_MASK, 0, NULL); +} + +static bool max96714_tx_port_enabled(struct max96714_priv *priv) +{ + u64 val; + + cci_read(priv->regmap, MAX96714_MIPI_STDBY_N, &val, NULL); + + return val & MAX96714_MIPI_STDBY_MASK; +} + +static int max96714_apply_patgen_timing(struct max96714_priv *priv, + struct v4l2_subdev_state *state) +{ + struct v4l2_mbus_framefmt *fmt = + v4l2_subdev_state_get_format(state, MAX96714_PAD_SOURCE); + const u32 h_active = fmt->width; + const u32 h_fp = 88; + const u32 h_sw = 44; + const u32 h_bp = 148; + u32 h_tot; + const u32 v_active = fmt->height; + const u32 v_fp = 4; + const u32 v_sw = 5; + const u32 v_bp = 36; + u32 v_tot; + int ret = 0; + + h_tot = h_active + h_fp + h_sw + h_bp; + v_tot = v_active + v_fp + v_sw + v_bp; + + /* 75 Mhz pixel clock */ + cci_update_bits(priv->regmap, MAX96714_IO_CHK0, + MAX96714_PATTERN_CLK_FREQ, 1, &ret); + + dev_info(&priv->client->dev, "height: %d width: %d\n", fmt->height, + fmt->width); + + cci_write(priv->regmap, MAX96714_PATGEN_VS_DLY, 0, &ret); + cci_write(priv->regmap, MAX96714_PATGEN_VS_HIGH, v_sw * h_tot, &ret); + cci_write(priv->regmap, MAX96714_PATGEN_VS_LOW, + (v_active + v_fp + v_bp) * h_tot, &ret); + cci_write(priv->regmap, MAX96714_PATGEN_HS_HIGH, h_sw, &ret); + cci_write(priv->regmap, MAX96714_PATGEN_HS_LOW, h_active + h_fp + h_bp, + &ret); + cci_write(priv->regmap, MAX96714_PATGEN_V2D, + h_tot * (v_sw + v_bp) + (h_sw + h_bp), &ret); + cci_write(priv->regmap, MAX96714_PATGEN_HS_CNT, v_tot, &ret); + cci_write(priv->regmap, MAX96714_PATGEN_DE_HIGH, h_active, &ret); + cci_write(priv->regmap, MAX96714_PATGEN_DE_LOW, h_fp + h_sw + h_bp, + &ret); + cci_write(priv->regmap, MAX96714_PATGEN_DE_CNT, v_active, &ret); + /* B G R */ + cci_write(priv->regmap, MAX96714_PATGEN_CHKB_COLOR_A, 0xfecc00, &ret); + /* B G R */ + cci_write(priv->regmap, MAX96714_PATGEN_CHKB_COLOR_B, 0x006aa7, &ret); + cci_write(priv->regmap, MAX96714_PATGEN_CHKB_RPT_CNT_A, 0x3c, &ret); + cci_write(priv->regmap, MAX96714_PATGEN_CHKB_RPT_CNT_B, 0x3c, &ret); + cci_write(priv->regmap, MAX96714_PATGEN_CHKB_ALT, 0x3c, &ret); + cci_write(priv->regmap, MAX96714_PATGEN_GRAD_INC, 0x10, &ret); + + return ret; +} + +static int max96714_apply_patgen(struct max96714_priv *priv, + struct v4l2_subdev_state *state) +{ + unsigned int val; + int ret = 0; + + if (priv->pattern) + ret = max96714_apply_patgen_timing(priv, state); + + cci_write(priv->regmap, MAX96714_PATGEN_0, priv->pattern ? 0xfb : 0, + &ret); + + val = FIELD_PREP(MAX96714_PATGEN_MODE, priv->pattern); + cci_update_bits(priv->regmap, MAX96714_PATGEN_1, MAX96714_PATGEN_MODE, + val, &ret); + return ret; +} + +static int max96714_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct max96714_priv *priv = + container_of(ctrl->handler, struct max96714_priv, ctrl_handler); + int ret; + + switch (ctrl->id) { + case V4L2_CID_TEST_PATTERN: + if (priv->enabled_source_streams) + return -EBUSY; + priv->pattern = ctrl->val; + break; + default: + return -EINVAL; + } + + ret = cci_update_bits(priv->regmap, MAX96714_MIPI_PHY0, + MAX96714_FORCE_CSI_OUT, + priv->pattern ? MAX96714_FORCE_CSI_OUT : 0, NULL); + + /* Pattern generator doesn't work with tunnel mode */ + return cci_update_bits(priv->regmap, MAX96714_MIPI_TX52, + MAX96714_TUN_EN, + priv->pattern ? 0 : MAX96714_TUN_EN, &ret); +} + +static const char * const max96714_test_pattern[] = { + "Disabled", + "Checkerboard", + "Gradient" +}; + +static const struct v4l2_ctrl_ops max96714_ctrl_ops = { + .s_ctrl = max96714_s_ctrl, +}; + +static int max96714_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 source_pad, u64 streams_mask) +{ + struct max96714_priv *priv = sd_to_max96714(sd); + u64 sink_streams; + int ret; + + if (!priv->enabled_source_streams) + max96714_enable_tx_port(priv); + + ret = max96714_apply_patgen(priv, state); + if (ret) + goto err; + + if (!priv->pattern) { + if (!priv->rxport.source.sd) { + ret = -ENODEV; + goto err; + } + + sink_streams = + v4l2_subdev_state_xlate_streams(state, + MAX96714_PAD_SOURCE, + MAX96714_PAD_SINK, + &streams_mask); + + ret = v4l2_subdev_enable_streams(priv->rxport.source.sd, + priv->rxport.source.pad, + sink_streams); + if (ret) + goto err; + } + + priv->enabled_source_streams |= streams_mask; + + return 0; + +err: + if (!priv->enabled_source_streams) + max96714_disable_tx_port(priv); + + return ret; +} + +static int max96714_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 source_pad, u64 streams_mask) +{ + struct max96714_priv *priv = sd_to_max96714(sd); + u64 sink_streams; + + if (!priv->pattern) { + int ret; + + sink_streams = + v4l2_subdev_state_xlate_streams(state, + MAX96714_PAD_SOURCE, + MAX96714_PAD_SINK, + &streams_mask); + + ret = v4l2_subdev_disable_streams(priv->rxport.source.sd, + priv->rxport.source.pad, + sink_streams); + if (ret) + return ret; + } + + priv->enabled_source_streams &= ~streams_mask; + + if (!priv->enabled_source_streams) + max96714_disable_tx_port(priv); + + return 0; +} + +static int max96714_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct max96714_priv *priv = sd_to_max96714(sd); + struct v4l2_mbus_framefmt *fmt; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && + priv->enabled_source_streams) + return -EBUSY; + + /* No transcoding, source and sink formats must match. */ + if (format->pad == MAX96714_PAD_SOURCE) + return v4l2_subdev_get_fmt(sd, state, format); + + fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); + if (!fmt) + return -EINVAL; + + *fmt = format->format; + + fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, + format->stream); + if (!fmt) + return -EINVAL; + + *fmt = format->format; + + return 0; +} + +static int _max96714_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + static const struct v4l2_mbus_framefmt format = { + .width = 1280, + .height = 1080, + .code = MEDIA_BUS_FMT_Y8_1X8, + .field = V4L2_FIELD_NONE, + }; + int ret; + + /* + * Note: we can only support up to V4L2_FRAME_DESC_ENTRY_MAX, until + * frame desc is made dynamically allocated. + */ + if (routing->num_routes > V4L2_FRAME_DESC_ENTRY_MAX) + return -EINVAL; + + ret = v4l2_subdev_routing_validate(sd, routing, + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); + if (ret) + return ret; + + return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format); +} + +static int max96714_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + struct max96714_priv *priv = sd_to_max96714(sd); + + if (which == V4L2_SUBDEV_FORMAT_ACTIVE && priv->enabled_source_streams) + return -EBUSY; + + return _max96714_set_routing(sd, state, which, routing); +} + +static int max96714_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_route routes[] = { + { + .sink_pad = MAX96714_PAD_SINK, + .sink_stream = 0, + .source_pad = MAX96714_PAD_SOURCE, + .source_stream = 0, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + } + }; + struct v4l2_subdev_krouting routing = { + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; + + return _max96714_set_routing(sd, state, V4L2_SUBDEV_FORMAT_ACTIVE, + &routing); +} + +static const struct v4l2_subdev_pad_ops max96714_pad_ops = { + .enable_streams = max96714_enable_streams, + .disable_streams = max96714_disable_streams, + + .set_routing = max96714_set_routing, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = max96714_set_fmt, +}; + +static bool max96714_link_locked(struct max96714_priv *priv) +{ + u64 val = 0; + + cci_read(priv->regmap, MAX96714_LINK_LOCK, &val, NULL); + + return val & MAX96714_LINK_LOCK_BIT; +} + +static void max96714_link_status(struct max96714_priv *priv) +{ + struct device *dev = &priv->client->dev; + + dev_info(dev, "Link locked:%d\n", max96714_link_locked(priv)); +} + +static bool max96714_pipe_locked(struct max96714_priv *priv) +{ + u64 val; + + cci_read(priv->regmap, MAX96714_VIDEO_RX8, &val, NULL); + + return val & MAX96714_VID_LOCK; +} + +static void max96714_pipe_status(struct max96714_priv *priv) +{ + struct device *dev = &priv->client->dev; + + dev_info(dev, "Pipe vidlock:%d\n", max96714_pipe_locked(priv)); +} + +static void max96714_csi_status(struct max96714_priv *priv) +{ + struct device *dev = &priv->client->dev; + u64 freq = 0; + + cci_read(priv->regmap, MAX96714_BACKTOP25, &freq, NULL); + freq = FIELD_GET(CSI_DPLL_FREQ_MASK, freq); + + dev_info(dev, "CSI controller DPLL freq:%u00MHz CSIPHY enabled:%d\n", + (u8)freq, max96714_tx_port_enabled(priv)); +} + +static int max96714_log_status(struct v4l2_subdev *sd) +{ + struct max96714_priv *priv = sd_to_max96714(sd); + struct device *dev = &priv->client->dev; + + dev_info(dev, "Deserializer: max96714\n"); + + max96714_link_status(priv); + max96714_pipe_status(priv); + max96714_csi_status(priv); + + return 0; +} + +static const struct v4l2_subdev_core_ops max96714_subdev_core_ops = { + .log_status = max96714_log_status, +}; + +static const struct v4l2_subdev_video_ops max96714_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_internal_ops max96714_internal_ops = { + .init_state = max96714_init_state, +}; + +static const struct v4l2_subdev_ops max96714_subdev_ops = { + .video = &max96714_video_ops, + .core = &max96714_subdev_core_ops, + .pad = &max96714_pad_ops, +}; + +static const struct media_entity_operations max96714_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static int max96714_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_connection *asd) +{ + struct max96714_priv *priv = sd_to_max96714(notifier->sd); + struct device *dev = &priv->client->dev; + int ret; + + ret = media_entity_get_fwnode_pad(&subdev->entity, + priv->rxport.source.ep_fwnode, + MEDIA_PAD_FL_SOURCE); + if (ret < 0) { + dev_err(dev, "Failed to find pad for %s\n", subdev->name); + return ret; + } + + priv->rxport.source.sd = subdev; + priv->rxport.source.pad = ret; + + ret = media_create_pad_link(&priv->rxport.source.sd->entity, + priv->rxport.source.pad, &priv->sd.entity, + MAX96714_PAD_SINK, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + dev_err(dev, "Unable to link %s:%u -> %s:%u\n", + priv->rxport.source.sd->name, priv->rxport.source.pad, + priv->sd.name, MAX96714_PAD_SINK); + return ret; + } + + return 0; +} + +static const struct v4l2_async_notifier_operations max96714_notify_ops = { + .bound = max96714_notify_bound, +}; + +static int max96714_v4l2_notifier_register(struct max96714_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct max96714_rxport *rxport = &priv->rxport; + struct v4l2_async_connection *asd; + int ret; + + if (!rxport->source.ep_fwnode) + return 0; + + v4l2_async_subdev_nf_init(&priv->notifier, &priv->sd); + + asd = v4l2_async_nf_add_fwnode(&priv->notifier, + rxport->source.ep_fwnode, + struct v4l2_async_connection); + if (IS_ERR(asd)) { + dev_err(dev, "Failed to add subdev: %pe", asd); + v4l2_async_nf_cleanup(&priv->notifier); + return PTR_ERR(asd); + } + + priv->notifier.ops = &max96714_notify_ops; + + ret = v4l2_async_nf_register(&priv->notifier); + if (ret) { + dev_err(dev, "Failed to register subdev_notifier"); + v4l2_async_nf_cleanup(&priv->notifier); + return ret; + } + + return 0; +} + +static int max96714_create_subdev(struct max96714_priv *priv) +{ + struct device *dev = &priv->client->dev; + int ret; + + v4l2_i2c_subdev_init(&priv->sd, priv->client, &max96714_subdev_ops); + priv->sd.internal_ops = &max96714_internal_ops; + + v4l2_ctrl_handler_init(&priv->ctrl_handler, 1); + priv->sd.ctrl_handler = &priv->ctrl_handler; + + v4l2_ctrl_new_int_menu(&priv->ctrl_handler, NULL, V4L2_CID_LINK_FREQ, + 0, 0, &priv->tx_link_freq); + v4l2_ctrl_new_std_menu_items(&priv->ctrl_handler, + &max96714_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(max96714_test_pattern) - 1, + 0, 0, max96714_test_pattern); + if (priv->ctrl_handler.error) { + ret = priv->ctrl_handler.error; + goto err_free_ctrl; + } + + priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; + priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + priv->sd.entity.ops = &max96714_entity_ops; + + priv->pads[MAX96714_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + priv->pads[MAX96714_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&priv->sd.entity, + MAX96714_NPORTS, + priv->pads); + if (ret) + goto err_free_ctrl; + + priv->sd.state_lock = priv->sd.ctrl_handler->lock; + + ret = v4l2_subdev_init_finalize(&priv->sd); + if (ret) + goto err_entity_cleanup; + + ret = max96714_v4l2_notifier_register(priv); + if (ret) { + dev_err(dev, "v4l2 subdev notifier register failed: %d\n", ret); + goto err_subdev_cleanup; + } + + ret = v4l2_async_register_subdev(&priv->sd); + if (ret) { + dev_err(dev, "v4l2_async_register_subdev error: %d\n", ret); + goto err_unreg_notif; + } + + return 0; + +err_unreg_notif: + v4l2_async_nf_unregister(&priv->notifier); + v4l2_async_nf_cleanup(&priv->notifier); +err_subdev_cleanup: + v4l2_subdev_cleanup(&priv->sd); +err_entity_cleanup: + media_entity_cleanup(&priv->sd.entity); +err_free_ctrl: + v4l2_ctrl_handler_free(&priv->ctrl_handler); + + return ret; +}; + +static void max96714_destroy_subdev(struct max96714_priv *priv) +{ + v4l2_async_nf_unregister(&priv->notifier); + v4l2_async_nf_cleanup(&priv->notifier); + v4l2_async_unregister_subdev(&priv->sd); + + v4l2_subdev_cleanup(&priv->sd); + + media_entity_cleanup(&priv->sd.entity); + v4l2_ctrl_handler_free(&priv->ctrl_handler); +} + +static int max96714_i2c_mux_select(struct i2c_mux_core *mux, u32 chan) +{ + return 0; +} + +static int max96714_i2c_mux_init(struct max96714_priv *priv) +{ + priv->mux = i2c_mux_alloc(priv->client->adapter, &priv->client->dev, + 1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE, + max96714_i2c_mux_select, NULL); + if (!priv->mux) + return -ENOMEM; + + return i2c_mux_add_adapter(priv->mux, 0, 0); +} + +static int max96714_init_tx_port(struct max96714_priv *priv) +{ + struct v4l2_mbus_config_mipi_csi2 *mipi; + unsigned long lanes_used = 0; + unsigned int val, lane; + int ret; + + ret = max96714_disable_tx_port(priv); + + mipi = &priv->mipi_csi2; + val = div_u64(priv->tx_link_freq * 2, MHZ(100)); + + cci_update_bits(priv->regmap, MAX96714_BACKTOP25, + CSI_DPLL_FREQ_MASK, val, &ret); + + val = FIELD_PREP(MAX96714_CSI2_LANE_CNT_MASK, mipi->num_data_lanes - 1); + cci_update_bits(priv->regmap, MAX96714_MIPI_LANE_CNT, + MAX96714_CSI2_LANE_CNT_MASK, val, &ret); + + /* lanes polarity */ + val = 0; + for (lane = 0; lane < mipi->num_data_lanes + 1; lane++) { + if (!mipi->lane_polarities[lane]) + continue; + if (lane == 0) + /* clock lane */ + val |= BIT(5); + else if (lane < 3) + /* Lane D0 and D1 */ + val |= BIT(lane - 1); + else + /* D2 and D3 */ + val |= BIT(lane); + } + + cci_update_bits(priv->regmap, MAX96714_MIPI_POLARITY, + MAX96714_MIPI_POLARITY_MASK, val, &ret); + + /* lanes mapping */ + val = 0; + for (lane = 0; lane < mipi->num_data_lanes; lane++) { + val |= (mipi->data_lanes[lane] - 1) << (lane * 2); + lanes_used |= BIT(mipi->data_lanes[lane] - 1); + } + + /* + * Unused lanes need to be mapped as well to not have + * the same lanes mapped twice. + */ + for (; lane < 4; lane++) { + unsigned int idx = find_first_zero_bit(&lanes_used, 4); + + val |= idx << (lane * 2); + lanes_used |= BIT(idx); + } + + return cci_write(priv->regmap, MAX96714_MIPI_LANE_MAP, val, &ret); +} + +static int max96714_rxport_enable_poc(struct max96714_priv *priv) +{ + struct max96714_rxport *rxport = &priv->rxport; + + if (!rxport->poc) + return 0; + + return regulator_enable(rxport->poc); +} + +static int max96714_rxport_disable_poc(struct max96714_priv *priv) +{ + struct max96714_rxport *rxport = &priv->rxport; + + if (!rxport->poc) + return 0; + + return regulator_disable(rxport->poc); +} + +static int max96714_parse_dt_txport(struct max96714_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct v4l2_fwnode_endpoint vep = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + struct fwnode_handle *ep_fwnode; + u32 num_data_lanes; + int ret; + + ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), + MAX96714_PAD_SOURCE, 0, 0); + if (!ep_fwnode) + return -EINVAL; + + ret = v4l2_fwnode_endpoint_alloc_parse(ep_fwnode, &vep); + fwnode_handle_put(ep_fwnode); + if (ret) { + dev_err(dev, "tx: failed to parse endpoint data\n"); + return -EINVAL; + } + + if (vep.nr_of_link_frequencies != 1) { + ret = -EINVAL; + goto err_free_vep; + } + + priv->tx_link_freq = vep.link_frequencies[0]; + /* Min 50MHz, Max 1250MHz, 50MHz step */ + if (priv->tx_link_freq < MHZ(50) || priv->tx_link_freq > MHZ(1250) || + (u32)priv->tx_link_freq % MHZ(50)) { + dev_err(dev, "tx: invalid link frequency\n"); + ret = -EINVAL; + goto err_free_vep; + } + + num_data_lanes = vep.bus.mipi_csi2.num_data_lanes; + if (num_data_lanes < 1 || num_data_lanes > 4) { + dev_err(dev, + "tx: invalid number of data lanes must be 1 to 4\n"); + ret = -EINVAL; + goto err_free_vep; + } + + memcpy(&priv->mipi_csi2, &vep.bus.mipi_csi2, sizeof(priv->mipi_csi2)); + +err_free_vep: + v4l2_fwnode_endpoint_free(&vep); + + return ret; +} + +static int max96714_parse_dt_rxport(struct max96714_priv *priv) +{ + static const char *poc_name = "port0-poc"; + struct max96714_rxport *rxport = &priv->rxport; + struct device *dev = &priv->client->dev; + struct fwnode_handle *ep_fwnode; + int ret; + + ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), + MAX96714_PAD_SINK, 0, 0); + if (!ep_fwnode) + return -ENOENT; + + rxport->source.ep_fwnode = fwnode_graph_get_remote_endpoint(ep_fwnode); + fwnode_handle_put(ep_fwnode); + + if (!rxport->source.ep_fwnode) { + dev_err(dev, "rx: no remote endpoint\n"); + return -EINVAL; + } + + rxport->poc = devm_regulator_get_optional(dev, poc_name); + if (IS_ERR(rxport->poc)) { + ret = PTR_ERR(rxport->poc); + if (ret == -ENODEV) { + rxport->poc = NULL; + } else { + dev_err(dev, "rx: failed to get POC supply: %d\n", ret); + goto err_put_source_ep_fwnode; + } + } + + return 0; + +err_put_source_ep_fwnode: + fwnode_handle_put(rxport->source.ep_fwnode); + return ret; +} + +static int max96714_parse_dt(struct max96714_priv *priv) +{ + int ret; + + ret = max96714_parse_dt_txport(priv); + if (ret) + return ret; + + ret = max96714_parse_dt_rxport(priv); + /* + * The deserializer can create a test pattern even if the + * rx port is not connected to a serializer. + */ + if (ret && ret == -ENOENT) + ret = 0; + + return ret; +} + +static int max96714_enable_core_hw(struct max96714_priv *priv) +{ + struct device *dev = &priv->client->dev; + u64 val; + int ret; + + if (priv->pd_gpio) { + /* wait min 2 ms for reset to complete */ + gpiod_set_value_cansleep(priv->pd_gpio, 1); + fsleep(2000); + gpiod_set_value_cansleep(priv->pd_gpio, 0); + /* wait min 2 ms for power up to finish */ + fsleep(2000); + } + + ret = cci_read(priv->regmap, MAX96714_REG13, &val, NULL); + if (ret) { + dev_err_probe(dev, ret, "Cannot read first register, abort\n"); + goto err_pd_gpio; + } + + if (val != MAX96714_DEVICE_ID && val != MAX96714F_DEVICE_ID) { + dev_err(dev, "Unsupported device id expected %x got %x\n", + MAX96714F_DEVICE_ID, (u8)val); + ret = -EOPNOTSUPP; + goto err_pd_gpio; + } + + ret = cci_read(priv->regmap, MAX96714_DEV_REV, &val, NULL); + if (ret) + goto err_pd_gpio; + + dev_dbg(dev, "Found %x (rev %lx)\n", MAX96714F_DEVICE_ID, + (u8)val & MAX96714_DEV_REV_MASK); + + ret = cci_read(priv->regmap, MAX96714_MIPI_TX52, &val, NULL); + if (ret) + goto err_pd_gpio; + + if (!(val & MAX96714_TUN_EN)) { + dev_err(dev, "Only supporting tunnel mode"); + ret = -EOPNOTSUPP; + goto err_pd_gpio; + } + + return 0; + +err_pd_gpio: + gpiod_set_value_cansleep(priv->pd_gpio, 1); + return ret; +} + +static void max96714_disable_core_hw(struct max96714_priv *priv) +{ + gpiod_set_value_cansleep(priv->pd_gpio, 1); +} + +static int max96714_get_hw_resources(struct max96714_priv *priv) +{ + struct device *dev = &priv->client->dev; + + priv->regmap = devm_cci_regmap_init_i2c(priv->client, 16); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + priv->pd_gpio = + devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); + if (IS_ERR(priv->pd_gpio)) + return dev_err_probe(dev, PTR_ERR(priv->pd_gpio), + "Cannot get powerdown GPIO\n"); + return 0; +} + +static int max96714_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct max96714_priv *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->client = client; + + ret = max96714_get_hw_resources(priv); + if (ret) + return ret; + + ret = max96714_enable_core_hw(priv); + if (ret) + return ret; + + ret = max96714_parse_dt(priv); + if (ret) + goto err_disable_core_hw; + + max96714_init_tx_port(priv); + + ret = max96714_rxport_enable_poc(priv); + if (ret) + goto err_free_ports; + + ret = max96714_i2c_mux_init(priv); + if (ret) + goto err_disable_poc; + + ret = max96714_create_subdev(priv); + if (ret) + goto err_del_mux; + + return 0; + +err_del_mux: + i2c_mux_del_adapters(priv->mux); +err_disable_poc: + max96714_rxport_disable_poc(priv); +err_free_ports: + fwnode_handle_put(priv->rxport.source.ep_fwnode); +err_disable_core_hw: + max96714_disable_core_hw(priv); + + return ret; +} + +static void max96714_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct max96714_priv *priv = sd_to_max96714(sd); + + max96714_destroy_subdev(priv); + i2c_mux_del_adapters(priv->mux); + max96714_rxport_disable_poc(priv); + fwnode_handle_put(priv->rxport.source.ep_fwnode); + max96714_disable_core_hw(priv); + gpiod_set_value_cansleep(priv->pd_gpio, 1); +} + +static const struct of_device_id max96714_of_ids[] = { + { .compatible = "maxim,max96714f" }, + { } +}; +MODULE_DEVICE_TABLE(of, max96714_of_ids); + +static struct i2c_driver max96714_i2c_driver = { + .driver = { + .name = "max96714", + .of_match_table = max96714_of_ids, + }, + .probe = max96714_probe, + .remove = max96714_remove, +}; + +module_i2c_driver(max96714_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Maxim Integrated GMSL2 Deserializers Driver"); +MODULE_AUTHOR("Julien Massot <julien.massot@collabora.com>"); diff --git a/drivers/media/i2c/max96717.c b/drivers/media/i2c/max96717.c new file mode 100644 index 000000000000..949306485873 --- /dev/null +++ b/drivers/media/i2c/max96717.c @@ -0,0 +1,927 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Maxim GMSL2 Serializer Driver + * + * Copyright (C) 2024 Collabora Ltd. + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/fwnode.h> +#include <linux/gpio/driver.h> +#include <linux/i2c-mux.h> +#include <linux/i2c.h> +#include <linux/regmap.h> + +#include <media/v4l2-cci.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> + +#define MAX96717_DEVICE_ID 0xbf +#define MAX96717F_DEVICE_ID 0xc8 +#define MAX96717_PORTS 2 +#define MAX96717_PAD_SINK 0 +#define MAX96717_PAD_SOURCE 1 + +#define MAX96717_DEFAULT_CLKOUT_RATE 24000000UL + +/* DEV */ +#define MAX96717_REG3 CCI_REG8(0x3) +#define MAX96717_RCLKSEL GENMASK(1, 0) +#define RCLKSEL_REF_PLL CCI_REG8(0x3) +#define MAX96717_REG6 CCI_REG8(0x6) +#define RCLKEN BIT(5) +#define MAX96717_DEV_ID CCI_REG8(0xd) +#define MAX96717_DEV_REV CCI_REG8(0xe) +#define MAX96717_DEV_REV_MASK GENMASK(3, 0) + +/* VID_TX Z */ +#define MAX96717_VIDEO_TX2 CCI_REG8(0x112) +#define MAX96717_VIDEO_PCLKDET BIT(7) + +/* GPIO */ +#define MAX96717_NUM_GPIO 11 +#define MAX96717_GPIO_REG_A(gpio) CCI_REG8(0x2be + (gpio) * 3) +#define MAX96717_GPIO_OUT BIT(4) +#define MAX96717_GPIO_IN BIT(3) +#define MAX96717_GPIO_RX_EN BIT(2) +#define MAX96717_GPIO_TX_EN BIT(1) +#define MAX96717_GPIO_OUT_DIS BIT(0) + +/* FRONTTOP */ +/* MAX96717 only have CSI port 'B' */ +#define MAX96717_FRONTOP0 CCI_REG8(0x308) +#define MAX96717_START_PORT_B BIT(5) + +/* MIPI_RX */ +#define MAX96717_MIPI_RX1 CCI_REG8(0x331) +#define MAX96717_MIPI_LANES_CNT GENMASK(5, 4) +#define MAX96717_MIPI_RX2 CCI_REG8(0x332) /* phy1 Lanes map */ +#define MAX96717_PHY2_LANES_MAP GENMASK(7, 4) +#define MAX96717_MIPI_RX3 CCI_REG8(0x333) /* phy2 Lanes map */ +#define MAX96717_PHY1_LANES_MAP GENMASK(3, 0) +#define MAX96717_MIPI_RX4 CCI_REG8(0x334) /* phy1 lane polarities */ +#define MAX96717_PHY1_LANES_POL GENMASK(6, 4) +#define MAX96717_MIPI_RX5 CCI_REG8(0x335) /* phy2 lane polarities */ +#define MAX96717_PHY2_LANES_POL GENMASK(2, 0) + +/* MIPI_RX_EXT */ +#define MAX96717_MIPI_RX_EXT11 CCI_REG8(0x383) +#define MAX96717_TUN_MODE BIT(7) + +/* REF_VTG */ +#define REF_VTG0 CCI_REG8(0x3f0) +#define REFGEN_PREDEF_EN BIT(6) +#define REFGEN_PREDEF_FREQ_MASK GENMASK(5, 4) +#define REFGEN_PREDEF_FREQ_ALT BIT(3) +#define REFGEN_RST BIT(1) +#define REFGEN_EN BIT(0) + +/* MISC */ +#define PIO_SLEW_1 CCI_REG8(0x570) + +struct max96717_priv { + struct i2c_client *client; + struct regmap *regmap; + struct i2c_mux_core *mux; + struct v4l2_mbus_config_mipi_csi2 mipi_csi2; + struct v4l2_subdev sd; + struct media_pad pads[MAX96717_PORTS]; + struct v4l2_async_notifier notifier; + struct v4l2_subdev *source_sd; + u16 source_sd_pad; + u64 enabled_source_streams; + u8 pll_predef_index; + struct clk_hw clk_hw; + struct gpio_chip gpio_chip; +}; + +static inline struct max96717_priv *sd_to_max96717(struct v4l2_subdev *sd) +{ + return container_of(sd, struct max96717_priv, sd); +} + +static inline struct max96717_priv *clk_hw_to_max96717(struct clk_hw *hw) +{ + return container_of(hw, struct max96717_priv, clk_hw); +} + +static int max96717_i2c_mux_select(struct i2c_mux_core *mux, u32 chan) +{ + return 0; +} + +static int max96717_i2c_mux_init(struct max96717_priv *priv) +{ + priv->mux = i2c_mux_alloc(priv->client->adapter, &priv->client->dev, + 1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE, + max96717_i2c_mux_select, NULL); + if (!priv->mux) + return -ENOMEM; + + return i2c_mux_add_adapter(priv->mux, 0, 0); +} + +static inline int max96717_start_csi(struct max96717_priv *priv, bool start) +{ + return cci_update_bits(priv->regmap, MAX96717_FRONTOP0, + MAX96717_START_PORT_B, + start ? MAX96717_START_PORT_B : 0, NULL); +} + +static int max96717_gpiochip_get(struct gpio_chip *gpiochip, + unsigned int offset) +{ + struct max96717_priv *priv = gpiochip_get_data(gpiochip); + u64 val; + int ret; + + ret = cci_read(priv->regmap, MAX96717_GPIO_REG_A(offset), + &val, NULL); + if (ret) + return ret; + + if (val & MAX96717_GPIO_OUT_DIS) + return !!(val & MAX96717_GPIO_IN); + else + return !!(val & MAX96717_GPIO_OUT); +} + +static void max96717_gpiochip_set(struct gpio_chip *gpiochip, + unsigned int offset, int value) +{ + struct max96717_priv *priv = gpiochip_get_data(gpiochip); + + cci_update_bits(priv->regmap, MAX96717_GPIO_REG_A(offset), + MAX96717_GPIO_OUT, MAX96717_GPIO_OUT, NULL); +} + +static int max96717_gpio_get_direction(struct gpio_chip *gpiochip, + unsigned int offset) +{ + struct max96717_priv *priv = gpiochip_get_data(gpiochip); + u64 val; + int ret; + + ret = cci_read(priv->regmap, MAX96717_GPIO_REG_A(offset), &val, NULL); + if (ret < 0) + return ret; + + return !!(val & MAX96717_GPIO_OUT_DIS); +} + +static int max96717_gpio_direction_out(struct gpio_chip *gpiochip, + unsigned int offset, int value) +{ + struct max96717_priv *priv = gpiochip_get_data(gpiochip); + + return cci_update_bits(priv->regmap, MAX96717_GPIO_REG_A(offset), + MAX96717_GPIO_OUT_DIS | MAX96717_GPIO_OUT, + value ? MAX96717_GPIO_OUT : 0, NULL); +} + +static int max96717_gpio_direction_in(struct gpio_chip *gpiochip, + unsigned int offset) +{ + struct max96717_priv *priv = gpiochip_get_data(gpiochip); + + return cci_update_bits(priv->regmap, MAX96717_GPIO_REG_A(offset), + MAX96717_GPIO_OUT_DIS, MAX96717_GPIO_OUT_DIS, + NULL); +} + +static int max96717_gpiochip_probe(struct max96717_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct gpio_chip *gc = &priv->gpio_chip; + int i, ret = 0; + + gc->label = dev_name(dev); + gc->parent = dev; + gc->owner = THIS_MODULE; + gc->ngpio = MAX96717_NUM_GPIO; + gc->base = -1; + gc->can_sleep = true; + gc->get_direction = max96717_gpio_get_direction; + gc->direction_input = max96717_gpio_direction_in; + gc->direction_output = max96717_gpio_direction_out; + gc->set = max96717_gpiochip_set; + gc->get = max96717_gpiochip_get; + gc->of_gpio_n_cells = 2; + + /* Disable GPIO forwarding */ + for (i = 0; i < gc->ngpio; i++) + cci_update_bits(priv->regmap, MAX96717_GPIO_REG_A(i), + MAX96717_GPIO_RX_EN | MAX96717_GPIO_TX_EN, + 0, &ret); + + if (ret) + return ret; + + ret = devm_gpiochip_add_data(dev, gc, priv); + if (ret) { + dev_err(dev, "Unable to create gpio_chip\n"); + return ret; + } + + return 0; +} + +static int _max96717_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing) +{ + static const struct v4l2_mbus_framefmt format = { + .width = 1280, + .height = 1080, + .code = MEDIA_BUS_FMT_Y8_1X8, + .field = V4L2_FIELD_NONE, + }; + int ret; + + ret = v4l2_subdev_routing_validate(sd, routing, + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); + if (ret) + return ret; + + ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format); + if (ret) + return ret; + + return 0; +} + +static int max96717_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + struct max96717_priv *priv = sd_to_max96717(sd); + + if (which == V4L2_SUBDEV_FORMAT_ACTIVE && priv->enabled_source_streams) + return -EBUSY; + + return _max96717_set_routing(sd, state, routing); +} + +static int max96717_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct max96717_priv *priv = sd_to_max96717(sd); + struct v4l2_mbus_framefmt *fmt; + u64 stream_source_mask; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && + priv->enabled_source_streams) + return -EBUSY; + + /* No transcoding, source and sink formats must match. */ + if (format->pad == MAX96717_PAD_SOURCE) + return v4l2_subdev_get_fmt(sd, state, format); + + /* Set sink format */ + fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); + if (!fmt) + return -EINVAL; + + *fmt = format->format; + + /* Propagate to source format */ + fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, + format->stream); + if (!fmt) + return -EINVAL; + *fmt = format->format; + + stream_source_mask = BIT(format->stream); + + return v4l2_subdev_state_xlate_streams(state, MAX96717_PAD_SOURCE, + MAX96717_PAD_SINK, + &stream_source_mask); +} + +static int max96717_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_route routes[] = { + { + .sink_pad = MAX96717_PAD_SINK, + .sink_stream = 0, + .source_pad = MAX96717_PAD_SOURCE, + .source_stream = 0, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }, + }; + struct v4l2_subdev_krouting routing = { + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; + + return _max96717_set_routing(sd, state, &routing); +} + +static bool max96717_pipe_pclkdet(struct max96717_priv *priv) +{ + u64 val = 0; + + cci_read(priv->regmap, MAX96717_VIDEO_TX2, &val, NULL); + + return val & MAX96717_VIDEO_PCLKDET; +} + +static int max96717_log_status(struct v4l2_subdev *sd) +{ + struct max96717_priv *priv = sd_to_max96717(sd); + struct device *dev = &priv->client->dev; + + dev_info(dev, "Serializer: max96717\n"); + dev_info(dev, "Pipe: pclkdet:%d\n", max96717_pipe_pclkdet(priv)); + + return 0; +} + +static int max96717_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct max96717_priv *priv = sd_to_max96717(sd); + struct device *dev = &priv->client->dev; + u64 sink_streams; + int ret; + + sink_streams = v4l2_subdev_state_xlate_streams(state, + MAX96717_PAD_SOURCE, + MAX96717_PAD_SINK, + &streams_mask); + + if (!priv->enabled_source_streams) + max96717_start_csi(priv, true); + + ret = v4l2_subdev_enable_streams(priv->source_sd, priv->source_sd_pad, + sink_streams); + if (ret) { + dev_err(dev, "Fail to start streams:%llu on remote subdev\n", + sink_streams); + goto stop_csi; + } + + priv->enabled_source_streams |= streams_mask; + + return 0; + +stop_csi: + if (!priv->enabled_source_streams) + max96717_start_csi(priv, false); + return ret; +} + +static int max96717_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct max96717_priv *priv = sd_to_max96717(sd); + u64 sink_streams; + + /* + * Stop the CSI receiver first then the source, + * otherwise the device may become unresponsive + * while holding the I2C bus low. + */ + priv->enabled_source_streams &= ~streams_mask; + if (!priv->enabled_source_streams) + max96717_start_csi(priv, false); + + sink_streams = v4l2_subdev_state_xlate_streams(state, + MAX96717_PAD_SOURCE, + MAX96717_PAD_SINK, + &streams_mask); + + return v4l2_subdev_disable_streams(priv->source_sd, priv->source_sd_pad, + sink_streams); +} + +static const struct v4l2_subdev_pad_ops max96717_pad_ops = { + .enable_streams = max96717_enable_streams, + .disable_streams = max96717_disable_streams, + .set_routing = max96717_set_routing, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = max96717_set_fmt, +}; + +static const struct v4l2_subdev_core_ops max96717_subdev_core_ops = { + .log_status = max96717_log_status, +}; + +static const struct v4l2_subdev_internal_ops max96717_internal_ops = { + .init_state = max96717_init_state, +}; + +static const struct v4l2_subdev_ops max96717_subdev_ops = { + .core = &max96717_subdev_core_ops, + .pad = &max96717_pad_ops, +}; + +static const struct media_entity_operations max96717_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static int max96717_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *source_subdev, + struct v4l2_async_connection *asd) +{ + struct max96717_priv *priv = sd_to_max96717(notifier->sd); + struct device *dev = &priv->client->dev; + int ret; + + ret = media_entity_get_fwnode_pad(&source_subdev->entity, + source_subdev->fwnode, + MEDIA_PAD_FL_SOURCE); + if (ret < 0) { + dev_err(dev, "Failed to find pad for %s\n", + source_subdev->name); + return ret; + } + + priv->source_sd = source_subdev; + priv->source_sd_pad = ret; + + ret = media_create_pad_link(&source_subdev->entity, priv->source_sd_pad, + &priv->sd.entity, 0, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + dev_err(dev, "Unable to link %s:%u -> %s:0\n", + source_subdev->name, priv->source_sd_pad, + priv->sd.name); + return ret; + } + + return 0; +} + +static const struct v4l2_async_notifier_operations max96717_notify_ops = { + .bound = max96717_notify_bound, +}; + +static int max96717_v4l2_notifier_register(struct max96717_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct v4l2_async_connection *asd; + struct fwnode_handle *ep_fwnode; + int ret; + + ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), + MAX96717_PAD_SINK, 0, 0); + if (!ep_fwnode) { + dev_err(dev, "No graph endpoint\n"); + return -ENODEV; + } + + v4l2_async_subdev_nf_init(&priv->notifier, &priv->sd); + + asd = v4l2_async_nf_add_fwnode_remote(&priv->notifier, ep_fwnode, + struct v4l2_async_connection); + + fwnode_handle_put(ep_fwnode); + + if (IS_ERR(asd)) { + dev_err(dev, "Failed to add subdev: %ld", PTR_ERR(asd)); + v4l2_async_nf_cleanup(&priv->notifier); + return PTR_ERR(asd); + } + + priv->notifier.ops = &max96717_notify_ops; + + ret = v4l2_async_nf_register(&priv->notifier); + if (ret) { + dev_err(dev, "Failed to register subdev_notifier"); + v4l2_async_nf_cleanup(&priv->notifier); + return ret; + } + + return 0; +} + +static int max96717_subdev_init(struct max96717_priv *priv) +{ + struct device *dev = &priv->client->dev; + int ret; + + v4l2_i2c_subdev_init(&priv->sd, priv->client, &max96717_subdev_ops); + priv->sd.internal_ops = &max96717_internal_ops; + + priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; + priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + priv->sd.entity.ops = &max96717_entity_ops; + + priv->pads[MAX96717_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + priv->pads[MAX96717_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&priv->sd.entity, 2, priv->pads); + if (ret) + return dev_err_probe(dev, ret, "Failed to init pads\n"); + + ret = v4l2_subdev_init_finalize(&priv->sd); + if (ret) { + dev_err_probe(dev, ret, + "v4l2 subdev init finalized failed\n"); + goto err_entity_cleanup; + } + ret = max96717_v4l2_notifier_register(priv); + if (ret) { + dev_err_probe(dev, ret, + "v4l2 subdev notifier register failed\n"); + goto err_free_state; + } + + ret = v4l2_async_register_subdev(&priv->sd); + if (ret) { + dev_err_probe(dev, ret, "v4l2_async_register_subdev error\n"); + goto err_unreg_notif; + } + + return 0; + +err_unreg_notif: + v4l2_async_nf_unregister(&priv->notifier); + v4l2_async_nf_cleanup(&priv->notifier); +err_free_state: + v4l2_subdev_cleanup(&priv->sd); +err_entity_cleanup: + media_entity_cleanup(&priv->sd.entity); + + return ret; +} + +static void max96717_subdev_uninit(struct max96717_priv *priv) +{ + v4l2_async_unregister_subdev(&priv->sd); + v4l2_async_nf_unregister(&priv->notifier); + v4l2_async_nf_cleanup(&priv->notifier); + v4l2_subdev_cleanup(&priv->sd); + media_entity_cleanup(&priv->sd.entity); +} + +struct max96717_pll_predef_freq { + unsigned long freq; + bool is_alt; + u8 val; +}; + +static const struct max96717_pll_predef_freq max96717_predef_freqs[] = { + { 13500000, true, 0 }, { 19200000, false, 0 }, + { 24000000, true, 1 }, { 27000000, false, 1 }, + { 37125000, false, 2 }, { 74250000, false, 3 }, +}; + +static unsigned long +max96717_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + struct max96717_priv *priv = clk_hw_to_max96717(hw); + + return max96717_predef_freqs[priv->pll_predef_index].freq; +} + +static unsigned int max96717_clk_find_best_index(struct max96717_priv *priv, + unsigned long rate) +{ + unsigned int i, idx; + unsigned long diff_new, diff_old; + + diff_old = U32_MAX; + idx = 0; + + for (i = 0; i < ARRAY_SIZE(max96717_predef_freqs); i++) { + diff_new = abs(rate - max96717_predef_freqs[i].freq); + if (diff_new < diff_old) { + diff_old = diff_new; + idx = i; + } + } + + return idx; +} + +static long max96717_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct max96717_priv *priv = clk_hw_to_max96717(hw); + struct device *dev = &priv->client->dev; + unsigned int idx; + + idx = max96717_clk_find_best_index(priv, rate); + + if (rate != max96717_predef_freqs[idx].freq) { + dev_warn(dev, "Request CLK freq:%lu, found CLK freq:%lu\n", + rate, max96717_predef_freqs[idx].freq); + } + + return max96717_predef_freqs[idx].freq; +} + +static int max96717_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct max96717_priv *priv = clk_hw_to_max96717(hw); + unsigned int val, idx; + int ret = 0; + + idx = max96717_clk_find_best_index(priv, rate); + + val = FIELD_PREP(REFGEN_PREDEF_FREQ_MASK, + max96717_predef_freqs[idx].val); + + if (max96717_predef_freqs[idx].is_alt) + val |= REFGEN_PREDEF_FREQ_ALT; + + val |= REFGEN_RST | REFGEN_PREDEF_EN; + + cci_write(priv->regmap, REF_VTG0, val, &ret); + cci_update_bits(priv->regmap, REF_VTG0, REFGEN_RST | REFGEN_EN, + REFGEN_EN, &ret); + if (ret) + return ret; + + priv->pll_predef_index = idx; + + return 0; +} + +static int max96717_clk_prepare(struct clk_hw *hw) +{ + struct max96717_priv *priv = clk_hw_to_max96717(hw); + + return cci_update_bits(priv->regmap, MAX96717_REG6, RCLKEN, + RCLKEN, NULL); +} + +static void max96717_clk_unprepare(struct clk_hw *hw) +{ + struct max96717_priv *priv = clk_hw_to_max96717(hw); + + cci_update_bits(priv->regmap, MAX96717_REG6, RCLKEN, 0, NULL); +} + +static const struct clk_ops max96717_clk_ops = { + .prepare = max96717_clk_prepare, + .unprepare = max96717_clk_unprepare, + .set_rate = max96717_clk_set_rate, + .recalc_rate = max96717_clk_recalc_rate, + .round_rate = max96717_clk_round_rate, +}; + +static int max96717_register_clkout(struct max96717_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct clk_init_data init = { .ops = &max96717_clk_ops }; + int ret; + + init.name = kasprintf(GFP_KERNEL, "max96717.%s.clk_out", + dev_name(dev)); + if (!init.name) + return -ENOMEM; + + /* RCLKSEL Reference PLL output */ + ret = cci_update_bits(priv->regmap, MAX96717_REG3, MAX96717_RCLKSEL, + MAX96717_RCLKSEL, NULL); + /* MFP4 fastest slew rate */ + cci_update_bits(priv->regmap, PIO_SLEW_1, BIT(5) | BIT(4), 0, &ret); + if (ret) + goto free_init_name; + + priv->clk_hw.init = &init; + + /* Initialize to 24 MHz */ + ret = max96717_clk_set_rate(&priv->clk_hw, + MAX96717_DEFAULT_CLKOUT_RATE, 0); + if (ret < 0) + goto free_init_name; + + ret = devm_clk_hw_register(dev, &priv->clk_hw); + kfree(init.name); + if (ret) + return dev_err_probe(dev, ret, "Cannot register clock HW\n"); + + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, + &priv->clk_hw); + if (ret) + return dev_err_probe(dev, ret, + "Cannot add OF clock provider\n"); + + return 0; + +free_init_name: + kfree(init.name); + return ret; +} + +static int max96717_init_csi_lanes(struct max96717_priv *priv) +{ + struct v4l2_mbus_config_mipi_csi2 *mipi = &priv->mipi_csi2; + unsigned long lanes_used = 0; + unsigned int nlanes, lane, val = 0; + int ret; + + nlanes = mipi->num_data_lanes; + + ret = cci_update_bits(priv->regmap, MAX96717_MIPI_RX1, + MAX96717_MIPI_LANES_CNT, + FIELD_PREP(MAX96717_MIPI_LANES_CNT, + nlanes - 1), NULL); + + /* lanes polarity */ + for (lane = 0; lane < nlanes + 1; lane++) { + if (!mipi->lane_polarities[lane]) + continue; + /* Clock lane */ + if (lane == 0) + val |= BIT(2); + else if (lane < 3) + val |= BIT(lane - 1); + else + val |= BIT(lane); + } + + cci_update_bits(priv->regmap, MAX96717_MIPI_RX5, + MAX96717_PHY2_LANES_POL, + FIELD_PREP(MAX96717_PHY2_LANES_POL, val), &ret); + + cci_update_bits(priv->regmap, MAX96717_MIPI_RX4, + MAX96717_PHY1_LANES_POL, + FIELD_PREP(MAX96717_PHY1_LANES_POL, + val >> 3), &ret); + /* lanes mapping */ + for (lane = 0, val = 0; lane < nlanes; lane++) { + val |= (mipi->data_lanes[lane] - 1) << (lane * 2); + lanes_used |= BIT(mipi->data_lanes[lane] - 1); + } + + /* + * Unused lanes need to be mapped as well to not have + * the same lanes mapped twice. + */ + for (; lane < 4; lane++) { + unsigned int idx = find_first_zero_bit(&lanes_used, 4); + + val |= idx << (lane * 2); + lanes_used |= BIT(idx); + } + + cci_update_bits(priv->regmap, MAX96717_MIPI_RX3, + MAX96717_PHY1_LANES_MAP, + FIELD_PREP(MAX96717_PHY1_LANES_MAP, val), &ret); + + return cci_update_bits(priv->regmap, MAX96717_MIPI_RX2, + MAX96717_PHY2_LANES_MAP, + FIELD_PREP(MAX96717_PHY2_LANES_MAP, val >> 4), + &ret); +} + +static int max96717_hw_init(struct max96717_priv *priv) +{ + struct device *dev = &priv->client->dev; + u64 dev_id, val; + int ret; + + ret = cci_read(priv->regmap, MAX96717_DEV_ID, &dev_id, NULL); + if (ret) + return dev_err_probe(dev, ret, + "Fail to read the device id\n"); + + if (dev_id != MAX96717_DEVICE_ID && dev_id != MAX96717F_DEVICE_ID) + return dev_err_probe(dev, -EOPNOTSUPP, + "Unsupported device id got %x\n", (u8)dev_id); + + ret = cci_read(priv->regmap, MAX96717_DEV_REV, &val, NULL); + if (ret) + return dev_err_probe(dev, ret, + "Fail to read device revision"); + + dev_dbg(dev, "Found %x (rev %lx)\n", (u8)dev_id, + (u8)val & MAX96717_DEV_REV_MASK); + + ret = cci_read(priv->regmap, MAX96717_MIPI_RX_EXT11, &val, NULL); + if (ret) + return dev_err_probe(dev, ret, + "Fail to read mipi rx extension"); + + if (!(val & MAX96717_TUN_MODE)) + return dev_err_probe(dev, -EOPNOTSUPP, + "Only supporting tunnel mode"); + + return max96717_init_csi_lanes(priv); +} + +static int max96717_parse_dt(struct max96717_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct v4l2_fwnode_endpoint vep = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + struct fwnode_handle *ep_fwnode; + unsigned char num_data_lanes; + int ret; + + ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), + MAX96717_PAD_SINK, 0, 0); + if (!ep_fwnode) + return dev_err_probe(dev, -ENOENT, "no endpoint found\n"); + + ret = v4l2_fwnode_endpoint_parse(ep_fwnode, &vep); + + fwnode_handle_put(ep_fwnode); + + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to parse sink endpoint"); + + num_data_lanes = vep.bus.mipi_csi2.num_data_lanes; + if (num_data_lanes < 1 || num_data_lanes > 4) + return dev_err_probe(dev, -EINVAL, + "Invalid data lanes must be 1 to 4\n"); + + memcpy(&priv->mipi_csi2, &vep.bus.mipi_csi2, sizeof(priv->mipi_csi2)); + + return 0; +} + +static int max96717_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct max96717_priv *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->client = client; + priv->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(priv->regmap)) { + ret = PTR_ERR(priv->regmap); + return dev_err_probe(dev, ret, "Failed to init regmap\n"); + } + + ret = max96717_parse_dt(priv); + if (ret) + return dev_err_probe(dev, ret, "Failed to parse the dt\n"); + + ret = max96717_hw_init(priv); + if (ret) + return dev_err_probe(dev, ret, + "Failed to initialize the hardware\n"); + + ret = max96717_gpiochip_probe(priv); + if (ret) + return dev_err_probe(&client->dev, ret, + "Failed to init gpiochip\n"); + + ret = max96717_register_clkout(priv); + if (ret) + return dev_err_probe(dev, ret, "Failed to register clkout\n"); + + ret = max96717_subdev_init(priv); + if (ret) + return dev_err_probe(dev, ret, + "Failed to initialize v4l2 subdev\n"); + + ret = max96717_i2c_mux_init(priv); + if (ret) { + dev_err_probe(dev, ret, "failed to add remote i2c adapter\n"); + max96717_subdev_uninit(priv); + } + + return ret; +} + +static void max96717_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct max96717_priv *priv = sd_to_max96717(sd); + + max96717_subdev_uninit(priv); + i2c_mux_del_adapters(priv->mux); +} + +static const struct of_device_id max96717_of_ids[] = { + { .compatible = "maxim,max96717f" }, + { } +}; +MODULE_DEVICE_TABLE(of, max96717_of_ids); + +static struct i2c_driver max96717_i2c_driver = { + .driver = { + .name = "max96717", + .of_match_table = max96717_of_ids, + }, + .probe = max96717_probe, + .remove = max96717_remove, +}; + +module_i2c_driver(max96717_i2c_driver); + +MODULE_DESCRIPTION("Maxim GMSL2 MAX96717 Serializer Driver"); +MODULE_AUTHOR("Julien Massot <julien.massot@collabora.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/ov2680.c b/drivers/media/i2c/ov2680.c index 3ae0ea58668d..7237fb27ecd0 100644 --- a/drivers/media/i2c/ov2680.c +++ b/drivers/media/i2c/ov2680.c @@ -971,7 +971,7 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor) if (ret < 0) return ret; - v4l2_ctrl_handler_init(hdl, 5); + v4l2_ctrl_handler_init(hdl, 11); hdl->lock = &sensor->lock; diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 7e1ecdf2485f..0fb4d7bff9d1 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -1360,24 +1360,21 @@ static int ov5647_parse_dt(struct ov5647 *sensor, struct device_node *np) struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = V4L2_MBUS_CSI2_DPHY, }; - struct device_node *ep; + struct device_node *ep __free(device_node) = + of_graph_get_endpoint_by_regs(np, 0, -1); int ret; - ep = of_graph_get_endpoint_by_regs(np, 0, -1); if (!ep) return -EINVAL; ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &bus_cfg); if (ret) - goto out; + return ret; sensor->clock_ncont = bus_cfg.bus.mipi_csi2.flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK; -out: - of_node_put(ep); - - return ret; + return 0; } static int ov5647_probe(struct i2c_client *client) diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c index 8deb28b55983..46b9ce111676 100644 --- a/drivers/media/i2c/ov5693.c +++ b/drivers/media/i2c/ov5693.c @@ -141,7 +141,6 @@ struct ov5693_device { struct gpio_desc *reset; struct gpio_desc *powerdown; - struct gpio_desc *privacy_led; struct regulator_bulk_data supplies[OV5693_NUM_SUPPLIES]; struct clk *xvclk; @@ -657,7 +656,6 @@ static int ov5693_sensor_init(struct ov5693_device *ov5693) static void ov5693_sensor_powerdown(struct ov5693_device *ov5693) { - gpiod_set_value_cansleep(ov5693->privacy_led, 0); gpiod_set_value_cansleep(ov5693->reset, 1); gpiod_set_value_cansleep(ov5693->powerdown, 1); @@ -687,7 +685,6 @@ static int ov5693_sensor_powerup(struct ov5693_device *ov5693) gpiod_set_value_cansleep(ov5693->powerdown, 0); gpiod_set_value_cansleep(ov5693->reset, 0); - gpiod_set_value_cansleep(ov5693->privacy_led, 1); usleep_range(5000, 7500); @@ -1201,13 +1198,6 @@ static int ov5693_configure_gpios(struct ov5693_device *ov5693) return PTR_ERR(ov5693->powerdown); } - ov5693->privacy_led = devm_gpiod_get_optional(ov5693->dev, "privacy-led", - GPIOD_OUT_LOW); - if (IS_ERR(ov5693->privacy_led)) { - dev_err(ov5693->dev, "Error fetching privacy-led GPIO\n"); - return PTR_ERR(ov5693->privacy_led); - } - return 0; } diff --git a/drivers/media/i2c/tw9910.c b/drivers/media/i2c/tw9910.c index 905af98c7d53..6dffaaa9ed56 100644 --- a/drivers/media/i2c/tw9910.c +++ b/drivers/media/i2c/tw9910.c @@ -212,11 +212,6 @@ * structure */ -struct regval_list { - unsigned char reg_num; - unsigned char value; -}; - struct tw9910_scale_ctrl { char *name; unsigned short width; diff --git a/drivers/media/i2c/uda1342.c b/drivers/media/i2c/uda1342.c index da7bc4700bed..abd052a44bd7 100644 --- a/drivers/media/i2c/uda1342.c +++ b/drivers/media/i2c/uda1342.c @@ -95,4 +95,5 @@ static struct i2c_driver uda1342_driver = { module_i2c_driver(uda1342_driver); +MODULE_DESCRIPTION("Philips UDA1342 audio codec driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/st-vgxy61.c b/drivers/media/i2c/vgxy61.c index b9e7c57027b1..30378e962016 100644 --- a/drivers/media/i2c/st-vgxy61.c +++ b/drivers/media/i2c/vgxy61.c @@ -1878,7 +1878,7 @@ static const struct dev_pm_ops vgxy61_pm_ops = { static struct i2c_driver vgxy61_i2c_driver = { .driver = { - .name = "st-vgxy61", + .name = "vgxy61", .of_match_table = vgxy61_dt_ids, .pm = &vgxy61_pm_ops, }, diff --git a/drivers/media/pci/bt8xx/bt878.c b/drivers/media/pci/bt8xx/bt878.c index 90972d6952f1..62a6c4a80bed 100644 --- a/drivers/media/pci/bt8xx/bt878.c +++ b/drivers/media/pci/bt8xx/bt878.c @@ -300,8 +300,8 @@ static irqreturn_t bt878_irq(int irq, void *dev_id) } if (astat & BT878_ARISCI) { bt->finished_block = (stat & BT878_ARISCS) >> 28; - if (bt->tasklet.callback) - tasklet_schedule(&bt->tasklet); + if (bt->bh_work.func) + queue_work(system_bh_wq, &bt->bh_work); break; } count++; @@ -478,8 +478,8 @@ static int bt878_probe(struct pci_dev *dev, const struct pci_device_id *pci_id) btwrite(0, BT878_AINT_MASK); bt878_num++; - if (!bt->tasklet.func) - tasklet_disable(&bt->tasklet); + if (!bt->bh_work.func) + disable_work_sync(&bt->bh_work); return 0; @@ -563,4 +563,5 @@ static void __exit bt878_cleanup_module(void) module_init(bt878_init_module); module_exit(bt878_cleanup_module); +MODULE_DESCRIPTION("DVB/ATSC Support for bt878 based TV cards"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/bt8xx/bt878.h b/drivers/media/pci/bt8xx/bt878.h index fde8db293c54..5b1c7f56e553 100644 --- a/drivers/media/pci/bt8xx/bt878.h +++ b/drivers/media/pci/bt8xx/bt878.h @@ -14,6 +14,7 @@ #include <linux/sched.h> #include <linux/spinlock.h> #include <linux/mutex.h> +#include <linux/workqueue.h> #include "bt848.h" #include "bttv.h" @@ -120,7 +121,7 @@ struct bt878 { dma_addr_t risc_dma; u32 risc_pos; - struct tasklet_struct tasklet; + struct work_struct bh_work; int shutdown; }; diff --git a/drivers/media/pci/bt8xx/dvb-bt8xx.c b/drivers/media/pci/bt8xx/dvb-bt8xx.c index 390cbba6c065..f0fbb468aea2 100644 --- a/drivers/media/pci/bt8xx/dvb-bt8xx.c +++ b/drivers/media/pci/bt8xx/dvb-bt8xx.c @@ -39,9 +39,9 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); #define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */ -static void dvb_bt8xx_task(struct tasklet_struct *t) +static void dvb_bt8xx_work(struct work_struct *t) { - struct bt878 *bt = from_tasklet(bt, t, tasklet); + struct bt878 *bt = from_work(bt, t, bh_work); struct dvb_bt8xx_card *card = dev_get_drvdata(&bt->adapter->dev); dprintk("%d\n", card->bt->finished_block); @@ -782,7 +782,7 @@ static int dvb_bt8xx_load_card(struct dvb_bt8xx_card *card, u32 type) goto err_disconnect_frontend; } - tasklet_setup(&card->bt->tasklet, dvb_bt8xx_task); + INIT_WORK(&card->bt->bh_work, dvb_bt8xx_work); frontend_init(card, type); @@ -922,7 +922,7 @@ static void dvb_bt8xx_remove(struct bttv_sub_device *sub) dprintk("dvb_bt8xx: unloading card%d\n", card->bttv_nr); bt878_stop(card->bt); - tasklet_kill(&card->bt->tasklet); + cancel_work_sync(&card->bt->bh_work); dvb_net_release(&card->dvbnet); card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_mem); card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_hw); diff --git a/drivers/media/pci/cx18/cx18-scb.h b/drivers/media/pci/cx18/cx18-scb.h index f7105421dd25..841edc0712ab 100644 --- a/drivers/media/pci/cx18/cx18-scb.h +++ b/drivers/media/pci/cx18/cx18-scb.h @@ -258,7 +258,7 @@ struct cx18_scb { struct cx18_mailbox ppu2epu_mb; struct cx18_mdl_ack cpu_mdl_ack[CX18_MAX_STREAMS][CX18_MAX_MDL_ACKS]; - struct cx18_mdl_ent cpu_mdl[1]; + struct cx18_mdl_ent cpu_mdl[]; }; void cx18_init_scb(struct cx18 *cx); diff --git a/drivers/media/pci/ddbridge/ddbridge.h b/drivers/media/pci/ddbridge/ddbridge.h index f3699dbd193f..f01ecdb0b627 100644 --- a/drivers/media/pci/ddbridge/ddbridge.h +++ b/drivers/media/pci/ddbridge/ddbridge.h @@ -298,7 +298,7 @@ struct ddb_link { spinlock_t lock; /* lock link access */ struct mutex flash_mutex; /* lock flash access */ struct ddb_lnb lnb; - struct tasklet_struct tasklet; + struct work_struct bh_work; struct ddb_ids ids; spinlock_t temp_lock; /* lock temp chip access */ diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c index 61750cc98d70..a0e9a71580b5 100644 --- a/drivers/media/pci/intel/ipu-bridge.c +++ b/drivers/media/pci/intel/ipu-bridge.c @@ -43,28 +43,46 @@ * becoming apparent in the future. * * Do not add an entry for a sensor that is not actually supported. + * + * Please keep the list sorted by ACPI HID. */ static const struct ipu_sensor_config ipu_supported_sensors[] = { + /* Himax HM11B1 */ + IPU_SENSOR_CONFIG("HIMX11B1", 1, 384000000), + /* Himax HM2170 */ + IPU_SENSOR_CONFIG("HIMX2170", 1, 384000000), + /* Himax HM2172 */ + IPU_SENSOR_CONFIG("HIMX2172", 1, 384000000), + /* GalaxyCore GC0310 */ + IPU_SENSOR_CONFIG("INT0310", 0), /* Omnivision OV5693 */ IPU_SENSOR_CONFIG("INT33BE", 1, 419200000), + /* Omnivision OV2740 */ + IPU_SENSOR_CONFIG("INT3474", 1, 180000000), /* Omnivision OV8865 */ IPU_SENSOR_CONFIG("INT347A", 1, 360000000), /* Omnivision OV7251 */ IPU_SENSOR_CONFIG("INT347E", 1, 319200000), + /* Hynix Hi-556 */ + IPU_SENSOR_CONFIG("INT3537", 1, 437000000), + /* Omnivision OV01A10 / OV01A1S */ + IPU_SENSOR_CONFIG("OVTI01A0", 1, 400000000), + IPU_SENSOR_CONFIG("OVTI01AS", 1, 400000000), + /* Omnivision OV02C10 */ + IPU_SENSOR_CONFIG("OVTI02C1", 1, 400000000), + /* Omnivision OV02E10 */ + IPU_SENSOR_CONFIG("OVTI02E1", 1, 360000000), + /* Omnivision OV08A10 */ + IPU_SENSOR_CONFIG("OVTI08A1", 1, 500000000), + /* Omnivision OV08x40 */ + IPU_SENSOR_CONFIG("OVTI08F4", 1, 400000000), + /* Omnivision OV13B10 */ + IPU_SENSOR_CONFIG("OVTI13B1", 1, 560000000), + IPU_SENSOR_CONFIG("OVTIDB10", 1, 560000000), /* Omnivision OV2680 */ IPU_SENSOR_CONFIG("OVTI2680", 1, 331200000), - /* Omnivision ov8856 */ + /* Omnivision OV8856 */ IPU_SENSOR_CONFIG("OVTI8856", 3, 180000000, 360000000, 720000000), - /* Omnivision ov2740 */ - IPU_SENSOR_CONFIG("INT3474", 1, 180000000), - /* Hynix hi556 */ - IPU_SENSOR_CONFIG("INT3537", 1, 437000000), - /* Omnivision ov13b10 */ - IPU_SENSOR_CONFIG("OVTIDB10", 1, 560000000), - /* GalaxyCore GC0310 */ - IPU_SENSOR_CONFIG("INT0310", 0), - /* Omnivision ov01a10 */ - IPU_SENSOR_CONFIG("OVTI01A0", 1, 400000000), }; static const struct ipu_property_names prop_names = { diff --git a/drivers/media/pci/intel/ipu6/ipu6-buttress.c b/drivers/media/pci/intel/ipu6/ipu6-buttress.c index 23c537e7ce1e..e47f84c30e10 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-buttress.c +++ b/drivers/media/pci/intel/ipu6/ipu6-buttress.c @@ -163,8 +163,8 @@ int ipu6_buttress_ipc_reset(struct ipu6_device *isp, writel(ENTRY, isp->base + ipc->csr_out); break; default: - dev_warn_ratelimited(&isp->pdev->dev, - "Unexpected CSR 0x%x\n", val); + dev_dbg_ratelimited(&isp->pdev->dev, + "Unexpected CSR 0x%x\n", val); break; } } while (retries--); diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c index b9ce4324996d..051898ce53f4 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c @@ -345,42 +345,61 @@ static int ipu6_isys_csi2_set_stream(struct v4l2_subdev *sd, return ret; } -static int set_stream(struct v4l2_subdev *sd, int enable) +static int ipu6_isys_csi2_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) { struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd); struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd); - struct device *dev = &csi2->isys->adev->auxdev.dev; struct ipu6_isys_csi2_timing timing = { }; - unsigned int nlanes; + struct v4l2_subdev *remote_sd; + struct media_pad *remote_pad; + u64 sink_streams; int ret; - dev_dbg(dev, "csi2 stream %s callback\n", enable ? "on" : "off"); - - if (!enable) { - csi2->stream_count--; - if (csi2->stream_count) - return 0; - - ipu6_isys_csi2_set_stream(sd, &timing, 0, enable); - return 0; - } - - if (csi2->stream_count) { - csi2->stream_count++; - return 0; - } + remote_pad = media_pad_remote_pad_first(&sd->entity.pads[CSI2_PAD_SINK]); + remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); - nlanes = csi2->nlanes; + sink_streams = v4l2_subdev_state_xlate_streams(state, CSI2_PAD_SRC, + CSI2_PAD_SINK, + &streams_mask); ret = ipu6_isys_csi2_calc_timing(csi2, &timing, CSI2_ACCINV); if (ret) return ret; - ret = ipu6_isys_csi2_set_stream(sd, &timing, nlanes, enable); + ret = ipu6_isys_csi2_set_stream(sd, &timing, csi2->nlanes, true); if (ret) return ret; - csi2->stream_count++; + ret = v4l2_subdev_enable_streams(remote_sd, remote_pad->index, + sink_streams); + if (ret) { + ipu6_isys_csi2_set_stream(sd, NULL, 0, false); + return ret; + } + + return 0; +} + +static int ipu6_isys_csi2_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct v4l2_subdev *remote_sd; + struct media_pad *remote_pad; + u64 sink_streams; + + sink_streams = v4l2_subdev_state_xlate_streams(state, CSI2_PAD_SRC, + CSI2_PAD_SINK, + &streams_mask); + + remote_pad = media_pad_remote_pad_first(&sd->entity.pads[CSI2_PAD_SINK]); + remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); + + ipu6_isys_csi2_set_stream(sd, NULL, 0, false); + + v4l2_subdev_disable_streams(remote_sd, remote_pad->index, sink_streams); return 0; } @@ -475,10 +494,6 @@ static int ipu6_isys_csi2_get_sel(struct v4l2_subdev *sd, return ret; } -static const struct v4l2_subdev_video_ops csi2_sd_video_ops = { - .s_stream = set_stream, -}; - static const struct v4l2_subdev_pad_ops csi2_sd_pad_ops = { .get_fmt = v4l2_subdev_get_fmt, .set_fmt = ipu6_isys_subdev_set_fmt, @@ -486,11 +501,12 @@ static const struct v4l2_subdev_pad_ops csi2_sd_pad_ops = { .set_selection = ipu6_isys_csi2_set_sel, .enum_mbus_code = ipu6_isys_subdev_enum_mbus_code, .set_routing = ipu6_isys_subdev_set_routing, + .enable_streams = ipu6_isys_csi2_enable_streams, + .disable_streams = ipu6_isys_csi2_disable_streams, }; static const struct v4l2_subdev_ops csi2_sd_ops = { .core = &csi2_sd_core_ops, - .video = &csi2_sd_video_ops, .pad = &csi2_sd_pad_ops, }; @@ -631,33 +647,3 @@ int ipu6_isys_csi2_get_remote_desc(u32 source_stream, return 0; } - -void ipu6_isys_set_csi2_streams_status(struct ipu6_isys_video *av, bool status) -{ - struct ipu6_isys_stream *stream = av->stream; - struct v4l2_subdev *sd = &stream->asd->sd; - struct v4l2_subdev_state *state; - struct media_pad *r_pad; - unsigned int i; - u32 r_stream; - - r_pad = media_pad_remote_pad_first(&av->pad); - r_stream = ipu6_isys_get_src_stream_by_src_pad(sd, r_pad->index); - - state = v4l2_subdev_lock_and_get_active_state(sd); - - for (i = 0; i < state->stream_configs.num_configs; i++) { - struct v4l2_subdev_stream_config *cfg = - &state->stream_configs.configs[i]; - - if (cfg->pad == r_pad->index && r_stream == cfg->stream) { - dev_dbg(&av->isys->adev->auxdev.dev, - "%s: pad:%u, stream:%u, status:%u\n", - sd->entity.name, r_pad->index, r_stream, - status); - cfg->enabled = status; - } - } - - v4l2_subdev_unlock_state(state); -} diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h index eba6b29386ea..bc8594c94f99 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h @@ -45,7 +45,6 @@ struct ipu6_isys_csi2 { u32 receiver_errors; unsigned int nlanes; unsigned int port; - unsigned int stream_count; }; struct ipu6_isys_csi2_timing { @@ -77,6 +76,5 @@ int ipu6_isys_csi2_get_remote_desc(u32 source_stream, struct ipu6_isys_csi2 *csi2, struct media_entity *source_entity, struct v4l2_mbus_frame_desc_entry *entry); -void ipu6_isys_set_csi2_streams_status(struct ipu6_isys_video *av, bool status); #endif /* IPU6_ISYS_CSI2_H */ diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c index 4bd4e324abc9..03dbb0e0ea79 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c @@ -551,7 +551,6 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) stream->nr_queues); list_add(&aq->node, &stream->queues); - ipu6_isys_set_csi2_streams_status(av, true); ipu6_isys_configure_stream_watermark(av, true); ipu6_isys_update_stream_watermark(av, true); @@ -598,8 +597,6 @@ static void stop_streaming(struct vb2_queue *q) struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq); struct ipu6_isys_stream *stream = av->stream; - ipu6_isys_set_csi2_streams_status(av, false); - mutex_lock(&stream->mutex); ipu6_isys_update_stream_watermark(av, false); diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c index 06090cc0a476..b37561352ead 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c @@ -990,9 +990,7 @@ int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state, struct v4l2_subdev_state *subdev_state; struct device *dev = &av->isys->adev->auxdev.dev; struct v4l2_subdev *sd; - struct v4l2_subdev *ssd; struct media_pad *r_pad; - struct media_pad *s_pad; u32 sink_pad, sink_stream; u64 r_stream; u64 stream_mask = 0; @@ -1003,7 +1001,6 @@ int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state, if (WARN(!stream->source_entity, "No source entity for stream\n")) return -ENODEV; - ssd = media_entity_to_v4l2_subdev(stream->source_entity); sd = &stream->asd->sd; r_pad = media_pad_remote_pad_first(&av->pad); r_stream = ipu6_isys_get_src_stream_by_src_pad(sd, r_pad->index); @@ -1017,27 +1014,15 @@ int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state, if (ret) return ret; - s_pad = media_pad_remote_pad_first(&stream->asd->pad[sink_pad]); - stream_mask = get_stream_mask_by_pipeline(av); if (!state) { stop_streaming_firmware(av); - /* stop external sub-device now. */ - dev_dbg(dev, "disable streams 0x%llx of %s\n", stream_mask, - ssd->name); - ret = v4l2_subdev_disable_streams(ssd, s_pad->index, - stream_mask); - if (ret) { - dev_err(dev, "disable streams of %s failed with %d\n", - ssd->name, ret); - return ret; - } - /* stop sub-device which connects with video */ - dev_dbg(dev, "stream off entity %s pad:%d\n", sd->name, - r_pad->index); - ret = v4l2_subdev_call(sd, video, s_stream, state); + dev_dbg(dev, "stream off entity %s pad:%d mask:0x%llx\n", + sd->name, r_pad->index, stream_mask); + ret = v4l2_subdev_disable_streams(sd, r_pad->index, + stream_mask); if (ret) { dev_err(dev, "stream off %s failed with %d\n", sd->name, ret); @@ -1052,34 +1037,20 @@ int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state, } /* start sub-device which connects with video */ - dev_dbg(dev, "stream on %s pad %d\n", sd->name, r_pad->index); - ret = v4l2_subdev_call(sd, video, s_stream, state); + dev_dbg(dev, "stream on %s pad %d mask 0x%llx\n", sd->name, + r_pad->index, stream_mask); + ret = v4l2_subdev_enable_streams(sd, r_pad->index, stream_mask); if (ret) { dev_err(dev, "stream on %s failed with %d\n", sd->name, ret); goto out_media_entity_stop_streaming_firmware; } - - /* start external sub-device now. */ - dev_dbg(dev, "enable streams 0x%llx of %s\n", stream_mask, - ssd->name); - ret = v4l2_subdev_enable_streams(ssd, s_pad->index, - stream_mask); - if (ret) { - dev_err(dev, - "enable streams 0x%llx of %s failed with %d\n", - stream_mask, stream->source_entity->name, ret); - goto out_media_entity_stop_streaming; - } } av->streaming = state; return 0; -out_media_entity_stop_streaming: - v4l2_subdev_disable_streams(sd, r_pad->index, BIT(r_stream)); - out_media_entity_stop_streaming_firmware: stop_streaming_firmware(av); diff --git a/drivers/media/pci/intel/ivsc/mei_csi.c b/drivers/media/pci/intel/ivsc/mei_csi.c index f04a89584334..2a9c12c975ca 100644 --- a/drivers/media/pci/intel/ivsc/mei_csi.c +++ b/drivers/media/pci/intel/ivsc/mei_csi.c @@ -126,6 +126,8 @@ struct mei_csi { struct v4l2_ctrl_handler ctrl_handler; struct v4l2_ctrl *freq_ctrl; struct v4l2_ctrl *privacy_ctrl; + /* lock for v4l2 controls */ + struct mutex ctrl_lock; unsigned int remote_pad; /* start streaming or not */ int streaming; @@ -136,9 +138,6 @@ struct mei_csi { u32 nr_of_lanes; /* frequency of the CSI-2 link */ u64 link_freq; - - /* privacy status */ - enum ivsc_privacy_status status; }; static const struct v4l2_mbus_framefmt mei_csi_format_mbus_default = { @@ -190,7 +189,11 @@ static int mei_csi_send(struct mei_csi *csi, u8 *buf, size_t len) /* command response status */ ret = csi->cmd_response.status; - if (ret) { + if (ret == -1) { + /* notify privacy on instead of reporting error */ + ret = 0; + v4l2_ctrl_s_ctrl(csi->privacy_ctrl, 1); + } else if (ret) { ret = -EINVAL; goto out; } @@ -265,10 +268,9 @@ static void mei_csi_rx(struct mei_cl_device *cldev) switch (notif.cmd_id) { case CSI_PRIVACY_NOTIF: - if (notif.cont.cont < CSI_PRIVACY_MAX) { - csi->status = notif.cont.cont; - v4l2_ctrl_s_ctrl(csi->privacy_ctrl, csi->status); - } + if (notif.cont.cont < CSI_PRIVACY_MAX) + v4l2_ctrl_s_ctrl(csi->privacy_ctrl, + notif.cont.cont == CSI_PRIVACY_ON); break; case CSI_SET_OWNER: case CSI_SET_CONF: @@ -559,11 +561,13 @@ static int mei_csi_init_controls(struct mei_csi *csi) u32 max; int ret; + mutex_init(&csi->ctrl_lock); + ret = v4l2_ctrl_handler_init(&csi->ctrl_handler, 2); if (ret) return ret; - csi->ctrl_handler.lock = &csi->lock; + csi->ctrl_handler.lock = &csi->ctrl_lock; max = ARRAY_SIZE(link_freq_menu_items) - 1; csi->freq_ctrl = v4l2_ctrl_new_int_menu(&csi->ctrl_handler, @@ -755,6 +759,7 @@ err_entity: err_ctrl_handler: v4l2_ctrl_handler_free(&csi->ctrl_handler); + mutex_destroy(&csi->ctrl_lock); v4l2_async_nf_unregister(&csi->notifier); v4l2_async_nf_cleanup(&csi->notifier); @@ -774,6 +779,7 @@ static void mei_csi_remove(struct mei_cl_device *cldev) v4l2_async_nf_unregister(&csi->notifier); v4l2_async_nf_cleanup(&csi->notifier); v4l2_ctrl_handler_free(&csi->ctrl_handler); + mutex_destroy(&csi->ctrl_lock); v4l2_async_unregister_subdev(&csi->subdev); v4l2_subdev_cleanup(&csi->subdev); media_entity_cleanup(&csi->subdev.entity); diff --git a/drivers/media/pci/ivtv/ivtv-driver.c b/drivers/media/pci/ivtv/ivtv-driver.c index ba503d820e48..ecc20cd89926 100644 --- a/drivers/media/pci/ivtv/ivtv-driver.c +++ b/drivers/media/pci/ivtv/ivtv-driver.c @@ -371,33 +371,6 @@ int ivtv_msleep_timeout(unsigned int msecs, int intr) return 0; } -/* Release ioremapped memory */ -static void ivtv_iounmap(struct ivtv *itv) -{ - if (itv == NULL) - return; - - /* Release registers memory */ - if (itv->reg_mem != NULL) { - IVTV_DEBUG_INFO("releasing reg_mem\n"); - iounmap(itv->reg_mem); - itv->reg_mem = NULL; - } - /* Release io memory */ - if (itv->has_cx23415 && itv->dec_mem != NULL) { - IVTV_DEBUG_INFO("releasing dec_mem\n"); - iounmap(itv->dec_mem); - } - itv->dec_mem = NULL; - - /* Release io memory */ - if (itv->enc_mem != NULL) { - IVTV_DEBUG_INFO("releasing enc_mem\n"); - iounmap(itv->enc_mem); - itv->enc_mem = NULL; - } -} - /* Hauppauge card? get values from tveeprom */ void ivtv_read_eeprom(struct ivtv *itv, struct tveeprom *tv) { @@ -833,7 +806,7 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *pdev, IVTV_DEBUG_INFO("Enabling pci device\n"); - if (pci_enable_device(pdev)) { + if (pcim_enable_device(pdev)) { IVTV_ERR("Can't enable device!\n"); return -EIO; } @@ -841,24 +814,24 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *pdev, IVTV_ERR("No suitable DMA available.\n"); return -EIO; } - if (!request_mem_region(itv->base_addr, IVTV_ENCODER_SIZE, "ivtv encoder")) { + if (!devm_request_mem_region(&pdev->dev, itv->base_addr, + IVTV_ENCODER_SIZE, "ivtv encoder")) { IVTV_ERR("Cannot request encoder memory region.\n"); return -EIO; } - if (!request_mem_region(itv->base_addr + IVTV_REG_OFFSET, - IVTV_REG_SIZE, "ivtv registers")) { + if (!devm_request_mem_region(&pdev->dev, + itv->base_addr + IVTV_REG_OFFSET, + IVTV_REG_SIZE, "ivtv registers")) { IVTV_ERR("Cannot request register memory region.\n"); - release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); return -EIO; } if (itv->has_cx23415 && - !request_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, - IVTV_DECODER_SIZE, "ivtv decoder")) { + !devm_request_mem_region(&pdev->dev, + itv->base_addr + IVTV_DECODER_OFFSET, + IVTV_DECODER_SIZE, "ivtv decoder")) { IVTV_ERR("Cannot request decoder memory region.\n"); - release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); - release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); return -EIO; } @@ -870,11 +843,6 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *pdev, pci_read_config_word(pdev, PCI_COMMAND, &cmd); if (!(cmd & PCI_COMMAND_MASTER)) { IVTV_ERR("Bus Mastering is not enabled\n"); - if (itv->has_cx23415) - release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, - IVTV_DECODER_SIZE); - release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); - release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); return -ENXIO; } } @@ -1033,37 +1001,37 @@ static int ivtv_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) /* PCI Device Setup */ retval = ivtv_setup_pci(itv, pdev, pci_id); - if (retval == -EIO) + if (retval == -EIO || retval == -ENXIO) goto free_worker; - if (retval == -ENXIO) - goto free_mem; /* map io memory */ IVTV_DEBUG_INFO("attempting ioremap at 0x%llx len 0x%08x\n", (u64)itv->base_addr + IVTV_ENCODER_OFFSET, IVTV_ENCODER_SIZE); - itv->enc_mem = ioremap(itv->base_addr + IVTV_ENCODER_OFFSET, - IVTV_ENCODER_SIZE); + itv->enc_mem = devm_ioremap(&pdev->dev, + itv->base_addr + IVTV_ENCODER_OFFSET, + IVTV_ENCODER_SIZE); if (!itv->enc_mem) { IVTV_ERR("ioremap failed. Can't get a window into CX23415/6 encoder memory\n"); IVTV_ERR("Each capture card with a CX23415/6 needs 8 MB of vmalloc address space for this window\n"); IVTV_ERR("Check the output of 'grep Vmalloc /proc/meminfo'\n"); IVTV_ERR("Use the vmalloc= kernel command line option to set VmallocTotal to a larger value\n"); retval = -ENOMEM; - goto free_mem; + goto free_worker; } if (itv->has_cx23415) { IVTV_DEBUG_INFO("attempting ioremap at 0x%llx len 0x%08x\n", (u64)itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE); - itv->dec_mem = ioremap(itv->base_addr + IVTV_DECODER_OFFSET, - IVTV_DECODER_SIZE); + itv->dec_mem = devm_ioremap(&pdev->dev, + itv->base_addr + IVTV_DECODER_OFFSET, + IVTV_DECODER_SIZE); if (!itv->dec_mem) { IVTV_ERR("ioremap failed. Can't get a window into CX23415 decoder memory\n"); IVTV_ERR("Each capture card with a CX23415 needs 8 MB of vmalloc address space for this window\n"); IVTV_ERR("Check the output of 'grep Vmalloc /proc/meminfo'\n"); IVTV_ERR("Use the vmalloc= kernel command line option to set VmallocTotal to a larger value\n"); retval = -ENOMEM; - goto free_mem; + goto free_worker; } } else { @@ -1073,26 +1041,27 @@ static int ivtv_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) /* map registers memory */ IVTV_DEBUG_INFO("attempting ioremap at 0x%llx len 0x%08x\n", (u64)itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); - itv->reg_mem = - ioremap(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); + itv->reg_mem = devm_ioremap(&pdev->dev, + itv->base_addr + IVTV_REG_OFFSET, + IVTV_REG_SIZE); if (!itv->reg_mem) { IVTV_ERR("ioremap failed. Can't get a window into CX23415/6 register space\n"); IVTV_ERR("Each capture card with a CX23415/6 needs 64 kB of vmalloc address space for this window\n"); IVTV_ERR("Check the output of 'grep Vmalloc /proc/meminfo'\n"); IVTV_ERR("Use the vmalloc= kernel command line option to set VmallocTotal to a larger value\n"); retval = -ENOMEM; - goto free_io; + goto free_worker; } retval = ivtv_gpio_init(itv); if (retval) - goto free_io; + goto free_worker; /* active i2c */ IVTV_DEBUG_INFO("activating i2c...\n"); if (init_ivtv_i2c(itv)) { IVTV_ERR("Could not initialize i2c\n"); - goto free_io; + goto free_worker; } if (itv->card->hw_all & IVTV_HW_TVEEPROM) { @@ -1277,13 +1246,6 @@ free_irq: free_i2c: v4l2_ctrl_handler_free(&itv->cxhdl.hdl); exit_ivtv_i2c(itv); -free_io: - ivtv_iounmap(itv); -free_mem: - release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); - release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); - if (itv->has_cx23415) - release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE); free_worker: kthread_stop(itv->irq_worker_task); err: @@ -1439,14 +1401,7 @@ static void ivtv_remove(struct pci_dev *pdev) exit_ivtv_i2c(itv); free_irq(itv->pdev->irq, (void *)itv); - ivtv_iounmap(itv); - - release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); - release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); - if (itv->has_cx23415) - release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE); - pci_disable_device(itv->pdev); for (i = 0; i < IVTV_VBI_FRAMES; i++) kfree(itv->vbi.sliced_mpeg_data[i]); diff --git a/drivers/media/pci/ivtv/ivtv-fileops.c b/drivers/media/pci/ivtv/ivtv-fileops.c index 4202c3a47d33..cfa28d035586 100644 --- a/drivers/media/pci/ivtv/ivtv-fileops.c +++ b/drivers/media/pci/ivtv/ivtv-fileops.c @@ -21,6 +21,7 @@ #include "ivtv-ioctl.h" #include "ivtv-cards.h" #include "ivtv-firmware.h" +#include <linux/lockdep.h> #include <media/v4l2-event.h> #include <media/i2c/saa7115.h> @@ -190,12 +191,27 @@ static void ivtv_update_pgm_info(struct ivtv *itv) itv->pgm_info_write_idx = (itv->pgm_info_write_idx + i) % itv->pgm_info_num; } +static void ivtv_schedule(struct ivtv_stream *s) +{ + struct ivtv *itv = s->itv; + DEFINE_WAIT(wait); + + lockdep_assert_held(&itv->serialize_lock); + + mutex_unlock(&itv->serialize_lock); + prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE); + /* New buffers might have become free before we were added to the waitqueue */ + if (!s->q_free.buffers) + schedule(); + finish_wait(&s->waitq, &wait); + mutex_lock(&itv->serialize_lock); +} + static struct ivtv_buffer *ivtv_get_buffer(struct ivtv_stream *s, int non_block, int *err) { struct ivtv *itv = s->itv; struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; struct ivtv_buffer *buf; - DEFINE_WAIT(wait); *err = 0; while (1) { @@ -258,13 +274,7 @@ static struct ivtv_buffer *ivtv_get_buffer(struct ivtv_stream *s, int non_block, } /* wait for more data to arrive */ - mutex_unlock(&itv->serialize_lock); - prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE); - /* New buffers might have become available before we were added to the waitqueue */ - if (!s->q_full.buffers) - schedule(); - finish_wait(&s->waitq, &wait); - mutex_lock(&itv->serialize_lock); + ivtv_schedule(s); if (signal_pending(current)) { /* return if a signal was received */ IVTV_DEBUG_INFO("User stopped %s\n", s->name); @@ -533,6 +543,25 @@ int ivtv_start_decoding(struct ivtv_open_id *id, int speed) return 0; } +static int ivtv_schedule_dma(struct ivtv_stream *s) +{ + struct ivtv *itv = s->itv; + int got_sig; + DEFINE_WAIT(wait); + + lockdep_assert_held(&itv->serialize_lock); + + mutex_unlock(&itv->serialize_lock); + prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE); + while (!(got_sig = signal_pending(current)) && + test_bit(IVTV_F_S_DMA_PENDING, &s->s_flags)) + schedule(); + finish_wait(&itv->dma_waitq, &wait); + mutex_lock(&itv->serialize_lock); + + return got_sig; +} + static ssize_t ivtv_write(struct file *filp, const char __user *user_buf, size_t count, loff_t *pos) { struct ivtv_open_id *id = fh2id(filp->private_data); @@ -544,7 +573,6 @@ static ssize_t ivtv_write(struct file *filp, const char __user *user_buf, size_t int bytes_written = 0; int mode; int rc; - DEFINE_WAIT(wait); IVTV_DEBUG_HI_FILE("write %zd bytes to %s\n", count, s->name); @@ -618,13 +646,7 @@ retry: break; if (filp->f_flags & O_NONBLOCK) return -EAGAIN; - mutex_unlock(&itv->serialize_lock); - prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE); - /* New buffers might have become free before we were added to the waitqueue */ - if (!s->q_free.buffers) - schedule(); - finish_wait(&s->waitq, &wait); - mutex_lock(&itv->serialize_lock); + ivtv_schedule(s); if (signal_pending(current)) { IVTV_DEBUG_INFO("User stopped %s\n", s->name); return -EINTR; @@ -674,20 +696,10 @@ retry: if (test_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags)) { if (s->q_full.length >= itv->dma_data_req_size) { - int got_sig; - if (mode == OUT_YUV) ivtv_yuv_setup_stream_frame(itv); - mutex_unlock(&itv->serialize_lock); - prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE); - while (!(got_sig = signal_pending(current)) && - test_bit(IVTV_F_S_DMA_PENDING, &s->s_flags)) { - schedule(); - } - finish_wait(&itv->dma_waitq, &wait); - mutex_lock(&itv->serialize_lock); - if (got_sig) { + if (ivtv_schedule_dma(s)) { IVTV_DEBUG_INFO("User interrupted %s\n", s->name); return -EINTR; } diff --git a/drivers/media/pci/ivtv/ivtv-udma.c b/drivers/media/pci/ivtv/ivtv-udma.c index 99b9f55ca829..f467a00492f4 100644 --- a/drivers/media/pci/ivtv/ivtv-udma.c +++ b/drivers/media/pci/ivtv/ivtv-udma.c @@ -131,6 +131,8 @@ int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr, /* Fill SG List with new values */ if (ivtv_udma_fill_sg_list(dma, &user_dma, 0) < 0) { + IVTV_DEBUG_WARN("%s: could not allocate bounce buffers for highmem userspace buffers\n", + __func__); unpin_user_pages(dma->map, dma->page_count); dma->page_count = 0; return -ENOMEM; @@ -139,6 +141,12 @@ int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr, /* Map SG List */ dma->SG_length = dma_map_sg(&itv->pdev->dev, dma->SGlist, dma->page_count, DMA_TO_DEVICE); + if (!dma->SG_length) { + IVTV_DEBUG_WARN("%s: DMA map error, SG_length is 0\n", __func__); + unpin_user_pages(dma->map, dma->page_count); + dma->page_count = 0; + return -EINVAL; + } /* Fill SG Array with new values */ ivtv_udma_fill_sg_array (dma, ivtv_dest_addr, 0, -1); diff --git a/drivers/media/pci/ivtv/ivtv-yuv.c b/drivers/media/pci/ivtv/ivtv-yuv.c index 582146f8d70d..2d9274537725 100644 --- a/drivers/media/pci/ivtv/ivtv-yuv.c +++ b/drivers/media/pci/ivtv/ivtv-yuv.c @@ -114,6 +114,12 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma, } dma->SG_length = dma_map_sg(&itv->pdev->dev, dma->SGlist, dma->page_count, DMA_TO_DEVICE); + if (!dma->SG_length) { + IVTV_DEBUG_WARN("%s: DMA map error, SG_length is 0\n", __func__); + unpin_user_pages(dma->map, dma->page_count); + dma->page_count = 0; + return -EINVAL; + } /* Fill SG Array with new values */ ivtv_udma_fill_sg_array(dma, y_buffer_offset, uv_buffer_offset, y_size); diff --git a/drivers/media/pci/ivtv/ivtvfb.c b/drivers/media/pci/ivtv/ivtvfb.c index 410477e3e621..90c584cf97c2 100644 --- a/drivers/media/pci/ivtv/ivtvfb.c +++ b/drivers/media/pci/ivtv/ivtvfb.c @@ -104,6 +104,7 @@ MODULE_PARM_DESC(osd_xres, "\t\t\tdefault 640"); MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil, John Harvey, Ian Armstrong"); +MODULE_DESCRIPTION("Conexant cx23415 framebuffer support"); MODULE_LICENSE("GPL"); /* --------------------------------------------------------------------- */ @@ -281,10 +282,10 @@ static int ivtvfb_prep_dec_dma_to_device(struct ivtv *itv, /* Map User DMA */ if (ivtv_udma_setup(itv, ivtv_dest_addr, userbuf, size_in_bytes) <= 0) { mutex_unlock(&itv->udma.lock); - IVTVFB_WARN("ivtvfb_prep_dec_dma_to_device, Error with pin_user_pages: %d bytes, %d pages returned\n", - size_in_bytes, itv->udma.page_count); + IVTVFB_WARN("%s, Error in ivtv_udma_setup: %d bytes, %d pages returned\n", + __func__, size_in_bytes, itv->udma.page_count); - /* pin_user_pages must have failed completely */ + /* pin_user_pages or DMA must have failed completely */ return -EIO; } diff --git a/drivers/media/pci/mantis/hopper_cards.c b/drivers/media/pci/mantis/hopper_cards.c index c0bd5d7e148b..b85aef4e2b24 100644 --- a/drivers/media/pci/mantis/hopper_cards.c +++ b/drivers/media/pci/mantis/hopper_cards.c @@ -116,7 +116,7 @@ static irqreturn_t hopper_irq_handler(int irq, void *dev_id) if (stat & MANTIS_INT_RISCI) { dprintk(MANTIS_DEBUG, 0, "<%s>", label[8]); mantis->busy_block = (stat & MANTIS_INT_RISCSTAT) >> 28; - tasklet_schedule(&mantis->tasklet); + queue_work(system_bh_wq, &mantis->bh_work); } if (stat & MANTIS_INT_I2CDONE) { dprintk(MANTIS_DEBUG, 0, "<%s>", label[9]); diff --git a/drivers/media/pci/mantis/mantis_cards.c b/drivers/media/pci/mantis/mantis_cards.c index 906e4500d87d..b44b4cf42f86 100644 --- a/drivers/media/pci/mantis/mantis_cards.c +++ b/drivers/media/pci/mantis/mantis_cards.c @@ -125,7 +125,7 @@ static irqreturn_t mantis_irq_handler(int irq, void *dev_id) if (stat & MANTIS_INT_RISCI) { dprintk(MANTIS_DEBUG, 0, "<%s>", label[8]); mantis->busy_block = (stat & MANTIS_INT_RISCSTAT) >> 28; - tasklet_schedule(&mantis->tasklet); + queue_work(system_bh_wq, &mantis->bh_work); } if (stat & MANTIS_INT_I2CDONE) { dprintk(MANTIS_DEBUG, 0, "<%s>", label[9]); diff --git a/drivers/media/pci/mantis/mantis_common.h b/drivers/media/pci/mantis/mantis_common.h index d88ac280226c..6e563ecd94e8 100644 --- a/drivers/media/pci/mantis/mantis_common.h +++ b/drivers/media/pci/mantis/mantis_common.h @@ -125,7 +125,7 @@ struct mantis_pci { __le32 *risc_cpu; dma_addr_t risc_dma; - struct tasklet_struct tasklet; + struct work_struct bh_work; spinlock_t intmask_lock; struct i2c_adapter adapter; diff --git a/drivers/media/pci/mantis/mantis_dma.c b/drivers/media/pci/mantis/mantis_dma.c index 80c843936493..eeb210a2862f 100644 --- a/drivers/media/pci/mantis/mantis_dma.c +++ b/drivers/media/pci/mantis/mantis_dma.c @@ -200,9 +200,9 @@ void mantis_dma_stop(struct mantis_pci *mantis) } -void mantis_dma_xfer(struct tasklet_struct *t) +void mantis_dma_xfer(struct work_struct *t) { - struct mantis_pci *mantis = from_tasklet(mantis, t, tasklet); + struct mantis_pci *mantis = from_work(mantis, t, bh_work); struct mantis_hwconfig *config = mantis->hwconfig; while (mantis->last_block != mantis->busy_block) { diff --git a/drivers/media/pci/mantis/mantis_dma.h b/drivers/media/pci/mantis/mantis_dma.h index 37da982c9c29..5db0d3728f15 100644 --- a/drivers/media/pci/mantis/mantis_dma.h +++ b/drivers/media/pci/mantis/mantis_dma.h @@ -13,6 +13,6 @@ extern int mantis_dma_init(struct mantis_pci *mantis); extern int mantis_dma_exit(struct mantis_pci *mantis); extern void mantis_dma_start(struct mantis_pci *mantis); extern void mantis_dma_stop(struct mantis_pci *mantis); -extern void mantis_dma_xfer(struct tasklet_struct *t); +extern void mantis_dma_xfer(struct work_struct *t); #endif /* __MANTIS_DMA_H */ diff --git a/drivers/media/pci/mantis/mantis_dvb.c b/drivers/media/pci/mantis/mantis_dvb.c index c7ba4a76e608..398e32f44692 100644 --- a/drivers/media/pci/mantis/mantis_dvb.c +++ b/drivers/media/pci/mantis/mantis_dvb.c @@ -105,7 +105,7 @@ static int mantis_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed) if (mantis->feeds == 1) { dprintk(MANTIS_DEBUG, 1, "mantis start feed & dma"); mantis_dma_start(mantis); - tasklet_enable(&mantis->tasklet); + enable_and_queue_work(system_bh_wq, &mantis->bh_work); } return mantis->feeds; @@ -125,7 +125,7 @@ static int mantis_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) mantis->feeds--; if (mantis->feeds == 0) { dprintk(MANTIS_DEBUG, 1, "mantis stop feed and dma"); - tasklet_disable(&mantis->tasklet); + disable_work_sync(&mantis->bh_work); mantis_dma_stop(mantis); } @@ -205,8 +205,8 @@ int mantis_dvb_init(struct mantis_pci *mantis) } dvb_net_init(&mantis->dvb_adapter, &mantis->dvbnet, &mantis->demux.dmx); - tasklet_setup(&mantis->tasklet, mantis_dma_xfer); - tasklet_disable(&mantis->tasklet); + INIT_WORK(&mantis->bh_work, mantis_dma_xfer); + disable_work_sync(&mantis->bh_work); if (mantis->hwconfig) { result = config->frontend_init(mantis, mantis->fe); if (result < 0) { @@ -235,7 +235,7 @@ int mantis_dvb_init(struct mantis_pci *mantis) /* Error conditions .. */ err5: - tasklet_kill(&mantis->tasklet); + cancel_work_sync(&mantis->bh_work); dvb_net_release(&mantis->dvbnet); if (mantis->fe) { dvb_unregister_frontend(mantis->fe); @@ -273,7 +273,7 @@ int mantis_dvb_exit(struct mantis_pci *mantis) dvb_frontend_detach(mantis->fe); } - tasklet_kill(&mantis->tasklet); + cancel_work_sync(&mantis->bh_work); dvb_net_release(&mantis->dvbnet); mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_mem); diff --git a/drivers/media/pci/ngene/ngene-core.c b/drivers/media/pci/ngene/ngene-core.c index 24ec576dc3bf..db6796240bce 100644 --- a/drivers/media/pci/ngene/ngene-core.c +++ b/drivers/media/pci/ngene/ngene-core.c @@ -50,9 +50,9 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); /* nGene interrupt handler **************************************************/ /****************************************************************************/ -static void event_tasklet(struct tasklet_struct *t) +static void event_bh_work(struct work_struct *t) { - struct ngene *dev = from_tasklet(dev, t, event_tasklet); + struct ngene *dev = from_work(dev, t, event_bh_work); while (dev->EventQueueReadIndex != dev->EventQueueWriteIndex) { struct EVENT_BUFFER Event = @@ -68,9 +68,9 @@ static void event_tasklet(struct tasklet_struct *t) } } -static void demux_tasklet(struct tasklet_struct *t) +static void demux_bh_work(struct work_struct *t) { - struct ngene_channel *chan = from_tasklet(chan, t, demux_tasklet); + struct ngene_channel *chan = from_work(chan, t, demux_bh_work); struct device *pdev = &chan->dev->pci_dev->dev; struct SBufferHeader *Cur = chan->nextBuffer; @@ -204,7 +204,7 @@ static irqreturn_t irq_handler(int irq, void *dev_id) dev->EventQueueOverflowFlag = 1; } dev->EventBuffer->EventStatus &= ~0x80; - tasklet_schedule(&dev->event_tasklet); + queue_work(system_bh_wq, &dev->event_bh_work); rc = IRQ_HANDLED; } @@ -217,8 +217,8 @@ static irqreturn_t irq_handler(int irq, void *dev_id) ngeneBuffer.SR.Flags & 0xC0) == 0x80) { dev->channel[i].nextBuffer-> ngeneBuffer.SR.Flags |= 0x40; - tasklet_schedule( - &dev->channel[i].demux_tasklet); + queue_work(system_bh_wq, + &dev->channel[i].demux_bh_work); rc = IRQ_HANDLED; } } @@ -1181,7 +1181,7 @@ static void ngene_init(struct ngene *dev) struct device *pdev = &dev->pci_dev->dev; int i; - tasklet_setup(&dev->event_tasklet, event_tasklet); + INIT_WORK(&dev->event_bh_work, event_bh_work); memset_io(dev->iomem + 0xc000, 0x00, 0x220); memset_io(dev->iomem + 0xc400, 0x00, 0x100); @@ -1395,7 +1395,7 @@ static void release_channel(struct ngene_channel *chan) if (chan->running) set_transfer(chan, 0); - tasklet_kill(&chan->demux_tasklet); + cancel_work_sync(&chan->demux_bh_work); if (chan->ci_dev) { dvb_unregister_device(chan->ci_dev); @@ -1445,7 +1445,7 @@ static int init_channel(struct ngene_channel *chan) struct ngene_info *ni = dev->card_info; int io = ni->io_type[nr]; - tasklet_setup(&chan->demux_tasklet, demux_tasklet); + INIT_WORK(&chan->demux_bh_work, demux_bh_work); chan->users = 0; chan->type = io; chan->mode = chan->type; /* for now only one mode */ @@ -1649,7 +1649,7 @@ void ngene_remove(struct pci_dev *pdev) struct ngene *dev = pci_get_drvdata(pdev); int i; - tasklet_kill(&dev->event_tasklet); + cancel_work_sync(&dev->event_bh_work); for (i = MAX_STREAM - 1; i >= 0; i--) release_channel(&dev->channel[i]); if (dev->ci.en) diff --git a/drivers/media/pci/ngene/ngene.h b/drivers/media/pci/ngene/ngene.h index d1d7da84cd9d..9f989420b9e7 100644 --- a/drivers/media/pci/ngene/ngene.h +++ b/drivers/media/pci/ngene/ngene.h @@ -16,6 +16,7 @@ #include <linux/scatterlist.h> #include <linux/dvb/frontend.h> +#include <linux/workqueue.h> #include <media/dmxdev.h> #include <media/dvbdev.h> @@ -621,7 +622,7 @@ struct ngene_channel { int users; struct video_device *v4l_dev; struct dvb_device *ci_dev; - struct tasklet_struct demux_tasklet; + struct work_struct demux_bh_work; struct SBufferHeader *nextBuffer; enum KSSTATE State; @@ -717,7 +718,7 @@ struct ngene { struct EVENT_BUFFER EventQueue[EVENT_QUEUE_SIZE]; int EventQueueOverflowCount; int EventQueueOverflowFlag; - struct tasklet_struct event_tasklet; + struct work_struct event_bh_work; struct EVENT_BUFFER *EventBuffer; int EventQueueWriteIndex; int EventQueueReadIndex; diff --git a/drivers/media/pci/saa7134/saa7134-alsa.c b/drivers/media/pci/saa7134/saa7134-alsa.c index dd2236c5c4a1..f86a44dfe6e3 100644 --- a/drivers/media/pci/saa7134/saa7134-alsa.c +++ b/drivers/media/pci/saa7134/saa7134-alsa.c @@ -1254,5 +1254,6 @@ static void saa7134_alsa_exit(void) /* We initialize this late, to make sure the sound system is up and running */ late_initcall(saa7134_alsa_init); module_exit(saa7134_alsa_exit); +MODULE_DESCRIPTION("Philips SAA7134 DMA audio support"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ricardo Cerqueira"); diff --git a/drivers/media/pci/saa7134/saa7134-dvb.c b/drivers/media/pci/saa7134/saa7134-dvb.c index 9c6cfef03331..6de8a02314af 100644 --- a/drivers/media/pci/saa7134/saa7134-dvb.c +++ b/drivers/media/pci/saa7134/saa7134-dvb.c @@ -52,6 +52,7 @@ #include "s5h1411.h" MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); +MODULE_DESCRIPTION("DVB/ATSC Support for saa7134 based TV cards"); MODULE_LICENSE("GPL"); static unsigned int antenna_pwr; @@ -466,7 +467,9 @@ static int philips_europa_tuner_sleep(struct dvb_frontend *fe) /* switch the board to analog mode */ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - i2c_transfer(&dev->i2c_adap, &analog_msg, 1); + if (i2c_transfer(&dev->i2c_adap, &analog_msg, 1) != 1) + return -EIO; + return 0; } @@ -1018,7 +1021,9 @@ static int md8800_set_voltage2(struct dvb_frontend *fe, else wbuf[1] = rbuf & 0xef; msg[0].len = 2; - i2c_transfer(&dev->i2c_adap, msg, 1); + if (i2c_transfer(&dev->i2c_adap, msg, 1) != 1) + return -EIO; + return 0; } diff --git a/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c index 434fa1ee1c33..bbf480ab31ca 100644 --- a/drivers/media/pci/saa7134/saa7134-empress.c +++ b/drivers/media/pci/saa7134/saa7134-empress.c @@ -19,6 +19,7 @@ /* ------------------------------------------------------------------ */ MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); +MODULE_DESCRIPTION("Philips SAA7134 empress support"); MODULE_LICENSE("GPL"); static unsigned int empress_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET }; diff --git a/drivers/media/pci/saa7134/saa7134-go7007.c b/drivers/media/pci/saa7134/saa7134-go7007.c index da83893ffee9..bd37db5ce363 100644 --- a/drivers/media/pci/saa7134/saa7134-go7007.c +++ b/drivers/media/pci/saa7134/saa7134-go7007.c @@ -516,4 +516,5 @@ static void __exit saa7134_go7007_mod_cleanup(void) module_init(saa7134_go7007_mod_init); module_exit(saa7134_go7007_mod_cleanup); +MODULE_DESCRIPTION("go7007 support for saa7134 based TV cards"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/pci/smipcie/smipcie-main.c b/drivers/media/pci/smipcie/smipcie-main.c index 0c300d019d9c..7db6d443fc54 100644 --- a/drivers/media/pci/smipcie/smipcie-main.c +++ b/drivers/media/pci/smipcie/smipcie-main.c @@ -279,10 +279,10 @@ static void smi_port_clearInterrupt(struct smi_port *port) (port->_dmaInterruptCH0 | port->_dmaInterruptCH1)); } -/* tasklet handler: DMA data to dmx.*/ -static void smi_dma_xfer(struct tasklet_struct *t) +/* BH work handler: DMA data to dmx.*/ +static void smi_dma_xfer(struct work_struct *t) { - struct smi_port *port = from_tasklet(port, t, tasklet); + struct smi_port *port = from_work(port, t, bh_work); struct smi_dev *dev = port->dev; u32 intr_status, finishedData, dmaManagement; u8 dmaChan0State, dmaChan1State; @@ -426,8 +426,8 @@ static int smi_port_init(struct smi_port *port, int dmaChanUsed) } smi_port_disableInterrupt(port); - tasklet_setup(&port->tasklet, smi_dma_xfer); - tasklet_disable(&port->tasklet); + INIT_WORK(&port->bh_work, smi_dma_xfer); + disable_work_sync(&port->bh_work); port->enable = 1; return 0; err: @@ -438,7 +438,7 @@ err: static void smi_port_exit(struct smi_port *port) { smi_port_disableInterrupt(port); - tasklet_kill(&port->tasklet); + cancel_work_sync(&port->bh_work); smi_port_dma_free(port); port->enable = 0; } @@ -452,7 +452,7 @@ static int smi_port_irq(struct smi_port *port, u32 int_status) smi_port_disableInterrupt(port); port->_int_status = int_status; smi_port_clearInterrupt(port); - tasklet_schedule(&port->tasklet); + queue_work(system_bh_wq, &port->bh_work); handled = 1; } return handled; @@ -823,7 +823,7 @@ static int smi_start_feed(struct dvb_demux_feed *dvbdmxfeed) smi_port_clearInterrupt(port); smi_port_enableInterrupt(port); smi_write(port->DMA_MANAGEMENT, dmaManagement); - tasklet_enable(&port->tasklet); + enable_and_queue_work(system_bh_wq, &port->bh_work); } return port->users; } @@ -837,7 +837,7 @@ static int smi_stop_feed(struct dvb_demux_feed *dvbdmxfeed) if (--port->users) return port->users; - tasklet_disable(&port->tasklet); + disable_work_sync(&port->bh_work); smi_port_disableInterrupt(port); smi_clear(port->DMA_MANAGEMENT, 0x30003); return 0; diff --git a/drivers/media/pci/smipcie/smipcie.h b/drivers/media/pci/smipcie/smipcie.h index 2b5e0154814c..98e44edaab9e 100644 --- a/drivers/media/pci/smipcie/smipcie.h +++ b/drivers/media/pci/smipcie/smipcie.h @@ -17,6 +17,7 @@ #include <linux/pci.h> #include <linux/dma-mapping.h> #include <linux/slab.h> +#include <linux/workqueue.h> #include <media/rc-core.h> #include <media/demux.h> @@ -257,7 +258,7 @@ struct smi_port { u32 _dmaInterruptCH0; u32 _dmaInterruptCH1; u32 _int_status; - struct tasklet_struct tasklet; + struct work_struct bh_work; /* dvb */ struct dmx_frontend hw_frontend; struct dmx_frontend mem_frontend; diff --git a/drivers/media/pci/ttpci/budget-av.c b/drivers/media/pci/ttpci/budget-av.c index 2e62c938e2cb..69f5e810f9b5 100644 --- a/drivers/media/pci/ttpci/budget-av.c +++ b/drivers/media/pci/ttpci/budget-av.c @@ -36,6 +36,7 @@ #include <linux/interrupt.h> #include <linux/input.h> #include <linux/spinlock.h> +#include <linux/workqueue.h> #include <media/dvb_ca_en50221.h> @@ -54,7 +55,7 @@ struct budget_av { struct video_device vd; int cur_input; int has_saa7113; - struct tasklet_struct ciintf_irq_tasklet; + struct work_struct ciintf_irq_bh_work; int slot_status; struct dvb_ca_en50221 ca; u8 reinitialise_demod:1; diff --git a/drivers/media/pci/ttpci/budget-ci.c b/drivers/media/pci/ttpci/budget-ci.c index 76de40e3c802..33f08adf4feb 100644 --- a/drivers/media/pci/ttpci/budget-ci.c +++ b/drivers/media/pci/ttpci/budget-ci.c @@ -18,6 +18,7 @@ #include <linux/slab.h> #include <linux/interrupt.h> #include <linux/spinlock.h> +#include <linux/workqueue.h> #include <media/rc-core.h> #include "budget.h" @@ -81,7 +82,7 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); struct budget_ci_ir { struct rc_dev *dev; - struct tasklet_struct msp430_irq_tasklet; + struct work_struct msp430_irq_bh_work; char name[72]; /* 40 + 32 for (struct saa7146_dev).name */ char phys[32]; int rc5_device; @@ -92,7 +93,7 @@ struct budget_ci_ir { struct budget_ci { struct budget budget; - struct tasklet_struct ciintf_irq_tasklet; + struct work_struct ciintf_irq_bh_work; int slot_status; int ci_irq; struct dvb_ca_en50221 ca; @@ -100,9 +101,9 @@ struct budget_ci { u8 tuner_pll_address; /* used for philips_tdm1316l configs */ }; -static void msp430_ir_interrupt(struct tasklet_struct *t) +static void msp430_ir_interrupt(struct work_struct *t) { - struct budget_ci_ir *ir = from_tasklet(ir, t, msp430_irq_tasklet); + struct budget_ci_ir *ir = from_work(ir, t, msp430_irq_bh_work); struct budget_ci *budget_ci = container_of(ir, typeof(*budget_ci), ir); struct rc_dev *dev = budget_ci->ir.dev; u32 command = ttpci_budget_debiread(&budget_ci->budget, DEBINOSWAP, DEBIADDR_IR, 2, 1, 0) >> 8; @@ -231,7 +232,7 @@ static int msp430_ir_init(struct budget_ci *budget_ci) budget_ci->ir.dev = dev; - tasklet_setup(&budget_ci->ir.msp430_irq_tasklet, msp430_ir_interrupt); + INIT_WORK(&budget_ci->ir.msp430_irq_bh_work, msp430_ir_interrupt); SAA7146_IER_ENABLE(saa, MASK_06); saa7146_setgpio(saa, 3, SAA7146_GPIO_IRQHI); @@ -245,7 +246,7 @@ static void msp430_ir_deinit(struct budget_ci *budget_ci) SAA7146_IER_DISABLE(saa, MASK_06); saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); - tasklet_kill(&budget_ci->ir.msp430_irq_tasklet); + cancel_work_sync(&budget_ci->ir.msp430_irq_bh_work); rc_unregister_device(budget_ci->ir.dev); } @@ -349,10 +350,10 @@ static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) return 0; } -static void ciintf_interrupt(struct tasklet_struct *t) +static void ciintf_interrupt(struct work_struct *t) { - struct budget_ci *budget_ci = from_tasklet(budget_ci, t, - ciintf_irq_tasklet); + struct budget_ci *budget_ci = from_work(budget_ci, t, + ciintf_irq_bh_work); struct saa7146_dev *saa = budget_ci->budget.dev; unsigned int flags; @@ -491,7 +492,7 @@ static int ciintf_init(struct budget_ci *budget_ci) // Setup CI slot IRQ if (budget_ci->ci_irq) { - tasklet_setup(&budget_ci->ciintf_irq_tasklet, ciintf_interrupt); + INIT_WORK(&budget_ci->ciintf_irq_bh_work, ciintf_interrupt); if (budget_ci->slot_status != SLOTSTATUS_NONE) saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO); else @@ -530,7 +531,7 @@ static void ciintf_deinit(struct budget_ci *budget_ci) if (budget_ci->ci_irq) { SAA7146_IER_DISABLE(saa, MASK_03); saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT); - tasklet_kill(&budget_ci->ciintf_irq_tasklet); + cancel_work_sync(&budget_ci->ciintf_irq_bh_work); } // reset interface @@ -556,13 +557,13 @@ static void budget_ci_irq(struct saa7146_dev *dev, u32 *isr) dprintk(8, "dev: %p, budget_ci: %p\n", dev, budget_ci); if (*isr & MASK_06) - tasklet_schedule(&budget_ci->ir.msp430_irq_tasklet); + queue_work(system_bh_wq, &budget_ci->ir.msp430_irq_bh_work); if (*isr & MASK_10) ttpci_budget_irq10_handler(dev, isr); if ((*isr & MASK_03) && (budget_ci->budget.ci_present) && (budget_ci->ci_irq)) - tasklet_schedule(&budget_ci->ciintf_irq_tasklet); + queue_work(system_bh_wq, &budget_ci->ciintf_irq_bh_work); } static u8 philips_su1278_tt_inittab[] = { diff --git a/drivers/media/pci/ttpci/budget-core.c b/drivers/media/pci/ttpci/budget-core.c index d33adeca196f..29531d9c9db0 100644 --- a/drivers/media/pci/ttpci/budget-core.c +++ b/drivers/media/pci/ttpci/budget-core.c @@ -172,9 +172,9 @@ static int budget_read_fe_status(struct dvb_frontend *fe, return ret; } -static void vpeirq(struct tasklet_struct *t) +static void vpeirq(struct work_struct *t) { - struct budget *budget = from_tasklet(budget, t, vpe_tasklet); + struct budget *budget = from_work(budget, t, vpe_bh_work); u8 *mem = (u8 *) (budget->grabbing); u32 olddma = budget->ttbp; u32 newdma = saa7146_read(budget->dev, PCI_VDP3); @@ -525,7 +525,7 @@ int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, /* upload all */ saa7146_write(dev, GPIO_CTRL, 0x000000); - tasklet_setup(&budget->vpe_tasklet, vpeirq); + INIT_WORK(&budget->vpe_bh_work, vpeirq); /* frontend power on */ if (bi->type != BUDGET_FS_ACTIVY) @@ -565,7 +565,7 @@ int ttpci_budget_deinit(struct budget *budget) budget_unregister(budget); - tasklet_kill(&budget->vpe_tasklet); + cancel_work_sync(&budget->vpe_bh_work); saa7146_vfree_destroy_pgtable(dev->pci, budget->grabbing, &budget->pt); @@ -584,7 +584,7 @@ void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 *isr) dprintk(8, "dev: %p, budget: %p\n", dev, budget); if (*isr & MASK_10) - tasklet_schedule(&budget->vpe_tasklet); + queue_work(system_bh_wq, &budget->vpe_bh_work); } EXPORT_SYMBOL_GPL(ttpci_budget_irq10_handler); @@ -602,4 +602,5 @@ void ttpci_budget_set_video_port(struct saa7146_dev *dev, int video_port) } EXPORT_SYMBOL_GPL(ttpci_budget_set_video_port); +MODULE_DESCRIPTION("base driver for the SAA7146 based Budget DVB cards"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/ttpci/budget.h b/drivers/media/pci/ttpci/budget.h index 83ead34dc766..e933764ac4e3 100644 --- a/drivers/media/pci/ttpci/budget.h +++ b/drivers/media/pci/ttpci/budget.h @@ -18,6 +18,7 @@ #include <linux/module.h> #include <linux/mutex.h> +#include <linux/workqueue.h> #include <media/drv-intf/saa7146.h> @@ -54,8 +55,8 @@ struct budget { unsigned char *grabbing; struct saa7146_pgtable pt; - struct tasklet_struct fidb_tasklet; - struct tasklet_struct vpe_tasklet; + struct work_struct fidb_bh_work; + struct work_struct vpe_bh_work; struct dmxdev dmxdev; struct dvb_demux demux; diff --git a/drivers/media/pci/tw5864/tw5864-core.c b/drivers/media/pci/tw5864/tw5864-core.c index 560ff1ddcc83..4d33caf83307 100644 --- a/drivers/media/pci/tw5864/tw5864-core.c +++ b/drivers/media/pci/tw5864/tw5864-core.c @@ -144,7 +144,7 @@ static void tw5864_h264_isr(struct tw5864_dev *dev) cur_frame->gop_seqno = input->frame_gop_seqno; dev->h264_buf_w_index = next_frame_index; - tasklet_schedule(&dev->tasklet); + queue_work(system_bh_wq, &dev->bh_work); cur_frame = next_frame; diff --git a/drivers/media/pci/tw5864/tw5864-video.c b/drivers/media/pci/tw5864/tw5864-video.c index 8b1aae4b6319..4f35c159efe5 100644 --- a/drivers/media/pci/tw5864/tw5864-video.c +++ b/drivers/media/pci/tw5864/tw5864-video.c @@ -6,6 +6,7 @@ */ #include <linux/module.h> +#include <linux/workqueue.h> #include <media/v4l2-common.h> #include <media/v4l2-event.h> #include <media/videobuf2-dma-contig.h> @@ -175,7 +176,7 @@ static const unsigned int intra4x4_lambda3[] = { static v4l2_std_id tw5864_get_v4l2_std(enum tw5864_vid_std std); static enum tw5864_vid_std tw5864_from_v4l2_std(v4l2_std_id v4l2_std); -static void tw5864_handle_frame_task(struct tasklet_struct *t); +static void tw5864_handle_frame_work(struct work_struct *t); static void tw5864_handle_frame(struct tw5864_h264_frame *frame); static void tw5864_frame_interval_set(struct tw5864_input *input); @@ -1062,7 +1063,7 @@ int tw5864_video_init(struct tw5864_dev *dev, int *video_nr) dev->irqmask |= TW5864_INTR_VLC_DONE | TW5864_INTR_TIMER; tw5864_irqmask_apply(dev); - tasklet_setup(&dev->tasklet, tw5864_handle_frame_task); + INIT_WORK(&dev->bh_work, tw5864_handle_frame_work); for (i = 0; i < TW5864_INPUTS; i++) { dev->inputs[i].root = dev; @@ -1079,7 +1080,7 @@ fini_video_inputs: for (i = last_input_nr_registered; i >= 0; i--) tw5864_video_input_fini(&dev->inputs[i]); - tasklet_kill(&dev->tasklet); + cancel_work_sync(&dev->bh_work); free_dma: for (i = last_dma_allocated; i >= 0; i--) { @@ -1198,7 +1199,7 @@ void tw5864_video_fini(struct tw5864_dev *dev) { int i; - tasklet_kill(&dev->tasklet); + cancel_work_sync(&dev->bh_work); for (i = 0; i < TW5864_INPUTS; i++) tw5864_video_input_fini(&dev->inputs[i]); @@ -1315,9 +1316,9 @@ static int tw5864_is_motion_triggered(struct tw5864_h264_frame *frame) return detected; } -static void tw5864_handle_frame_task(struct tasklet_struct *t) +static void tw5864_handle_frame_work(struct work_struct *t) { - struct tw5864_dev *dev = from_tasklet(dev, t, tasklet); + struct tw5864_dev *dev = from_work(dev, t, bh_work); unsigned long flags; int batch_size = H264_BUF_CNT; diff --git a/drivers/media/pci/tw5864/tw5864.h b/drivers/media/pci/tw5864/tw5864.h index a8b6fbd5b710..2da5f4215fd9 100644 --- a/drivers/media/pci/tw5864/tw5864.h +++ b/drivers/media/pci/tw5864/tw5864.h @@ -12,6 +12,7 @@ #include <linux/mutex.h> #include <linux/io.h> #include <linux/interrupt.h> +#include <linux/workqueue.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> @@ -85,7 +86,7 @@ struct tw5864_input { int nr; /* input number */ struct tw5864_dev *root; struct mutex lock; /* used for vidq and vdev */ - spinlock_t slock; /* used for sync between ISR, tasklet & V4L2 API */ + spinlock_t slock; /* used for sync between ISR, bh_work & V4L2 API */ struct video_device vdev; struct v4l2_ctrl_handler hdl; struct vb2_queue vidq; @@ -142,7 +143,7 @@ struct tw5864_h264_frame { /* global device status */ struct tw5864_dev { - spinlock_t slock; /* used for sync between ISR, tasklet & V4L2 API */ + spinlock_t slock; /* used for sync between ISR, bh_work & V4L2 API */ struct v4l2_device v4l2_dev; struct tw5864_input inputs[TW5864_INPUTS]; #define H264_BUF_CNT 4 @@ -150,7 +151,7 @@ struct tw5864_dev { int h264_buf_r_index; int h264_buf_w_index; - struct tasklet_struct tasklet; + struct work_struct bh_work; int encoder_busy; /* Input number to check next for ready raw picture (in RR fashion) */ diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 2d79bfc68c15..85d2627776b6 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -70,6 +70,7 @@ source "drivers/media/platform/atmel/Kconfig" source "drivers/media/platform/broadcom/Kconfig" source "drivers/media/platform/cadence/Kconfig" source "drivers/media/platform/chips-media/Kconfig" +source "drivers/media/platform/imagination/Kconfig" source "drivers/media/platform/intel/Kconfig" source "drivers/media/platform/marvell/Kconfig" source "drivers/media/platform/mediatek/Kconfig" @@ -78,6 +79,7 @@ source "drivers/media/platform/nuvoton/Kconfig" source "drivers/media/platform/nvidia/Kconfig" source "drivers/media/platform/nxp/Kconfig" source "drivers/media/platform/qcom/Kconfig" +source "drivers/media/platform/raspberrypi/Kconfig" source "drivers/media/platform/renesas/Kconfig" source "drivers/media/platform/rockchip/Kconfig" source "drivers/media/platform/samsung/Kconfig" diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index da17301f7439..ace4e34483dd 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -13,6 +13,7 @@ obj-y += atmel/ obj-y += broadcom/ obj-y += cadence/ obj-y += chips-media/ +obj-y += imagination/ obj-y += intel/ obj-y += marvell/ obj-y += mediatek/ @@ -21,6 +22,7 @@ obj-y += nuvoton/ obj-y += nvidia/ obj-y += nxp/ obj-y += qcom/ +obj-y += raspberrypi/ obj-y += renesas/ obj-y += rockchip/ obj-y += samsung/ diff --git a/drivers/media/platform/allegro-dvt/nal-hevc.h b/drivers/media/platform/allegro-dvt/nal-hevc.h index eb46f12aae80..361e2f55c254 100644 --- a/drivers/media/platform/allegro-dvt/nal-hevc.h +++ b/drivers/media/platform/allegro-dvt/nal-hevc.h @@ -96,10 +96,11 @@ struct nal_hevc_vps { unsigned int extension_data_flag; }; +#define N_HRD_PARAMS 1 struct nal_hevc_sub_layer_hrd_parameters { - unsigned int bit_rate_value_minus1[1]; - unsigned int cpb_size_value_minus1[1]; - unsigned int cbr_flag[1]; + unsigned int bit_rate_value_minus1[N_HRD_PARAMS]; + unsigned int cpb_size_value_minus1[N_HRD_PARAMS]; + unsigned int cbr_flag[N_HRD_PARAMS]; }; struct nal_hevc_hrd_parameters { diff --git a/drivers/media/platform/amphion/vdec.c b/drivers/media/platform/amphion/vdec.c index a57f9f4f3b87..6a38a0fa0e2d 100644 --- a/drivers/media/platform/amphion/vdec.c +++ b/drivers/media/platform/amphion/vdec.c @@ -195,7 +195,6 @@ static int vdec_op_s_ctrl(struct v4l2_ctrl *ctrl) struct vdec_t *vdec = inst->priv; int ret = 0; - vpu_inst_lock(inst); switch (ctrl->id) { case V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY_ENABLE: vdec->params.display_delay_enable = ctrl->val; @@ -207,7 +206,6 @@ static int vdec_op_s_ctrl(struct v4l2_ctrl *ctrl) ret = -EINVAL; break; } - vpu_inst_unlock(inst); return ret; } diff --git a/drivers/media/platform/amphion/venc.c b/drivers/media/platform/amphion/venc.c index 4eb57d793a9c..351b4edc8742 100644 --- a/drivers/media/platform/amphion/venc.c +++ b/drivers/media/platform/amphion/venc.c @@ -518,7 +518,6 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) struct venc_t *venc = inst->priv; int ret = 0; - vpu_inst_lock(inst); switch (ctrl->id) { case V4L2_CID_MPEG_VIDEO_H264_PROFILE: venc->params.profile = ctrl->val; @@ -579,7 +578,6 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) ret = -EINVAL; break; } - vpu_inst_unlock(inst); return ret; } @@ -680,6 +678,9 @@ static int venc_ctrl_init(struct vpu_inst *inst) ~(1 << V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME), V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME); + v4l2_ctrl_new_std(&inst->ctrl_handler, NULL, + V4L2_CID_MPEG_VIDEO_AVERAGE_QP, 0, 51, 1, 0); + if (inst->ctrl_handler.error) { ret = inst->ctrl_handler.error; v4l2_ctrl_handler_free(&inst->ctrl_handler); @@ -819,6 +820,7 @@ static int venc_get_one_encoded_frame(struct vpu_inst *inst, vbuf->field = inst->cap_format.field; vbuf->flags |= frame->info.pic_type; vpu_set_buffer_state(vbuf, VPU_BUF_STATE_IDLE); + vpu_set_buffer_average_qp(vbuf, frame->info.average_qp); dev_dbg(inst->dev, "[%d][OUTPUT TS]%32lld\n", inst->id, vbuf->vb2_buf.timestamp); v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE); venc->ready_count++; diff --git a/drivers/media/platform/amphion/vpu.h b/drivers/media/platform/amphion/vpu.h index 0246cf0ac3a8..22f0da26ccec 100644 --- a/drivers/media/platform/amphion/vpu.h +++ b/drivers/media/platform/amphion/vpu.h @@ -306,6 +306,7 @@ struct vpu_vb2_buffer { dma_addr_t chroma_v; unsigned int state; u32 tag; + u32 average_qp; }; void vpu_writel(struct vpu_dev *vpu, u32 reg, u32 val); diff --git a/drivers/media/platform/amphion/vpu_defs.h b/drivers/media/platform/amphion/vpu_defs.h index 7320852668d6..428d988cf2f7 100644 --- a/drivers/media/platform/amphion/vpu_defs.h +++ b/drivers/media/platform/amphion/vpu_defs.h @@ -114,6 +114,7 @@ struct vpu_enc_pic_info { u32 wptr; u32 crc; s64 timestamp; + u32 average_qp; }; struct vpu_dec_codec_info { diff --git a/drivers/media/platform/amphion/vpu_malone.c b/drivers/media/platform/amphion/vpu_malone.c index d3425de7bccd..4769c053c6c2 100644 --- a/drivers/media/platform/amphion/vpu_malone.c +++ b/drivers/media/platform/amphion/vpu_malone.c @@ -207,11 +207,6 @@ struct vpu_malone_dbglog_desc { u32 reserved; }; -struct vpu_malone_frame_buffer { - u32 addr; - u32 size; -}; - struct vpu_malone_udata { u32 base; u32 total_size; diff --git a/drivers/media/platform/amphion/vpu_v4l2.c b/drivers/media/platform/amphion/vpu_v4l2.c index c88738e8fff7..83db57bc80b7 100644 --- a/drivers/media/platform/amphion/vpu_v4l2.c +++ b/drivers/media/platform/amphion/vpu_v4l2.c @@ -63,6 +63,13 @@ unsigned int vpu_get_buffer_state(struct vb2_v4l2_buffer *vbuf) return vpu_buf->state; } +void vpu_set_buffer_average_qp(struct vb2_v4l2_buffer *vbuf, u32 qp) +{ + struct vpu_vb2_buffer *vpu_buf = to_vpu_vb2_buffer(vbuf); + + vpu_buf->average_qp = qp; +} + void vpu_v4l2_set_error(struct vpu_inst *inst) { vpu_inst_lock(inst); @@ -539,6 +546,15 @@ static void vpu_vb2_buf_finish(struct vb2_buffer *vb) struct vpu_inst *inst = vb2_get_drv_priv(vb->vb2_queue); struct vb2_queue *q = vb->vb2_queue; + if (V4L2_TYPE_IS_CAPTURE(vb->type)) { + struct vpu_vb2_buffer *vpu_buf = to_vpu_vb2_buffer(vbuf); + struct v4l2_ctrl *ctrl = v4l2_ctrl_find(&inst->ctrl_handler, + V4L2_CID_MPEG_VIDEO_AVERAGE_QP); + + if (ctrl) + v4l2_ctrl_s_ctrl(ctrl, vpu_buf->average_qp); + } + if (vbuf->flags & V4L2_BUF_FLAG_LAST) vpu_notify_eos(inst); diff --git a/drivers/media/platform/amphion/vpu_v4l2.h b/drivers/media/platform/amphion/vpu_v4l2.h index 60f43056a7a2..56f2939fa84d 100644 --- a/drivers/media/platform/amphion/vpu_v4l2.h +++ b/drivers/media/platform/amphion/vpu_v4l2.h @@ -12,6 +12,7 @@ void vpu_inst_lock(struct vpu_inst *inst); void vpu_inst_unlock(struct vpu_inst *inst); void vpu_set_buffer_state(struct vb2_v4l2_buffer *vbuf, unsigned int state); unsigned int vpu_get_buffer_state(struct vb2_v4l2_buffer *vbuf); +void vpu_set_buffer_average_qp(struct vb2_v4l2_buffer *vbuf, u32 qp); int vpu_v4l2_open(struct file *file, struct vpu_inst *inst); int vpu_v4l2_close(struct file *file); diff --git a/drivers/media/platform/amphion/vpu_windsor.c b/drivers/media/platform/amphion/vpu_windsor.c index 5f1101d7cf9e..e7d37aa4b826 100644 --- a/drivers/media/platform/amphion/vpu_windsor.c +++ b/drivers/media/platform/amphion/vpu_windsor.c @@ -499,6 +499,7 @@ struct windsor_pic_info { u32 proc_dacc_rng_wr_cnt; s32 tv_s; u32 tv_ns; + u32 average_qp; }; u32 vpu_windsor_get_data_size(void) @@ -734,6 +735,7 @@ static void vpu_windsor_unpack_pic_info(struct vpu_rpc_event *pkt, void *data) info->wptr = get_ptr(windsor->str_buff_wptr); info->crc = windsor->frame_crc; info->timestamp = timespec64_to_ns(&ts); + info->average_qp = windsor->average_qp; } static void vpu_windsor_unpack_mem_req(struct vpu_rpc_event *pkt, void *data) diff --git a/drivers/media/platform/chips-media/wave5/wave5-helper.c b/drivers/media/platform/chips-media/wave5/wave5-helper.c index 7e0f34bfa5be..d60841c54a80 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-helper.c +++ b/drivers/media/platform/chips-media/wave5/wave5-helper.c @@ -29,7 +29,13 @@ void wave5_cleanup_instance(struct vpu_instance *inst) { int i; - if (list_is_singular(&inst->list)) + /* + * For Wave515 SRAM memory is allocated at + * wave5_vpu_dec_register_device() and freed at + * wave5_vpu_dec_unregister_device(). + */ + if (list_is_singular(&inst->list) && + inst->dev->product_code != WAVE515_CODE) wave5_vdi_free_sram(inst->dev); for (i = 0; i < inst->fbc_buf_count; i++) diff --git a/drivers/media/platform/chips-media/wave5/wave5-hw.c b/drivers/media/platform/chips-media/wave5/wave5-hw.c index 2d82791f575e..c89aafabc742 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-hw.c +++ b/drivers/media/platform/chips-media/wave5/wave5-hw.c @@ -18,18 +18,20 @@ #define QUEUE_REPORT_MASK 0xffff /* Encoder support fields */ -#define FEATURE_HEVC10BIT_ENC BIT(3) -#define FEATURE_AVC10BIT_ENC BIT(11) -#define FEATURE_AVC_ENCODER BIT(1) -#define FEATURE_HEVC_ENCODER BIT(0) +#define W521_FEATURE_HEVC10BIT_ENC BIT(3) +#define W521_FEATURE_AVC10BIT_ENC BIT(11) +#define W521_FEATURE_AVC_ENCODER BIT(1) +#define W521_FEATURE_HEVC_ENCODER BIT(0) /* Decoder support fields */ -#define FEATURE_AVC_DECODER BIT(3) -#define FEATURE_HEVC_DECODER BIT(2) +#define W521_FEATURE_AVC_DECODER BIT(3) +#define W521_FEATURE_HEVC_DECODER BIT(2) +#define W515_FEATURE_HEVC10BIT_DEC BIT(1) +#define W515_FEATURE_HEVC_DECODER BIT(0) -#define FEATURE_BACKBONE BIT(16) -#define FEATURE_VCORE_BACKBONE BIT(22) -#define FEATURE_VCPU_BACKBONE BIT(28) +#define W521_FEATURE_BACKBONE BIT(16) +#define W521_FEATURE_VCORE_BACKBONE BIT(22) +#define W521_FEATURE_VCPU_BACKBONE BIT(28) #define REMAP_CTRL_MAX_SIZE_BITS ((W5_REMAP_MAX_SIZE >> 12) & 0x1ff) #define REMAP_CTRL_REGISTER_VALUE(index) ( \ @@ -155,6 +157,8 @@ static int wave5_wait_bus_busy(struct vpu_device *vpu_dev, unsigned int addr) { u32 gdi_status_check_value = 0x3f; + if (vpu_dev->product_code == WAVE515_CODE) + gdi_status_check_value = 0x0738; if (vpu_dev->product_code == WAVE521C_CODE || vpu_dev->product_code == WAVE521_CODE || vpu_dev->product_code == WAVE521E1_CODE) @@ -186,6 +190,8 @@ unsigned int wave5_vpu_get_product_id(struct vpu_device *vpu_dev) u32 val = vpu_read_reg(vpu_dev, W5_PRODUCT_NUMBER); switch (val) { + case WAVE515_CODE: + return PRODUCT_ID_515; case WAVE521C_CODE: return PRODUCT_ID_521; case WAVE521_CODE: @@ -299,6 +305,27 @@ static int wave5_send_query(struct vpu_device *vpu_dev, struct vpu_instance *ins return wave5_vpu_firmware_command_queue_error_check(vpu_dev, NULL); } +static void setup_wave5_interrupts(struct vpu_device *vpu_dev) +{ + u32 reg_val = 0; + + if (vpu_dev->attr.support_encoders) { + /* Encoder interrupt */ + reg_val |= BIT(INT_WAVE5_ENC_SET_PARAM); + reg_val |= BIT(INT_WAVE5_ENC_PIC); + reg_val |= BIT(INT_WAVE5_BSBUF_FULL); + } + + if (vpu_dev->attr.support_decoders) { + /* Decoder interrupt */ + reg_val |= BIT(INT_WAVE5_INIT_SEQ); + reg_val |= BIT(INT_WAVE5_DEC_PIC); + reg_val |= BIT(INT_WAVE5_BSBUF_EMPTY); + } + + return vpu_write_reg(vpu_dev, W5_VPU_VINT_ENABLE, reg_val); +} + static int setup_wave5_properties(struct device *dev) { struct vpu_device *vpu_dev = dev_get_drvdata(dev); @@ -328,17 +355,35 @@ static int setup_wave5_properties(struct device *dev) hw_config_def1 = vpu_read_reg(vpu_dev, W5_RET_STD_DEF1); hw_config_feature = vpu_read_reg(vpu_dev, W5_RET_CONF_FEATURE); - p_attr->support_hevc10bit_enc = FIELD_GET(FEATURE_HEVC10BIT_ENC, hw_config_feature); - p_attr->support_avc10bit_enc = FIELD_GET(FEATURE_AVC10BIT_ENC, hw_config_feature); - - p_attr->support_decoders = FIELD_GET(FEATURE_AVC_DECODER, hw_config_def1) << STD_AVC; - p_attr->support_decoders |= FIELD_GET(FEATURE_HEVC_DECODER, hw_config_def1) << STD_HEVC; - p_attr->support_encoders = FIELD_GET(FEATURE_AVC_ENCODER, hw_config_def1) << STD_AVC; - p_attr->support_encoders |= FIELD_GET(FEATURE_HEVC_ENCODER, hw_config_def1) << STD_HEVC; - - p_attr->support_backbone = FIELD_GET(FEATURE_BACKBONE, hw_config_def0); - p_attr->support_vcpu_backbone = FIELD_GET(FEATURE_VCPU_BACKBONE, hw_config_def0); - p_attr->support_vcore_backbone = FIELD_GET(FEATURE_VCORE_BACKBONE, hw_config_def0); + if (vpu_dev->product_code == WAVE515_CODE) { + p_attr->support_hevc10bit_dec = FIELD_GET(W515_FEATURE_HEVC10BIT_DEC, + hw_config_feature); + p_attr->support_decoders = FIELD_GET(W515_FEATURE_HEVC_DECODER, + hw_config_def1) << STD_HEVC; + } else { + p_attr->support_hevc10bit_enc = FIELD_GET(W521_FEATURE_HEVC10BIT_ENC, + hw_config_feature); + p_attr->support_avc10bit_enc = FIELD_GET(W521_FEATURE_AVC10BIT_ENC, + hw_config_feature); + + p_attr->support_decoders = FIELD_GET(W521_FEATURE_AVC_DECODER, + hw_config_def1) << STD_AVC; + p_attr->support_decoders |= FIELD_GET(W521_FEATURE_HEVC_DECODER, + hw_config_def1) << STD_HEVC; + p_attr->support_encoders = FIELD_GET(W521_FEATURE_AVC_ENCODER, + hw_config_def1) << STD_AVC; + p_attr->support_encoders |= FIELD_GET(W521_FEATURE_HEVC_ENCODER, + hw_config_def1) << STD_HEVC; + + p_attr->support_backbone = FIELD_GET(W521_FEATURE_BACKBONE, + hw_config_def0); + p_attr->support_vcpu_backbone = FIELD_GET(W521_FEATURE_VCPU_BACKBONE, + hw_config_def0); + p_attr->support_vcore_backbone = FIELD_GET(W521_FEATURE_VCORE_BACKBONE, + hw_config_def0); + } + + setup_wave5_interrupts(vpu_dev); return 0; } @@ -380,12 +425,18 @@ int wave5_vpu_init(struct device *dev, u8 *fw, size_t size) common_vb = &vpu_dev->common_mem; code_base = common_vb->daddr; + + if (vpu_dev->product_code == WAVE515_CODE) + code_size = WAVE515_MAX_CODE_BUF_SIZE; + else + code_size = WAVE521_MAX_CODE_BUF_SIZE; + /* ALIGN TO 4KB */ - code_size = (WAVE5_MAX_CODE_BUF_SIZE & ~0xfff); + code_size &= ~0xfff; if (code_size < size * 2) return -EINVAL; - temp_base = common_vb->daddr + WAVE5_TEMPBUF_OFFSET; + temp_base = code_base + code_size; temp_size = WAVE5_TEMPBUF_SIZE; ret = wave5_vdi_write_memory(vpu_dev, common_vb, 0, fw, size); @@ -413,22 +464,15 @@ int wave5_vpu_init(struct device *dev, u8 *fw, size_t size) /* These register must be reset explicitly */ vpu_write_reg(vpu_dev, W5_HW_OPTION, 0); - wave5_fio_writel(vpu_dev, W5_BACKBONE_PROC_EXT_ADDR, 0); - wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0); - vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0); - - /* Encoder interrupt */ - reg_val = BIT(INT_WAVE5_ENC_SET_PARAM); - reg_val |= BIT(INT_WAVE5_ENC_PIC); - reg_val |= BIT(INT_WAVE5_BSBUF_FULL); - /* Decoder interrupt */ - reg_val |= BIT(INT_WAVE5_INIT_SEQ); - reg_val |= BIT(INT_WAVE5_DEC_PIC); - reg_val |= BIT(INT_WAVE5_BSBUF_EMPTY); - vpu_write_reg(vpu_dev, W5_VPU_VINT_ENABLE, reg_val); + + if (vpu_dev->product_code != WAVE515_CODE) { + wave5_fio_writel(vpu_dev, W5_BACKBONE_PROC_EXT_ADDR, 0); + wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0); + vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0); + } reg_val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0); - if (FIELD_GET(FEATURE_BACKBONE, reg_val)) { + if (FIELD_GET(W521_FEATURE_BACKBONE, reg_val)) { reg_val = ((WAVE5_PROC_AXI_ID << 28) | (WAVE5_PRP_AXI_ID << 24) | (WAVE5_FBD_Y_AXI_ID << 20) | @@ -440,6 +484,24 @@ int wave5_vpu_init(struct device *dev, u8 *fw, size_t size) wave5_fio_writel(vpu_dev, W5_BACKBONE_PROG_AXI_ID, reg_val); } + if (vpu_dev->product_code == WAVE515_CODE) { + dma_addr_t task_buf_base; + + vpu_write_reg(vpu_dev, W5_CMD_INIT_NUM_TASK_BUF, WAVE515_COMMAND_QUEUE_DEPTH); + vpu_write_reg(vpu_dev, W5_CMD_INIT_TASK_BUF_SIZE, WAVE515_ONE_TASKBUF_SIZE); + + for (i = 0; i < WAVE515_COMMAND_QUEUE_DEPTH; i++) { + task_buf_base = temp_base + temp_size + + (i * WAVE515_ONE_TASKBUF_SIZE); + vpu_write_reg(vpu_dev, + W5_CMD_INIT_ADDR_TASK_BUF0 + (i * 4), + task_buf_base); + } + + vpu_write_reg(vpu_dev, W515_CMD_ADDR_SEC_AXI, vpu_dev->sram_buf.daddr); + vpu_write_reg(vpu_dev, W515_CMD_SEC_AXI_SIZE, vpu_dev->sram_buf.size); + } + vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1); vpu_write_reg(vpu_dev, W5_COMMAND, W5_INIT_VPU); vpu_write_reg(vpu_dev, W5_VPU_REMAP_CORE_START, 1); @@ -480,29 +542,40 @@ int wave5_vpu_build_up_dec_param(struct vpu_instance *inst, return -EINVAL; } - p_dec_info->vb_work.size = WAVE521DEC_WORKBUF_SIZE; + if (vpu_dev->product == PRODUCT_ID_515) + p_dec_info->vb_work.size = WAVE515DEC_WORKBUF_SIZE; + else + p_dec_info->vb_work.size = WAVE521DEC_WORKBUF_SIZE; + ret = wave5_vdi_allocate_dma_memory(inst->dev, &p_dec_info->vb_work); if (ret) return ret; - vpu_write_reg(inst->dev, W5_CMD_DEC_VCORE_INFO, 1); + if (inst->dev->product_code != WAVE515_CODE) + vpu_write_reg(inst->dev, W5_CMD_DEC_VCORE_INFO, 1); wave5_vdi_clear_memory(inst->dev, &p_dec_info->vb_work); vpu_write_reg(inst->dev, W5_ADDR_WORK_BASE, p_dec_info->vb_work.daddr); vpu_write_reg(inst->dev, W5_WORK_SIZE, p_dec_info->vb_work.size); - vpu_write_reg(inst->dev, W5_CMD_ADDR_SEC_AXI, vpu_dev->sram_buf.daddr); - vpu_write_reg(inst->dev, W5_CMD_SEC_AXI_SIZE, vpu_dev->sram_buf.size); + if (inst->dev->product_code != WAVE515_CODE) { + vpu_write_reg(inst->dev, W5_CMD_ADDR_SEC_AXI, vpu_dev->sram_buf.daddr); + vpu_write_reg(inst->dev, W5_CMD_SEC_AXI_SIZE, vpu_dev->sram_buf.size); + } vpu_write_reg(inst->dev, W5_CMD_DEC_BS_START_ADDR, p_dec_info->stream_buf_start_addr); vpu_write_reg(inst->dev, W5_CMD_DEC_BS_SIZE, p_dec_info->stream_buf_size); /* NOTE: SDMA reads MSB first */ vpu_write_reg(inst->dev, W5_CMD_BS_PARAM, BITSTREAM_ENDIANNESS_BIG_ENDIAN); - /* This register must be reset explicitly */ - vpu_write_reg(inst->dev, W5_CMD_EXT_ADDR, 0); - vpu_write_reg(inst->dev, W5_CMD_NUM_CQ_DEPTH_M1, (COMMAND_QUEUE_DEPTH - 1)); + + if (inst->dev->product_code != WAVE515_CODE) { + /* This register must be reset explicitly */ + vpu_write_reg(inst->dev, W5_CMD_EXT_ADDR, 0); + vpu_write_reg(inst->dev, W5_CMD_NUM_CQ_DEPTH_M1, + WAVE521_COMMAND_QUEUE_DEPTH - 1); + } ret = send_firmware_command(inst, W5_CREATE_INSTANCE, true, NULL, NULL); if (ret) { @@ -553,7 +626,7 @@ static u32 get_bitstream_options(struct dec_info *info) int wave5_vpu_dec_init_seq(struct vpu_instance *inst) { struct dec_info *p_dec_info = &inst->codec_info->dec_info; - u32 cmd_option = INIT_SEQ_NORMAL; + u32 bs_option, cmd_option = INIT_SEQ_NORMAL; u32 reg_val, fail_res; int ret; @@ -563,7 +636,13 @@ int wave5_vpu_dec_init_seq(struct vpu_instance *inst) vpu_write_reg(inst->dev, W5_BS_RD_PTR, p_dec_info->stream_rd_ptr); vpu_write_reg(inst->dev, W5_BS_WR_PTR, p_dec_info->stream_wr_ptr); - vpu_write_reg(inst->dev, W5_BS_OPTION, get_bitstream_options(p_dec_info)); + bs_option = get_bitstream_options(p_dec_info); + + /* Without RD_PTR_VALID_FLAG Wave515 ignores RD_PTR value */ + if (inst->dev->product_code == WAVE515_CODE) + bs_option |= BSOPTION_RD_PTR_VALID_FLAG; + + vpu_write_reg(inst->dev, W5_BS_OPTION, bs_option); vpu_write_reg(inst->dev, W5_COMMAND_OPTION, cmd_option); vpu_write_reg(inst->dev, W5_CMD_DEC_USER_MASK, p_dec_info->user_data_enable); @@ -629,10 +708,12 @@ static void wave5_get_dec_seq_result(struct vpu_instance *inst, struct dec_initi info->profile = FIELD_GET(SEQ_PARAM_PROFILE_MASK, reg_val); } - info->vlc_buf_size = vpu_read_reg(inst->dev, W5_RET_VLC_BUF_SIZE); - info->param_buf_size = vpu_read_reg(inst->dev, W5_RET_PARAM_BUF_SIZE); - p_dec_info->vlc_buf_size = info->vlc_buf_size; - p_dec_info->param_buf_size = info->param_buf_size; + if (inst->dev->product_code != WAVE515_CODE) { + info->vlc_buf_size = vpu_read_reg(inst->dev, W5_RET_VLC_BUF_SIZE); + info->param_buf_size = vpu_read_reg(inst->dev, W5_RET_PARAM_BUF_SIZE); + p_dec_info->vlc_buf_size = info->vlc_buf_size; + p_dec_info->param_buf_size = info->param_buf_size; + } } int wave5_vpu_dec_get_seq_info(struct vpu_instance *inst, struct dec_initial_info *info) @@ -734,22 +815,27 @@ int wave5_vpu_dec_register_framebuffer(struct vpu_instance *inst, struct frame_b pic_size = (init_info->pic_width << 16) | (init_info->pic_height); - vb_buf.size = (p_dec_info->vlc_buf_size * VLC_BUF_NUM) + - (p_dec_info->param_buf_size * COMMAND_QUEUE_DEPTH); - vb_buf.daddr = 0; + if (inst->dev->product_code != WAVE515_CODE) { + vb_buf.size = (p_dec_info->vlc_buf_size * VLC_BUF_NUM) + + (p_dec_info->param_buf_size * WAVE521_COMMAND_QUEUE_DEPTH); + vb_buf.daddr = 0; - if (vb_buf.size != p_dec_info->vb_task.size) { - wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_task); - ret = wave5_vdi_allocate_dma_memory(inst->dev, &vb_buf); - if (ret) - goto free_fbc_c_tbl_buffers; + if (vb_buf.size != p_dec_info->vb_task.size) { + wave5_vdi_free_dma_memory(inst->dev, + &p_dec_info->vb_task); + ret = wave5_vdi_allocate_dma_memory(inst->dev, + &vb_buf); + if (ret) + goto free_fbc_c_tbl_buffers; - p_dec_info->vb_task = vb_buf; - } + p_dec_info->vb_task = vb_buf; + } - vpu_write_reg(inst->dev, W5_CMD_SET_FB_ADDR_TASK_BUF, - p_dec_info->vb_task.daddr); - vpu_write_reg(inst->dev, W5_CMD_SET_FB_TASK_BUF_SIZE, vb_buf.size); + vpu_write_reg(inst->dev, W5_CMD_SET_FB_ADDR_TASK_BUF, + p_dec_info->vb_task.daddr); + vpu_write_reg(inst->dev, W5_CMD_SET_FB_TASK_BUF_SIZE, + vb_buf.size); + } } else { pic_size = (init_info->pic_width << 16) | (init_info->pic_height); @@ -830,6 +916,43 @@ free_mv_buffers: return ret; } +static u32 wave5_vpu_dec_validate_sec_axi(struct vpu_instance *inst) +{ + u32 bitdepth = inst->codec_info->dec_info.initial_info.luma_bitdepth; + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + u32 bit_size = 0, ip_size = 0, lf_size = 0, ret = 0; + u32 sram_size = inst->dev->sram_size; + u32 width = inst->src_fmt.width; + + if (!sram_size) + return 0; + + /* + * TODO: calculate bit_size, ip_size, lf_size from width and bitdepth + * for Wave521. + */ + if (inst->dev->product_code == WAVE515_CODE) { + bit_size = DIV_ROUND_UP(width, 16) * 5 * 8; + ip_size = ALIGN(width, 16) * 2 * bitdepth / 8; + lf_size = ALIGN(width, 16) * 10 * bitdepth / 8; + } + + if (p_dec_info->sec_axi_info.use_bit_enable && sram_size >= bit_size) { + ret |= BIT(0); + sram_size -= bit_size; + } + + if (p_dec_info->sec_axi_info.use_ip_enable && sram_size >= ip_size) { + ret |= BIT(9); + sram_size -= ip_size; + } + + if (p_dec_info->sec_axi_info.use_lf_row_enable && sram_size >= lf_size) + ret |= BIT(15); + + return ret; +} + int wave5_vpu_decode(struct vpu_instance *inst, u32 *fail_res) { u32 reg_val; @@ -842,9 +965,7 @@ int wave5_vpu_decode(struct vpu_instance *inst, u32 *fail_res) vpu_write_reg(inst->dev, W5_BS_OPTION, get_bitstream_options(p_dec_info)); /* secondary AXI */ - reg_val = p_dec_info->sec_axi_info.use_bit_enable | - (p_dec_info->sec_axi_info.use_ip_enable << 9) | - (p_dec_info->sec_axi_info.use_lf_row_enable << 15); + reg_val = wave5_vpu_dec_validate_sec_axi(inst); vpu_write_reg(inst->dev, W5_USE_SEC_AXI, reg_val); /* set attributes of user buffer */ @@ -992,11 +1113,18 @@ int wave5_vpu_re_init(struct device *dev, u8 *fw, size_t size) common_vb = &vpu_dev->common_mem; code_base = common_vb->daddr; + + if (vpu_dev->product_code == WAVE515_CODE) + code_size = WAVE515_MAX_CODE_BUF_SIZE; + else + code_size = WAVE521_MAX_CODE_BUF_SIZE; + /* ALIGN TO 4KB */ - code_size = (WAVE5_MAX_CODE_BUF_SIZE & ~0xfff); + code_size &= ~0xfff; if (code_size < size * 2) return -EINVAL; - temp_base = common_vb->daddr + WAVE5_TEMPBUF_OFFSET; + + temp_base = code_base + code_size; temp_size = WAVE5_TEMPBUF_SIZE; old_code_base = vpu_read_reg(vpu_dev, W5_VPU_REMAP_PADDR); @@ -1030,22 +1158,15 @@ int wave5_vpu_re_init(struct device *dev, u8 *fw, size_t size) /* These register must be reset explicitly */ vpu_write_reg(vpu_dev, W5_HW_OPTION, 0); - wave5_fio_writel(vpu_dev, W5_BACKBONE_PROC_EXT_ADDR, 0); - wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0); - vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0); - /* Encoder interrupt */ - reg_val = BIT(INT_WAVE5_ENC_SET_PARAM); - reg_val |= BIT(INT_WAVE5_ENC_PIC); - reg_val |= BIT(INT_WAVE5_BSBUF_FULL); - /* Decoder interrupt */ - reg_val |= BIT(INT_WAVE5_INIT_SEQ); - reg_val |= BIT(INT_WAVE5_DEC_PIC); - reg_val |= BIT(INT_WAVE5_BSBUF_EMPTY); - vpu_write_reg(vpu_dev, W5_VPU_VINT_ENABLE, reg_val); + if (vpu_dev->product_code != WAVE515_CODE) { + wave5_fio_writel(vpu_dev, W5_BACKBONE_PROC_EXT_ADDR, 0); + wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0); + vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0); + } reg_val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0); - if (FIELD_GET(FEATURE_BACKBONE, reg_val)) { + if (FIELD_GET(W521_FEATURE_BACKBONE, reg_val)) { reg_val = ((WAVE5_PROC_AXI_ID << 28) | (WAVE5_PRP_AXI_ID << 24) | (WAVE5_FBD_Y_AXI_ID << 20) | @@ -1057,6 +1178,29 @@ int wave5_vpu_re_init(struct device *dev, u8 *fw, size_t size) wave5_fio_writel(vpu_dev, W5_BACKBONE_PROG_AXI_ID, reg_val); } + if (vpu_dev->product_code == WAVE515_CODE) { + dma_addr_t task_buf_base; + u32 i; + + vpu_write_reg(vpu_dev, W5_CMD_INIT_NUM_TASK_BUF, + WAVE515_COMMAND_QUEUE_DEPTH); + vpu_write_reg(vpu_dev, W5_CMD_INIT_TASK_BUF_SIZE, + WAVE515_ONE_TASKBUF_SIZE); + + for (i = 0; i < WAVE515_COMMAND_QUEUE_DEPTH; i++) { + task_buf_base = temp_base + temp_size + + (i * WAVE515_ONE_TASKBUF_SIZE); + vpu_write_reg(vpu_dev, + W5_CMD_INIT_ADDR_TASK_BUF0 + (i * 4), + task_buf_base); + } + + vpu_write_reg(vpu_dev, W515_CMD_ADDR_SEC_AXI, + vpu_dev->sram_buf.daddr); + vpu_write_reg(vpu_dev, W515_CMD_SEC_AXI_SIZE, + vpu_dev->sram_buf.size); + } + vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1); vpu_write_reg(vpu_dev, W5_COMMAND, W5_INIT_VPU); vpu_write_reg(vpu_dev, W5_VPU_REMAP_CORE_START, 1); @@ -1080,8 +1224,8 @@ static int wave5_vpu_sleep_wake(struct device *dev, bool i_sleep_wake, const uin { u32 reg_val; struct vpu_buf *common_vb; - dma_addr_t code_base; - u32 code_size, reason_code; + dma_addr_t code_base, temp_base; + u32 code_size, temp_size, reason_code; struct vpu_device *vpu_dev = dev_get_drvdata(dev); int ret; @@ -1111,13 +1255,22 @@ static int wave5_vpu_sleep_wake(struct device *dev, bool i_sleep_wake, const uin common_vb = &vpu_dev->common_mem; code_base = common_vb->daddr; + + if (vpu_dev->product_code == WAVE515_CODE) + code_size = WAVE515_MAX_CODE_BUF_SIZE; + else + code_size = WAVE521_MAX_CODE_BUF_SIZE; + /* ALIGN TO 4KB */ - code_size = (WAVE5_MAX_CODE_BUF_SIZE & ~0xfff); + code_size &= ~0xfff; if (code_size < size * 2) { dev_err(dev, "size too small\n"); return -EINVAL; } + temp_base = code_base + code_size; + temp_size = WAVE5_TEMPBUF_SIZE; + /* Power on without DEBUG mode */ vpu_write_reg(vpu_dev, W5_PO_CONF, 0); @@ -1130,22 +1283,17 @@ static int wave5_vpu_sleep_wake(struct device *dev, bool i_sleep_wake, const uin /* These register must be reset explicitly */ vpu_write_reg(vpu_dev, W5_HW_OPTION, 0); - wave5_fio_writel(vpu_dev, W5_BACKBONE_PROC_EXT_ADDR, 0); - wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0); - vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0); - /* Encoder interrupt */ - reg_val = BIT(INT_WAVE5_ENC_SET_PARAM); - reg_val |= BIT(INT_WAVE5_ENC_PIC); - reg_val |= BIT(INT_WAVE5_BSBUF_FULL); - /* Decoder interrupt */ - reg_val |= BIT(INT_WAVE5_INIT_SEQ); - reg_val |= BIT(INT_WAVE5_DEC_PIC); - reg_val |= BIT(INT_WAVE5_BSBUF_EMPTY); - vpu_write_reg(vpu_dev, W5_VPU_VINT_ENABLE, reg_val); + if (vpu_dev->product_code != WAVE515_CODE) { + wave5_fio_writel(vpu_dev, W5_BACKBONE_PROC_EXT_ADDR, 0); + wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0); + vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0); + } + + setup_wave5_interrupts(vpu_dev); reg_val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0); - if (FIELD_GET(FEATURE_BACKBONE, reg_val)) { + if (FIELD_GET(W521_FEATURE_BACKBONE, reg_val)) { reg_val = ((WAVE5_PROC_AXI_ID << 28) | (WAVE5_PRP_AXI_ID << 24) | (WAVE5_FBD_Y_AXI_ID << 20) | @@ -1157,6 +1305,29 @@ static int wave5_vpu_sleep_wake(struct device *dev, bool i_sleep_wake, const uin wave5_fio_writel(vpu_dev, W5_BACKBONE_PROG_AXI_ID, reg_val); } + if (vpu_dev->product_code == WAVE515_CODE) { + dma_addr_t task_buf_base; + u32 i; + + vpu_write_reg(vpu_dev, W5_CMD_INIT_NUM_TASK_BUF, + WAVE515_COMMAND_QUEUE_DEPTH); + vpu_write_reg(vpu_dev, W5_CMD_INIT_TASK_BUF_SIZE, + WAVE515_ONE_TASKBUF_SIZE); + + for (i = 0; i < WAVE515_COMMAND_QUEUE_DEPTH; i++) { + task_buf_base = temp_base + temp_size + + (i * WAVE515_ONE_TASKBUF_SIZE); + vpu_write_reg(vpu_dev, + W5_CMD_INIT_ADDR_TASK_BUF0 + (i * 4), + task_buf_base); + } + + vpu_write_reg(vpu_dev, W515_CMD_ADDR_SEC_AXI, + vpu_dev->sram_buf.daddr); + vpu_write_reg(vpu_dev, W515_CMD_SEC_AXI_SIZE, + vpu_dev->sram_buf.size); + } + vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1); vpu_write_reg(vpu_dev, W5_COMMAND, W5_WAKEUP_VPU); /* Start VPU after settings */ @@ -1401,7 +1572,7 @@ int wave5_vpu_build_up_enc_param(struct device *dev, struct vpu_instance *inst, reg_val = (open_param->line_buf_int_en << 6) | BITSTREAM_ENDIANNESS_BIG_ENDIAN; vpu_write_reg(inst->dev, W5_CMD_BS_PARAM, reg_val); vpu_write_reg(inst->dev, W5_CMD_EXT_ADDR, 0); - vpu_write_reg(inst->dev, W5_CMD_NUM_CQ_DEPTH_M1, (COMMAND_QUEUE_DEPTH - 1)); + vpu_write_reg(inst->dev, W5_CMD_NUM_CQ_DEPTH_M1, WAVE521_COMMAND_QUEUE_DEPTH - 1); /* This register must be reset explicitly */ vpu_write_reg(inst->dev, W5_CMD_ENC_SRC_OPTIONS, 0); @@ -1855,7 +2026,7 @@ int wave5_vpu_enc_register_framebuffer(struct device *dev, struct vpu_instance * p_enc_info->vb_sub_sam_buf = vb_sub_sam_buf; vb_task.size = (p_enc_info->vlc_buf_size * VLC_BUF_NUM) + - (p_enc_info->param_buf_size * COMMAND_QUEUE_DEPTH); + (p_enc_info->param_buf_size * WAVE521_COMMAND_QUEUE_DEPTH); vb_task.daddr = 0; if (p_enc_info->vb_task.size == 0) { ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vb_task); @@ -1943,6 +2114,31 @@ free_vb_fbc_y_tbl: return ret; } +static u32 wave5_vpu_enc_validate_sec_axi(struct vpu_instance *inst) +{ + struct enc_info *p_enc_info = &inst->codec_info->enc_info; + u32 rdo_size = 0, lf_size = 0, ret = 0; + u32 sram_size = inst->dev->sram_size; + + if (!sram_size) + return 0; + + /* + * TODO: calculate rdo_size and lf_size from inst->src_fmt.width and + * inst->codec_info->enc_info.open_param.wave_param.internal_bit_depth + */ + + if (p_enc_info->sec_axi_info.use_enc_rdo_enable && sram_size >= rdo_size) { + ret |= BIT(11); + sram_size -= rdo_size; + } + + if (p_enc_info->sec_axi_info.use_enc_lf_enable && sram_size >= lf_size) + ret |= BIT(15); + + return ret; +} + int wave5_vpu_encode(struct vpu_instance *inst, struct enc_param *option, u32 *fail_res) { u32 src_frame_format; @@ -1964,8 +2160,7 @@ int wave5_vpu_encode(struct vpu_instance *inst, struct enc_param *option, u32 *f vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_AXI_SEL, DEFAULT_SRC_AXI); /* secondary AXI */ - reg_val = (p_enc_info->sec_axi_info.use_enc_rdo_enable << 11) | - (p_enc_info->sec_axi_info.use_enc_lf_enable << 15); + reg_val = wave5_vpu_enc_validate_sec_axi(inst); vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_USE_SEC_AXI, reg_val); vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_REPORT_PARAM, 0); diff --git a/drivers/media/platform/chips-media/wave5/wave5-regdefine.h b/drivers/media/platform/chips-media/wave5/wave5-regdefine.h index a15c6b2c3d8b..557344754c4c 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-regdefine.h +++ b/drivers/media/platform/chips-media/wave5/wave5-regdefine.h @@ -205,6 +205,9 @@ enum query_opt { #define W5_ADDR_TEMP_BASE (W5_REG_BASE + 0x011C) #define W5_TEMP_SIZE (W5_REG_BASE + 0x0120) #define W5_HW_OPTION (W5_REG_BASE + 0x012C) +#define W5_CMD_INIT_NUM_TASK_BUF (W5_REG_BASE + 0x0134) +#define W5_CMD_INIT_ADDR_TASK_BUF0 (W5_REG_BASE + 0x0138) +#define W5_CMD_INIT_TASK_BUF_SIZE (W5_REG_BASE + 0x0178) #define W5_SEC_AXI_PARAM (W5_REG_BASE + 0x0180) /************************************************************************/ @@ -216,7 +219,9 @@ enum query_opt { #define W5_CMD_DEC_BS_SIZE (W5_REG_BASE + 0x0120) #define W5_CMD_BS_PARAM (W5_REG_BASE + 0x0124) #define W5_CMD_ADDR_SEC_AXI (W5_REG_BASE + 0x0130) +#define W515_CMD_ADDR_SEC_AXI (W5_REG_BASE + 0x0124) #define W5_CMD_SEC_AXI_SIZE (W5_REG_BASE + 0x0134) +#define W515_CMD_SEC_AXI_SIZE (W5_REG_BASE + 0x0128) #define W5_CMD_EXT_ADDR (W5_REG_BASE + 0x0138) #define W5_CMD_NUM_CQ_DEPTH_M1 (W5_REG_BASE + 0x013C) #define W5_CMD_ERR_CONCEAL (W5_REG_BASE + 0x0140) diff --git a/drivers/media/platform/chips-media/wave5/wave5-vdi.c b/drivers/media/platform/chips-media/wave5/wave5-vdi.c index 3809f70bc0b4..bb13267ced38 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vdi.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vdi.c @@ -18,7 +18,11 @@ static int wave5_vdi_allocate_common_memory(struct device *dev) if (!vpu_dev->common_mem.vaddr) { int ret; - vpu_dev->common_mem.size = SIZE_COMMON; + if (vpu_dev->product_code == WAVE515_CODE) + vpu_dev->common_mem.size = WAVE515_SIZE_COMMON; + else + vpu_dev->common_mem.size = WAVE521_SIZE_COMMON; + ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vpu_dev->common_mem); if (ret) { dev_err(dev, "unable to allocate common buffer\n"); @@ -174,16 +178,19 @@ int wave5_vdi_allocate_array(struct vpu_device *vpu_dev, struct vpu_buf *array, void wave5_vdi_allocate_sram(struct vpu_device *vpu_dev) { struct vpu_buf *vb = &vpu_dev->sram_buf; + dma_addr_t daddr; + void *vaddr; + size_t size; - if (!vpu_dev->sram_pool || !vpu_dev->sram_size) + if (!vpu_dev->sram_pool || vb->vaddr) return; - if (!vb->vaddr) { - vb->size = vpu_dev->sram_size; - vb->vaddr = gen_pool_dma_alloc(vpu_dev->sram_pool, vb->size, - &vb->daddr); - if (!vb->vaddr) - vb->size = 0; + size = min_t(size_t, vpu_dev->sram_size, gen_pool_avail(vpu_dev->sram_pool)); + vaddr = gen_pool_dma_alloc(vpu_dev->sram_pool, size, &daddr); + if (vaddr) { + vb->vaddr = vaddr; + vb->daddr = daddr; + vb->size = size; } dev_dbg(vpu_dev->dev, "%s: sram daddr: %pad, size: %zu, vaddr: 0x%p\n", @@ -197,9 +204,7 @@ void wave5_vdi_free_sram(struct vpu_device *vpu_dev) if (!vb->size || !vb->vaddr) return; - if (vb->vaddr) - gen_pool_free(vpu_dev->sram_pool, (unsigned long)vb->vaddr, - vb->size); + gen_pool_free(vpu_dev->sram_pool, (unsigned long)vb->vaddr, vb->size); memset(vb, 0, sizeof(*vb)); } diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c index c8624c681fa6..0c5c9a8de91f 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c @@ -1055,6 +1055,22 @@ static int wave5_prepare_fb(struct vpu_instance *inst) int ret, i; struct v4l2_m2m_buffer *buf, *n; struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + u32 bitdepth = inst->codec_info->dec_info.initial_info.luma_bitdepth; + + switch (bitdepth) { + case 8: + break; + case 10: + if (inst->std == W_HEVC_DEC && + inst->dev->attr.support_hevc10bit_dec) + break; + + fallthrough; + default: + dev_err(inst->dev->dev, "no support for %d bit depth\n", bitdepth); + + return -EINVAL; + } linear_num = v4l2_m2m_num_dst_bufs_ready(m2m_ctx); non_linear_num = inst->fbc_buf_count; @@ -1063,7 +1079,7 @@ static int wave5_prepare_fb(struct vpu_instance *inst) struct frame_buffer *frame = &inst->frame_buf[i]; struct vpu_buf *vframe = &inst->frame_vbuf[i]; - fb_stride = inst->dst_fmt.width; + fb_stride = ALIGN(inst->dst_fmt.width * bitdepth / 8, 32); fb_height = ALIGN(inst->dst_fmt.height, 32); luma_size = fb_stride * fb_height; @@ -1408,22 +1424,10 @@ static int wave5_vpu_dec_start_streaming(struct vb2_queue *q, unsigned int count if (ret) goto free_bitstream_vbuf; } else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { - struct dec_initial_info *initial_info = - &inst->codec_info->dec_info.initial_info; - if (inst->state == VPU_INST_STATE_STOP) ret = switch_state(inst, VPU_INST_STATE_INIT_SEQ); if (ret) goto return_buffers; - - if (inst->state == VPU_INST_STATE_INIT_SEQ) { - if (initial_info->luma_bitdepth != 8) { - dev_info(inst->dev->dev, "%s: no support for %d bit depth", - __func__, initial_info->luma_bitdepth); - ret = -EINVAL; - goto return_buffers; - } - } } return ret; @@ -1864,7 +1868,12 @@ static int wave5_vpu_open_dec(struct file *filp) goto cleanup_inst; } - wave5_vdi_allocate_sram(inst->dev); + /* + * For Wave515 SRAM memory was already allocated + * at wave5_vpu_dec_register_device() + */ + if (inst->dev->product_code != WAVE515_CODE) + wave5_vdi_allocate_sram(inst->dev); ret = mutex_lock_interruptible(&dev->dev_lock); if (ret) @@ -1904,6 +1913,13 @@ int wave5_vpu_dec_register_device(struct vpu_device *dev) struct video_device *vdev_dec; int ret; + /* + * Secondary AXI setup for Wave515 is done by INIT_VPU command, + * i.e. wave5_vpu_init(), that's why we allocate SRAM memory early. + */ + if (dev->product_code == WAVE515_CODE) + wave5_vdi_allocate_sram(dev); + vdev_dec = devm_kzalloc(dev->v4l2_dev.dev, sizeof(*vdev_dec), GFP_KERNEL); if (!vdev_dec) return -ENOMEM; @@ -1937,6 +1953,13 @@ int wave5_vpu_dec_register_device(struct vpu_device *dev) void wave5_vpu_dec_unregister_device(struct vpu_device *dev) { + /* + * Here is a freeing pair for Wave515 SRAM memory allocation + * happened at wave5_vpu_dec_register_device(). + */ + if (dev->product_code == WAVE515_CODE) + wave5_vdi_free_sram(dev); + video_unregister_device(dev->video_dev_dec); if (dev->v4l2_m2m_dec_dev) v4l2_m2m_release(dev->v4l2_m2m_dec_dev); diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c index a45a2f699000..3e35a05c2d8d 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c @@ -1247,7 +1247,7 @@ static int initialize_sequence(struct vpu_instance *inst) __func__, initial_info.min_frame_buffer_count, initial_info.min_src_frame_count); inst->min_src_buf_count = initial_info.min_src_frame_count + - COMMAND_QUEUE_DEPTH; + WAVE521_COMMAND_QUEUE_DEPTH; ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl, V4L2_CID_MIN_BUFFERS_FOR_OUTPUT); diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu.c b/drivers/media/platform/chips-media/wave5/wave5-vpu.c index 68a519ac412d..7273254ecb03 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu.c @@ -10,6 +10,7 @@ #include <linux/clk.h> #include <linux/firmware.h> #include <linux/interrupt.h> +#include <linux/reset.h> #include "wave5-vpu.h" #include "wave5-regdefine.h" #include "wave5-vpuconfig.h" @@ -24,6 +25,7 @@ struct wave5_match_data { int flags; const char *fw_name; + u32 sram_size; }; static int vpu_poll_interval = 5; @@ -61,7 +63,13 @@ static void wave5_vpu_handle_irq(void *dev_id) if (irq_reason & BIT(INT_WAVE5_INIT_SEQ) || irq_reason & BIT(INT_WAVE5_ENC_SET_PARAM)) { - if (seq_done & BIT(inst->id)) { + if (dev->product_code == WAVE515_CODE && + (cmd_done & BIT(inst->id))) { + cmd_done &= ~BIT(inst->id); + wave5_vdi_write_register(dev, W5_RET_QUEUE_CMD_DONE_INST, + cmd_done); + complete(&inst->irq_done); + } else if (seq_done & BIT(inst->id)) { seq_done &= ~BIT(inst->id); wave5_vdi_write_register(dev, W5_RET_SEQ_DONE_INSTANCE_INFO, seq_done); @@ -179,6 +187,16 @@ static int wave5_vpu_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, dev); dev->dev = &pdev->dev; + dev->resets = devm_reset_control_array_get_optional_exclusive(&pdev->dev); + if (IS_ERR(dev->resets)) { + return dev_err_probe(&pdev->dev, PTR_ERR(dev->resets), + "Failed to get reset control\n"); + } + + ret = reset_control_deassert(dev->resets); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to deassert resets\n"); + ret = devm_clk_bulk_get_all(&pdev->dev, &dev->clks); /* continue without clock, assume externally managed */ @@ -191,20 +209,15 @@ static int wave5_vpu_probe(struct platform_device *pdev) ret = clk_bulk_prepare_enable(dev->num_clks, dev->clks); if (ret) { dev_err(&pdev->dev, "Enabling clocks, fail: %d\n", ret); - return ret; - } - - ret = of_property_read_u32(pdev->dev.of_node, "sram-size", - &dev->sram_size); - if (ret) { - dev_warn(&pdev->dev, "sram-size not found\n"); - dev->sram_size = 0; + goto err_reset_assert; } dev->sram_pool = of_gen_pool_get(pdev->dev.of_node, "sram", 0); if (!dev->sram_pool) dev_warn(&pdev->dev, "sram node not found\n"); + dev->sram_size = match_data->sram_size; + dev->product_code = wave5_vdi_read_register(dev, VPU_PRODUCT_CODE_REGISTER); ret = wave5_vdi_init(&pdev->dev); if (ret < 0) { @@ -282,6 +295,8 @@ err_vdi_release: wave5_vdi_release(&pdev->dev); err_clk_dis: clk_bulk_disable_unprepare(dev->num_clks, dev->clks); +err_reset_assert: + reset_control_assert(dev->resets); return ret; } @@ -297,6 +312,7 @@ static void wave5_vpu_remove(struct platform_device *pdev) mutex_destroy(&dev->dev_lock); mutex_destroy(&dev->hw_lock); + reset_control_assert(dev->resets); clk_bulk_disable_unprepare(dev->num_clks, dev->clks); wave5_vpu_enc_unregister_device(dev); wave5_vpu_dec_unregister_device(dev); @@ -308,6 +324,7 @@ static void wave5_vpu_remove(struct platform_device *pdev) static const struct wave5_match_data ti_wave521c_data = { .flags = WAVE5_IS_ENC | WAVE5_IS_DEC, .fw_name = "cnm/wave521c_k3_codec_fw.bin", + .sram_size = (64 * 1024), }; static const struct of_device_id wave5_dt_ids[] = { diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h index edc50450ddb8..d2370511faf8 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h @@ -18,6 +18,7 @@ #include "wave5-vdi.h" enum product_id { + PRODUCT_ID_515, PRODUCT_ID_521, PRODUCT_ID_511, PRODUCT_ID_517, @@ -327,6 +328,7 @@ struct vpu_attr { u32 support_backbone: 1; u32 support_avc10bit_enc: 1; u32 support_hevc10bit_enc: 1; + u32 support_hevc10bit_dec: 1; u32 support_vcore_backbone: 1; u32 support_vcpu_backbone: 1; }; @@ -761,6 +763,7 @@ struct vpu_device { struct kthread_worker *worker; int vpu_poll_interval; int num_clks; + struct reset_control *resets; }; struct vpu_instance; diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h b/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h index d9751eedb0f9..e4bc2e467cb5 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h @@ -8,6 +8,7 @@ #ifndef _VPU_CONFIG_H_ #define _VPU_CONFIG_H_ +#define WAVE515_CODE 0x5150 #define WAVE517_CODE 0x5170 #define WAVE537_CODE 0x5370 #define WAVE511_CODE 0x5110 @@ -21,12 +22,13 @@ ((c) == WAVE517_CODE || (c) == WAVE537_CODE || \ (c) == WAVE511_CODE || (c) == WAVE521_CODE || \ (c) == WAVE521E1_CODE || (c) == WAVE521C_CODE || \ - (c) == WAVE521C_DUAL_CODE); \ + (c) == WAVE521C_DUAL_CODE) || (c) == WAVE515_CODE; \ }) #define WAVE517_WORKBUF_SIZE (2 * 1024 * 1024) #define WAVE521ENC_WORKBUF_SIZE (128 * 1024) //HEVC 128K, AVC 40K #define WAVE521DEC_WORKBUF_SIZE (1784 * 1024) +#define WAVE515DEC_WORKBUF_SIZE (2 * 1024 * 1024) #define MAX_NUM_INSTANCE 32 @@ -49,17 +51,21 @@ /************************************************************************/ #define VLC_BUF_NUM (2) -#define COMMAND_QUEUE_DEPTH (2) +#define WAVE521_COMMAND_QUEUE_DEPTH (2) +#define WAVE515_COMMAND_QUEUE_DEPTH (4) #define W5_REMAP_INDEX0 0 #define W5_REMAP_INDEX1 1 #define W5_REMAP_MAX_SIZE (1024 * 1024) -#define WAVE5_MAX_CODE_BUF_SIZE (2 * 1024 * 1024) -#define WAVE5_TEMPBUF_OFFSET WAVE5_MAX_CODE_BUF_SIZE +#define WAVE521_MAX_CODE_BUF_SIZE (2 * 1024 * 1024) +#define WAVE515_MAX_CODE_BUF_SIZE (1024 * 1024) #define WAVE5_TEMPBUF_SIZE (1024 * 1024) -#define SIZE_COMMON (WAVE5_MAX_CODE_BUF_SIZE + WAVE5_TEMPBUF_SIZE) +#define WAVE521_SIZE_COMMON (WAVE521_MAX_CODE_BUF_SIZE + WAVE5_TEMPBUF_SIZE) +#define WAVE515_ONE_TASKBUF_SIZE (8 * 1024 * 1024) +#define WAVE515_SIZE_COMMON (WAVE515_MAX_CODE_BUF_SIZE + WAVE5_TEMPBUF_SIZE + \ + WAVE515_COMMAND_QUEUE_DEPTH * WAVE515_ONE_TASKBUF_SIZE) //=====4. VPU REPORT MEMORY ======================// diff --git a/drivers/media/platform/chips-media/wave5/wave5.h b/drivers/media/platform/chips-media/wave5/wave5.h index 063028eccd3b..2a29b9164f97 100644 --- a/drivers/media/platform/chips-media/wave5/wave5.h +++ b/drivers/media/platform/chips-media/wave5/wave5.h @@ -22,6 +22,12 @@ */ #define BSOPTION_ENABLE_EXPLICIT_END BIT(0) #define BSOPTION_HIGHLIGHT_STREAM_END BIT(1) +/* + * When RD_PTR_VALID_FLAG is 0 Wave515 ignores RD_PTR value and starts to + * decode from the access unit end position of the last decoded picture in + * bitstream buffer. + */ +#define BSOPTION_RD_PTR_VALID_FLAG BIT(31) /* * Currently the driver only supports hardware with little endian but for source diff --git a/drivers/media/platform/imagination/Kconfig b/drivers/media/platform/imagination/Kconfig new file mode 100644 index 000000000000..7139ae22219b --- /dev/null +++ b/drivers/media/platform/imagination/Kconfig @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0 +config VIDEO_E5010_JPEG_ENC + tristate "Imagination E5010 JPEG Encoder Driver" + depends on VIDEO_DEV + select VIDEOBUF2_DMA_CONTIG + select VIDEOBUF2_VMALLOC + select V4L2_MEM2MEM_DEV + select V4L2_JPEG_HELPER + help + This is a video4linux2 M2M driver for Imagination E5010 JPEG encoder, + which supports JPEG and MJPEG baseline encoding of YUV422 and YUV420 + semiplanar video formats, with resolution ranging from 64x64 to 8K x 8K + pixels. The module will be named as e5010_jpeg_enc. diff --git a/drivers/media/platform/imagination/Makefile b/drivers/media/platform/imagination/Makefile new file mode 100644 index 000000000000..d45b85b88575 --- /dev/null +++ b/drivers/media/platform/imagination/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +e5010_jpeg_enc-objs := e5010-jpeg-enc-hw.o e5010-jpeg-enc.o +obj-$(CONFIG_VIDEO_E5010_JPEG_ENC) += e5010_jpeg_enc.o diff --git a/drivers/media/platform/imagination/e5010-core-regs.h b/drivers/media/platform/imagination/e5010-core-regs.h new file mode 100644 index 000000000000..aaec498fe83f --- /dev/null +++ b/drivers/media/platform/imagination/e5010-core-regs.h @@ -0,0 +1,585 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Imagination E5010 JPEG Encoder driver. + * + * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/ + * + * Author: David Huang <d-huang@ti.com> + * Author: Devarsh Thakkar <devarsht@ti.com> + */ + +#ifndef _E5010_CORE_REGS_H +#define _E5010_CORE_REGS_H + +#define JASPER_CORE_ID_OFFSET (0x0000) +#define JASPER_CORE_ID_CR_GROUP_ID_MASK (0xFF000000) +#define JASPER_CORE_ID_CR_GROUP_ID_SHIFT (24) +#define JASPER_CORE_ID_CR_CORE_ID_MASK (0x00FF0000) +#define JASPER_CORE_ID_CR_CORE_ID_SHIFT (16) +#define JASPER_CORE_ID_CR_UNIQUE_NUM_MASK (0x0000FFF8) +#define JASPER_CORE_ID_CR_UNIQUE_NUM_SHIFT (3) +#define JASPER_CORE_ID_CR_PELS_PER_CYCLE_MASK (0x00000007) +#define JASPER_CORE_ID_CR_PELS_PER_CYCLE_SHIFT (0) + +#define JASPER_CORE_REV_OFFSET (0x0004) +#define JASPER_CORE_REV_CR_JASPER_DESIGNER_MASK (0xFF000000) +#define JASPER_CORE_REV_CR_JASPER_DESIGNER_SHIFT (24) +#define JASPER_CORE_REV_CR_JASPER_MAJOR_REV_MASK (0x00FF0000) +#define JASPER_CORE_REV_CR_JASPER_MAJOR_REV_SHIFT (16) +#define JASPER_CORE_REV_CR_JASPER_MINOR_REV_MASK (0x0000FF00) +#define JASPER_CORE_REV_CR_JASPER_MINOR_REV_SHIFT (8) +#define JASPER_CORE_REV_CR_JASPER_MAINT_REV_MASK (0x000000FF) +#define JASPER_CORE_REV_CR_JASPER_MAINT_REV_SHIFT (0) + +#define JASPER_INTERRUPT_MASK_OFFSET (0x0008) +#define JASPER_INTERRUPT_MASK_CR_OUTPUT_ADDRESS_ERROR_ENABLE_MASK (0x00000002) +#define JASPER_INTERRUPT_MASK_CR_OUTPUT_ADDRESS_ERROR_ENABLE_SHIFT (1) +#define JASPER_INTERRUPT_MASK_CR_PICTURE_DONE_ENABLE_MASK (0x00000001) +#define JASPER_INTERRUPT_MASK_CR_PICTURE_DONE_ENABLE_SHIFT (0) + +#define JASPER_INTERRUPT_STATUS_OFFSET (0x000C) +#define JASPER_INTERRUPT_STATUS_CR_OUTPUT_ADDRESS_ERROR_IRQ_MASK (0x00000002) +#define JASPER_INTERRUPT_STATUS_CR_OUTPUT_ADDRESS_ERROR_IRQ_SHIFT (1) +#define JASPER_INTERRUPT_STATUS_CR_PICTURE_DONE_IRQ_MASK (0x00000001) +#define JASPER_INTERRUPT_STATUS_CR_PICTURE_DONE_IRQ_SHIFT (0) + +#define JASPER_INTERRUPT_CLEAR_OFFSET (0x0010) +#define JASPER_INTERRUPT_CLEAR_CR_OUTPUT_ERROR_CLEAR_MASK (0x00000002) +#define JASPER_INTERRUPT_CLEAR_CR_OUTPUT_ERROR_CLEAR_SHIFT (1) +#define JASPER_INTERRUPT_CLEAR_CR_PICTURE_DONE_CLEAR_MASK (0x00000001) +#define JASPER_INTERRUPT_CLEAR_CR_PICTURE_DONE_CLEAR_SHIFT (0) + +#define JASPER_CLK_CONTROL_OFFSET (0x0014) +#define JASPER_CLK_CONTROL_CR_JASPER_AUTO_CLKG_ENABLE_MASK (0x00000002) +#define JASPER_CLK_CONTROL_CR_JASPER_AUTO_CLKG_ENABLE_SHIFT (1) +#define JASPER_CLK_CONTROL_CR_JASPER_MAN_CLKG_ENABLE_MASK (0x00000001) +#define JASPER_CLK_CONTROL_CR_JASPER_MAN_CLKG_ENABLE_SHIFT (0) + +#define JASPER_CLK_STATUS_OFFSET (0x0018) +#define JASPER_CLK_STATUS_CR_JASPER_CLKG_STATUS_MASK (0x00000001) +#define JASPER_CLK_STATUS_CR_JASPER_CLKG_STATUS_SHIFT (0) + +#define JASPER_RESET_OFFSET (0x001C) +#define JASPER_RESET_CR_SYS_RESET_MASK (0x00000002) +#define JASPER_RESET_CR_SYS_RESET_SHIFT (1) +#define JASPER_RESET_CR_CORE_RESET_MASK (0x00000001) +#define JASPER_RESET_CR_CORE_RESET_SHIFT (0) + +#define JASPER_CORE_CTRL_OFFSET (0x0020) +#define JASPER_CORE_CTRL_CR_JASPER_ENCODE_START_MASK (0x00000001) +#define JASPER_CORE_CTRL_CR_JASPER_ENCODE_START_SHIFT (0) + +#define JASPER_STATUS_OFFSET (0x0024) +#define JASPER_STATUS_CR_FLUSH_MODE_MASK (0x00000002) +#define JASPER_STATUS_CR_FLUSH_MODE_SHIFT (1) +#define JASPER_STATUS_CR_JASPER_BUSY_MASK (0x00000001) +#define JASPER_STATUS_CR_JASPER_BUSY_SHIFT (0) + +#define JASPER_CRC_CLEAR_OFFSET (0x0028) +#define JASPER_CRC_CLEAR_CR_FRONT_END_CRC_CLEAR_MASK (0x00000001) +#define JASPER_CRC_CLEAR_CR_FRONT_END_CRC_CLEAR_SHIFT (0) +#define JASPER_CRC_CLEAR_CR_DCT_CRC_CLEAR_MASK (0x00000002) +#define JASPER_CRC_CLEAR_CR_DCT_CRC_CLEAR_SHIFT (1) +#define JASPER_CRC_CLEAR_CR_ZZ_CRC_CLEAR_MASK (0x00000004) +#define JASPER_CRC_CLEAR_CR_ZZ_CRC_CLEAR_SHIFT (2) +#define JASPER_CRC_CLEAR_CR_QUANT_CRC_CLEAR_MASK (0x00000008) +#define JASPER_CRC_CLEAR_CR_QUANT_CRC_CLEAR_SHIFT (3) +#define JASPER_CRC_CLEAR_CR_ENTROPY_ENCODER_CRC_CLEAR_MASK (0x00000010) +#define JASPER_CRC_CLEAR_CR_ENTROPY_ENCODER_CRC_CLEAR_SHIFT (4) +#define JASPER_CRC_CLEAR_CR_PACKING_BUFFER_CRC_CLEAR_MASK (0x00000020) +#define JASPER_CRC_CLEAR_CR_PACKING_BUFFER_CRC_CLEAR_SHIFT (5) + +#define JASPER_INPUT_CTRL0_OFFSET (0x002C) +#define JASPER_INPUT_CTRL0_CR_INPUT_CHROMA_ORDER_MASK (0x01000000) +#define JASPER_INPUT_CTRL0_CR_INPUT_CHROMA_ORDER_SHIFT (24) +#define JASPER_INPUT_CTRL0_CR_INPUT_SUBSAMPLING_MASK (0x00030000) +#define JASPER_INPUT_CTRL0_CR_INPUT_SUBSAMPLING_SHIFT (16) +#define JASPER_INPUT_CTRL0_CR_INPUT_SOURCE_MASK (0x00000004) +#define JASPER_INPUT_CTRL0_CR_INPUT_SOURCE_SHIFT (2) + +#define JASPER_INPUT_CTRL1_OFFSET (0x0030) +#define JASPER_INPUT_CTRL1_CR_INPUT_LUMA_STRIDE_MASK (0x1FC00000) +#define JASPER_INPUT_CTRL1_CR_INPUT_LUMA_STRIDE_SHIFT (22) +#define JASPER_INPUT_CTRL1_CR_INPUT_CHROMA_STRIDE_MASK (0x00001FC0) +#define JASPER_INPUT_CTRL1_CR_INPUT_CHROMA_STRIDE_SHIFT (6) + +#define JASPER_MMU_CTRL_OFFSET (0x0034) +#define JASPER_MMU_CTRL_CR_JASPER_TILING_SCHEME_MASK (0x00000002) +#define JASPER_MMU_CTRL_CR_JASPER_TILING_SCHEME_SHIFT (1) +#define JASPER_MMU_CTRL_CR_JASPER_TILING_ENABLE_MASK (0x00000001) +#define JASPER_MMU_CTRL_CR_JASPER_TILING_ENABLE_SHIFT (0) + +#define JASPER_IMAGE_SIZE_OFFSET (0x0038) +#define JASPER_IMAGE_SIZE_CR_IMAGE_VERTICAL_SIZE_MASK (0x1FFF0000) +#define JASPER_IMAGE_SIZE_CR_IMAGE_VERTICAL_SIZE_SHIFT (16) +#define JASPER_IMAGE_SIZE_CR_IMAGE_HORIZONTAL_SIZE_MASK (0x00001FFF) +#define JASPER_IMAGE_SIZE_CR_IMAGE_HORIZONTAL_SIZE_SHIFT (0) + +#define INPUT_LUMA_BASE_OFFSET (0x003C) +#define INPUT_LUMA_BASE_CR_INPUT_LUMA_BASE_MASK (0xFFFFFFC0) +#define INPUT_LUMA_BASE_CR_INPUT_LUMA_BASE_SHIFT (6) + +#define INPUT_CHROMA_BASE_OFFSET (0x0040) +#define INPUT_CHROMA_BASE_CR_INPUT_CHROMA_BASE_MASK (0xFFFFFFC0) +#define INPUT_CHROMA_BASE_CR_INPUT_CHROMA_BASE_SHIFT (6) + +#define JASPER_OUTPUT_BASE_OFFSET (0x0044) +#define JASPER_OUTPUT_BASE_CR_OUTPUT_BASE_MASK (0xFFFFFFFF) +#define JASPER_OUTPUT_BASE_CR_OUTPUT_BASE_SHIFT (0) + +#define JASPER_OUTPUT_SIZE_OFFSET (0x0048) +#define JASPER_OUTPUT_SIZE_CR_OUTPUT_SIZE_MASK (0xFFFFFFFF) +#define JASPER_OUTPUT_SIZE_CR_OUTPUT_SIZE_SHIFT (0) +#define JASPER_OUTPUT_MAX_SIZE_OFFSET (0x004C) +#define JASPER_OUTPUT_MAX_SIZE_CR_OUTPUT_MAX_SIZE_MASK (0xFFFFFFFF) +#define JASPER_OUTPUT_MAX_SIZE_CR_OUTPUT_MAX_SIZE_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE0_OFFSET (0x0050) +#define JASPER_LUMA_QUANTIZATION_TABLE0_CR_LUMA_QUANTIZATION_TABLE_03_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE0_CR_LUMA_QUANTIZATION_TABLE_03_SHIFT (24) +#define JASPER_LUMA_QUANTIZATION_TABLE0_CR_LUMA_QUANTIZATION_TABLE_02_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE0_CR_LUMA_QUANTIZATION_TABLE_02_SHIFT (16) +#define JASPER_LUMA_QUANTIZATION_TABLE0_CR_LUMA_QUANTIZATION_TABLE_01_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE0_CR_LUMA_QUANTIZATION_TABLE_01_SHIFT (8) +#define JASPER_LUMA_QUANTIZATION_TABLE0_CR_LUMA_QUANTIZATION_TABLE_00_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE0_CR_LUMA_QUANTIZATION_TABLE_00_SHIFT (0) +#define JASPER_LUMA_QUANTIZATION_TABLE1_OFFSET (0x0054) +#define JASPER_LUMA_QUANTIZATION_TABLE1_CR_LUMA_QUANTIZATION_TABLE_07_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE1_CR_LUMA_QUANTIZATION_TABLE_07_SHIFT (24) +#define JASPER_LUMA_QUANTIZATION_TABLE1_CR_LUMA_QUANTIZATION_TABLE_06_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE1_CR_LUMA_QUANTIZATION_TABLE_06_SHIFT (16) +#define JASPER_LUMA_QUANTIZATION_TABLE1_CR_LUMA_QUANTIZATION_TABLE_05_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE1_CR_LUMA_QUANTIZATION_TABLE_05_SHIFT (8) +#define JASPER_LUMA_QUANTIZATION_TABLE1_CR_LUMA_QUANTIZATION_TABLE_04_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE1_CR_LUMA_QUANTIZATION_TABLE_04_SHIFT (0) +#define JASPER_LUMA_QUANTIZATION_TABLE2_OFFSET (0x0058) +#define JASPER_LUMA_QUANTIZATION_TABLE2_CR_LUMA_QUANTIZATION_TABLE_13_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE2_CR_LUMA_QUANTIZATION_TABLE_13_SHIFT (24) +#define JASPER_LUMA_QUANTIZATION_TABLE2_CR_LUMA_QUANTIZATION_TABLE_12_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE2_CR_LUMA_QUANTIZATION_TABLE_12_SHIFT (16) +#define JASPER_LUMA_QUANTIZATION_TABLE2_CR_LUMA_QUANTIZATION_TABLE_11_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE2_CR_LUMA_QUANTIZATION_TABLE_11_SHIFT (8) +#define JASPER_LUMA_QUANTIZATION_TABLE2_CR_LUMA_QUANTIZATION_TABLE_10_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE2_CR_LUMA_QUANTIZATION_TABLE_10_SHIFT (0) +#define JASPER_LUMA_QUANTIZATION_TABLE3_OFFSET (0x005C) +#define JASPER_LUMA_QUANTIZATION_TABLE3_CR_LUMA_QUANTIZATION_TABLE_17_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE3_CR_LUMA_QUANTIZATION_TABLE_17_SHIFT (24) +#define JASPER_LUMA_QUANTIZATION_TABLE3_CR_LUMA_QUANTIZATION_TABLE_16_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE3_CR_LUMA_QUANTIZATION_TABLE_16_SHIFT (16) +#define JASPER_LUMA_QUANTIZATION_TABLE3_CR_LUMA_QUANTIZATION_TABLE_15_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE3_CR_LUMA_QUANTIZATION_TABLE_15_SHIFT (8) +#define JASPER_LUMA_QUANTIZATION_TABLE3_CR_LUMA_QUANTIZATION_TABLE_14_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE3_CR_LUMA_QUANTIZATION_TABLE_14_SHIFT (0) +#define JASPER_LUMA_QUANTIZATION_TABLE4_OFFSET (0x0060) +#define JASPER_LUMA_QUANTIZATION_TABLE4_CR_LUMA_QUANTIZATION_TABLE_21_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE4_CR_LUMA_QUANTIZATION_TABLE_21_SHIFT (8) +#define JASPER_LUMA_QUANTIZATION_TABLE4_CR_LUMA_QUANTIZATION_TABLE_20_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE4_CR_LUMA_QUANTIZATION_TABLE_20_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE5_OFFSET (0x0064) +#define JASPER_LUMA_QUANTIZATION_TABLE5_CR_LUMA_QUANTIZATION_TABLE_27_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE5_CR_LUMA_QUANTIZATION_TABLE_27_SHIFT (24) +#define JASPER_LUMA_QUANTIZATION_TABLE5_CR_LUMA_QUANTIZATION_TABLE_26_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE5_CR_LUMA_QUANTIZATION_TABLE_26_SHIFT (16) +#define JASPER_LUMA_QUANTIZATION_TABLE5_CR_LUMA_QUANTIZATION_TABLE_25_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE5_CR_LUMA_QUANTIZATION_TABLE_25_SHIFT (8) +#define JASPER_LUMA_QUANTIZATION_TABLE5_CR_LUMA_QUANTIZATION_TABLE_24_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE5_CR_LUMA_QUANTIZATION_TABLE_24_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE6_OFFSET (0x0068) + +#define JASPER_LUMA_QUANTIZATION_TABLE6_CR_LUMA_QUANTIZATION_TABLE_33_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE6_CR_LUMA_QUANTIZATION_TABLE_33_SHIFT (24) + +#define JASPER_LUMA_QUANTIZATION_TABLE6_CR_LUMA_QUANTIZATION_TABLE_32_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE6_CR_LUMA_QUANTIZATION_TABLE_32_SHIFT (16) + +#define JASPER_LUMA_QUANTIZATION_TABLE6_CR_LUMA_QUANTIZATION_TABLE_31_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE6_CR_LUMA_QUANTIZATION_TABLE_31_SHIFT (8) +#define JASPER_LUMA_QUANTIZATION_TABLE6_CR_LUMA_QUANTIZATION_TABLE_30_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE6_CR_LUMA_QUANTIZATION_TABLE_30_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE7_OFFSET (0x006C) + +#define JASPER_LUMA_QUANTIZATION_TABLE7_CR_LUMA_QUANTIZATION_TABLE_37_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE7_CR_LUMA_QUANTIZATION_TABLE_37_SHIFT (24) + +#define JASPER_LUMA_QUANTIZATION_TABLE7_CR_LUMA_QUANTIZATION_TABLE_36_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE7_CR_LUMA_QUANTIZATION_TABLE_36_SHIFT (16) + +#define JASPER_LUMA_QUANTIZATION_TABLE7_CR_LUMA_QUANTIZATION_TABLE_35_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE7_CR_LUMA_QUANTIZATION_TABLE_35_SHIFT (8) + +#define JASPER_LUMA_QUANTIZATION_TABLE7_CR_LUMA_QUANTIZATION_TABLE_34_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE7_CR_LUMA_QUANTIZATION_TABLE_34_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE8_OFFSET (0x0070) + +#define JASPER_LUMA_QUANTIZATION_TABLE8_CR_LUMA_QUANTIZATION_TABLE_43_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE8_CR_LUMA_QUANTIZATION_TABLE_43_SHIFT (24) +#define JASPER_LUMA_QUANTIZATION_TABLE8_CR_LUMA_QUANTIZATION_TABLE_42_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE8_CR_LUMA_QUANTIZATION_TABLE_42_SHIFT (16) + +#define JASPER_LUMA_QUANTIZATION_TABLE8_CR_LUMA_QUANTIZATION_TABLE_41_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE8_CR_LUMA_QUANTIZATION_TABLE_41_SHIFT (8) + +#define JASPER_LUMA_QUANTIZATION_TABLE8_CR_LUMA_QUANTIZATION_TABLE_40_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE8_CR_LUMA_QUANTIZATION_TABLE_40_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE9_OFFSET (0x0074) + +#define JASPER_LUMA_QUANTIZATION_TABLE9_CR_LUMA_QUANTIZATION_TABLE_47_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE9_CR_LUMA_QUANTIZATION_TABLE_47_SHIFT (24) + +#define JASPER_LUMA_QUANTIZATION_TABLE9_CR_LUMA_QUANTIZATION_TABLE_46_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE9_CR_LUMA_QUANTIZATION_TABLE_46_SHIFT (16) + +#define JASPER_LUMA_QUANTIZATION_TABLE9_CR_LUMA_QUANTIZATION_TABLE_45_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE9_CR_LUMA_QUANTIZATION_TABLE_45_SHIFT (8) + +#define JASPER_LUMA_QUANTIZATION_TABLE9_CR_LUMA_QUANTIZATION_TABLE_44_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE9_CR_LUMA_QUANTIZATION_TABLE_44_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE10_OFFSET (0x0078) + +#define JASPER_LUMA_QUANTIZATION_TABLE10_CR_LUMA_QUANTIZATION_TABLE_53_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE10_CR_LUMA_QUANTIZATION_TABLE_53_SHIFT (24) + +#define JASPER_LUMA_QUANTIZATION_TABLE10_CR_LUMA_QUANTIZATION_TABLE_52_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE10_CR_LUMA_QUANTIZATION_TABLE_52_SHIFT (16) + +#define JASPER_LUMA_QUANTIZATION_TABLE10_CR_LUMA_QUANTIZATION_TABLE_51_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE10_CR_LUMA_QUANTIZATION_TABLE_51_SHIFT (8) + +#define JASPER_LUMA_QUANTIZATION_TABLE10_CR_LUMA_QUANTIZATION_TABLE_50_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE10_CR_LUMA_QUANTIZATION_TABLE_50_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE11_OFFSET (0x007C) + +#define JASPER_LUMA_QUANTIZATION_TABLE11_CR_LUMA_QUANTIZATION_TABLE_57_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE11_CR_LUMA_QUANTIZATION_TABLE_57_SHIFT (24) + +#define JASPER_LUMA_QUANTIZATION_TABLE11_CR_LUMA_QUANTIZATION_TABLE_56_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE11_CR_LUMA_QUANTIZATION_TABLE_56_SHIFT (16) + +#define JASPER_LUMA_QUANTIZATION_TABLE11_CR_LUMA_QUANTIZATION_TABLE_55_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE11_CR_LUMA_QUANTIZATION_TABLE_55_SHIFT (8) + +#define JASPER_LUMA_QUANTIZATION_TABLE11_CR_LUMA_QUANTIZATION_TABLE_54_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE11_CR_LUMA_QUANTIZATION_TABLE_54_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE12_OFFSET (0x0080) + +#define JASPER_LUMA_QUANTIZATION_TABLE12_CR_LUMA_QUANTIZATION_TABLE_63_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE12_CR_LUMA_QUANTIZATION_TABLE_63_SHIFT (24) + +#define JASPER_LUMA_QUANTIZATION_TABLE12_CR_LUMA_QUANTIZATION_TABLE_62_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE12_CR_LUMA_QUANTIZATION_TABLE_62_SHIFT (16) + +#define JASPER_LUMA_QUANTIZATION_TABLE12_CR_LUMA_QUANTIZATION_TABLE_61_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE12_CR_LUMA_QUANTIZATION_TABLE_61_SHIFT (8) + +#define JASPER_LUMA_QUANTIZATION_TABLE12_CR_LUMA_QUANTIZATION_TABLE_60_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE12_CR_LUMA_QUANTIZATION_TABLE_60_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE13_OFFSET (0x0084) + +#define JASPER_LUMA_QUANTIZATION_TABLE13_CR_LUMA_QUANTIZATION_TABLE_67_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE13_CR_LUMA_QUANTIZATION_TABLE_67_SHIFT (24) + +#define JASPER_LUMA_QUANTIZATION_TABLE13_CR_LUMA_QUANTIZATION_TABLE_66_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE13_CR_LUMA_QUANTIZATION_TABLE_66_SHIFT (16) + +#define JASPER_LUMA_QUANTIZATION_TABLE13_CR_LUMA_QUANTIZATION_TABLE_65_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE13_CR_LUMA_QUANTIZATION_TABLE_65_SHIFT (8) + +#define JASPER_LUMA_QUANTIZATION_TABLE13_CR_LUMA_QUANTIZATION_TABLE_64_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE13_CR_LUMA_QUANTIZATION_TABLE_64_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE14_OFFSET (0x0088) + +#define JASPER_LUMA_QUANTIZATION_TABLE14_CR_LUMA_QUANTIZATION_TABLE_73_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE14_CR_LUMA_QUANTIZATION_TABLE_73_SHIFT (24) + +#define JASPER_LUMA_QUANTIZATION_TABLE14_CR_LUMA_QUANTIZATION_TABLE_72_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE14_CR_LUMA_QUANTIZATION_TABLE_72_SHIFT (16) + +#define JASPER_LUMA_QUANTIZATION_TABLE14_CR_LUMA_QUANTIZATION_TABLE_71_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE14_CR_LUMA_QUANTIZATION_TABLE_71_SHIFT (8) + +#define JASPER_LUMA_QUANTIZATION_TABLE14_CR_LUMA_QUANTIZATION_TABLE_70_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE14_CR_LUMA_QUANTIZATION_TABLE_70_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE15_OFFSET (0x008C) + +#define JASPER_LUMA_QUANTIZATION_TABLE15_CR_LUMA_QUANTIZATION_TABLE_77_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE15_CR_LUMA_QUANTIZATION_TABLE_77_SHIFT (24) + +#define JASPER_LUMA_QUANTIZATION_TABLE15_CR_LUMA_QUANTIZATION_TABLE_76_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE15_CR_LUMA_QUANTIZATION_TABLE_76_SHIFT (16) + +#define JASPER_LUMA_QUANTIZATION_TABLE15_CR_LUMA_QUANTIZATION_TABLE_75_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE15_CR_LUMA_QUANTIZATION_TABLE_75_SHIFT (8) + +#define JASPER_LUMA_QUANTIZATION_TABLE15_CR_LUMA_QUANTIZATION_TABLE_74_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE15_CR_LUMA_QUANTIZATION_TABLE_74_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE0_OFFSET (0x0090) + +#define JASPER_CHROMA_QUANTIZATION_TABLE0_CR_CHROMA_QUANTIZATION_TABLE_03_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE0_CR_CHROMA_QUANTIZATION_TABLE_03_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE0_CR_CHROMA_QUANTIZATION_TABLE_02_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE0_CR_CHROMA_QUANTIZATION_TABLE_02_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE0_CR_CHROMA_QUANTIZATION_TABLE_01_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE0_CR_CHROMA_QUANTIZATION_TABLE_01_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE0_CR_CHROMA_QUANTIZATION_TABLE_00_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE0_CR_CHROMA_QUANTIZATION_TABLE_00_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE1_OFFSET (0x0094) + +#define JASPER_CHROMA_QUANTIZATION_TABLE1_CR_CHROMA_QUANTIZATION_TABLE_07_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE1_CR_CHROMA_QUANTIZATION_TABLE_07_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE1_CR_CHROMA_QUANTIZATION_TABLE_06_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE1_CR_CHROMA_QUANTIZATION_TABLE_06_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE1_CR_CHROMA_QUANTIZATION_TABLE_05_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE1_CR_CHROMA_QUANTIZATION_TABLE_05_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE1_CR_CHROMA_QUANTIZATION_TABLE_04_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE1_CR_CHROMA_QUANTIZATION_TABLE_04_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE2_OFFSET (0x0098) + +#define JASPER_CHROMA_QUANTIZATION_TABLE2_CR_CHROMA_QUANTIZATION_TABLE_13_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE2_CR_CHROMA_QUANTIZATION_TABLE_13_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE2_CR_CHROMA_QUANTIZATION_TABLE_12_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE2_CR_CHROMA_QUANTIZATION_TABLE_12_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE2_CR_CHROMA_QUANTIZATION_TABLE_11_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE2_CR_CHROMA_QUANTIZATION_TABLE_11_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE2_CR_CHROMA_QUANTIZATION_TABLE_10_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE2_CR_CHROMA_QUANTIZATION_TABLE_10_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE3_OFFSET (0x009C) + +#define JASPER_CHROMA_QUANTIZATION_TABLE3_CR_CHROMA_QUANTIZATION_TABLE_17_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE3_CR_CHROMA_QUANTIZATION_TABLE_17_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE3_CR_CHROMA_QUANTIZATION_TABLE_16_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE3_CR_CHROMA_QUANTIZATION_TABLE_16_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE3_CR_CHROMA_QUANTIZATION_TABLE_15_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE3_CR_CHROMA_QUANTIZATION_TABLE_15_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE3_CR_CHROMA_QUANTIZATION_TABLE_14_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE3_CR_CHROMA_QUANTIZATION_TABLE_14_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE4_OFFSET (0x00A0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE4_CR_CHROMA_QUANTIZATION_TABLE_23_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE4_CR_CHROMA_QUANTIZATION_TABLE_23_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE4_CR_CHROMA_QUANTIZATION_TABLE_22_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE4_CR_CHROMA_QUANTIZATION_TABLE_22_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE4_CR_CHROMA_QUANTIZATION_TABLE_21_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE4_CR_CHROMA_QUANTIZATION_TABLE_21_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE4_CR_CHROMA_QUANTIZATION_TABLE_20_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE4_CR_CHROMA_QUANTIZATION_TABLE_20_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE5_OFFSET (0x00A4) + +#define JASPER_CHROMA_QUANTIZATION_TABLE5_CR_CHROMA_QUANTIZATION_TABLE_27_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE5_CR_CHROMA_QUANTIZATION_TABLE_27_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE5_CR_CHROMA_QUANTIZATION_TABLE_26_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE5_CR_CHROMA_QUANTIZATION_TABLE_26_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE5_CR_CHROMA_QUANTIZATION_TABLE_25_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE5_CR_CHROMA_QUANTIZATION_TABLE_25_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE5_CR_CHROMA_QUANTIZATION_TABLE_24_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE5_CR_CHROMA_QUANTIZATION_TABLE_24_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE6_OFFSET (0x00A8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE6_CR_CHROMA_QUANTIZATION_TABLE_33_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE6_CR_CHROMA_QUANTIZATION_TABLE_33_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE6_CR_CHROMA_QUANTIZATION_TABLE_32_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE6_CR_CHROMA_QUANTIZATION_TABLE_32_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE6_CR_CHROMA_QUANTIZATION_TABLE_31_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE6_CR_CHROMA_QUANTIZATION_TABLE_31_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE6_CR_CHROMA_QUANTIZATION_TABLE_30_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE6_CR_CHROMA_QUANTIZATION_TABLE_30_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE7_OFFSET (0x00AC) + +#define JASPER_CHROMA_QUANTIZATION_TABLE7_CR_CHROMA_QUANTIZATION_TABLE_37_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE7_CR_CHROMA_QUANTIZATION_TABLE_37_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE7_CR_CHROMA_QUANTIZATION_TABLE_36_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE7_CR_CHROMA_QUANTIZATION_TABLE_36_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE7_CR_CHROMA_QUANTIZATION_TABLE_35_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE7_CR_CHROMA_QUANTIZATION_TABLE_35_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE7_CR_CHROMA_QUANTIZATION_TABLE_34_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE7_CR_CHROMA_QUANTIZATION_TABLE_34_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE8_OFFSET (0x00B0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE8_CR_CHROMA_QUANTIZATION_TABLE_43_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE8_CR_CHROMA_QUANTIZATION_TABLE_43_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE8_CR_CHROMA_QUANTIZATION_TABLE_42_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE8_CR_CHROMA_QUANTIZATION_TABLE_42_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE8_CR_CHROMA_QUANTIZATION_TABLE_41_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE8_CR_CHROMA_QUANTIZATION_TABLE_41_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE8_CR_CHROMA_QUANTIZATION_TABLE_40_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE8_CR_CHROMA_QUANTIZATION_TABLE_40_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE9_OFFSET (0x00B4) + +#define JASPER_CHROMA_QUANTIZATION_TABLE9_CR_CHROMA_QUANTIZATION_TABLE_47_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE9_CR_CHROMA_QUANTIZATION_TABLE_47_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE9_CR_CHROMA_QUANTIZATION_TABLE_46_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE9_CR_CHROMA_QUANTIZATION_TABLE_46_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE9_CR_CHROMA_QUANTIZATION_TABLE_45_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE9_CR_CHROMA_QUANTIZATION_TABLE_45_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE9_CR_CHROMA_QUANTIZATION_TABLE_44_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE9_CR_CHROMA_QUANTIZATION_TABLE_44_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE10_OFFSET (0x00B8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE10_CR_CHROMA_QUANTIZATION_TABLE_53_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE10_CR_CHROMA_QUANTIZATION_TABLE_53_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE10_CR_CHROMA_QUANTIZATION_TABLE_52_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE10_CR_CHROMA_QUANTIZATION_TABLE_52_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE10_CR_CHROMA_QUANTIZATION_TABLE_51_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE10_CR_CHROMA_QUANTIZATION_TABLE_51_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE10_CR_CHROMA_QUANTIZATION_TABLE_50_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE10_CR_CHROMA_QUANTIZATION_TABLE_50_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE11_OFFSET (0x00BC) + +#define JASPER_CHROMA_QUANTIZATION_TABLE11_CR_CHROMA_QUANTIZATION_TABLE_57_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE11_CR_CHROMA_QUANTIZATION_TABLE_57_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE11_CR_CHROMA_QUANTIZATION_TABLE_56_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE11_CR_CHROMA_QUANTIZATION_TABLE_56_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE11_CR_CHROMA_QUANTIZATION_TABLE_55_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE11_CR_CHROMA_QUANTIZATION_TABLE_55_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE11_CR_CHROMA_QUANTIZATION_TABLE_54_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE11_CR_CHROMA_QUANTIZATION_TABLE_54_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE12_OFFSET (0x00C0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE12_CR_CHROMA_QUANTIZATION_TABLE_63_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE12_CR_CHROMA_QUANTIZATION_TABLE_63_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE12_CR_CHROMA_QUANTIZATION_TABLE_62_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE12_CR_CHROMA_QUANTIZATION_TABLE_62_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE12_CR_CHROMA_QUANTIZATION_TABLE_61_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE12_CR_CHROMA_QUANTIZATION_TABLE_61_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE12_CR_CHROMA_QUANTIZATION_TABLE_60_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE12_CR_CHROMA_QUANTIZATION_TABLE_60_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE13_OFFSET (0x00C4) + +#define JASPER_CHROMA_QUANTIZATION_TABLE13_CR_CHROMA_QUANTIZATION_TABLE_67_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE13_CR_CHROMA_QUANTIZATION_TABLE_67_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE13_CR_CHROMA_QUANTIZATION_TABLE_66_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE13_CR_CHROMA_QUANTIZATION_TABLE_66_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE13_CR_CHROMA_QUANTIZATION_TABLE_65_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE13_CR_CHROMA_QUANTIZATION_TABLE_65_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE13_CR_CHROMA_QUANTIZATION_TABLE_64_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE13_CR_CHROMA_QUANTIZATION_TABLE_64_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE14_OFFSET (0x00C8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE14_CR_CHROMA_QUANTIZATION_TABLE_73_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE14_CR_CHROMA_QUANTIZATION_TABLE_73_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE14_CR_CHROMA_QUANTIZATION_TABLE_72_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE14_CR_CHROMA_QUANTIZATION_TABLE_72_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE14_CR_CHROMA_QUANTIZATION_TABLE_71_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE14_CR_CHROMA_QUANTIZATION_TABLE_71_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE14_CR_CHROMA_QUANTIZATION_TABLE_70_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE14_CR_CHROMA_QUANTIZATION_TABLE_70_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE15_OFFSET (0x00CC) + +#define JASPER_CHROMA_QUANTIZATION_TABLE15_CR_CHROMA_QUANTIZATION_TABLE_77_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE15_CR_CHROMA_QUANTIZATION_TABLE_77_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE15_CR_CHROMA_QUANTIZATION_TABLE_76_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE15_CR_CHROMA_QUANTIZATION_TABLE_76_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE15_CR_CHROMA_QUANTIZATION_TABLE_75_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE15_CR_CHROMA_QUANTIZATION_TABLE_75_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE15_CR_CHROMA_QUANTIZATION_TABLE_74_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE15_CR_CHROMA_QUANTIZATION_TABLE_74_SHIFT (0) + +#define JASPER_CRC_CTRL_OFFSET (0x00D0) +#define JASPER_CRC_CTRL_JASPER_CRC_ENABLE_MASK (0x00000001) +#define JASPER_CRC_CTRL_JASPER_CRC_ENABLE_SHIFT (0) + +#define JASPER_FRONT_END_CRC_OFFSET (0x00D4) +#define JASPER_FRONT_END_CRC_CR_JASPER_FRONT_END_CRC_OUT_MASK (0xFFFFFFFF) +#define JASPER_FRONT_END_CRC_CR_JASPER_FRONT_END_CRC_OUT_SHIFT (0) + +#define JASPER_DCT_CRC_OFFSET (0x00D8) +#define JASPER_DCT_CRC_CR_JASPER_DCT_CRC_OUT_MASK (0xFFFFFFFF) +#define JASPER_DCT_CRC_CR_JASPER_DCT_CRC_OUT_SHIFT (0) + +#define JASPER_ZZ_CRC_OFFSET (0x00DC) +#define JASPER_ZZ_CRC_CR_JASPER_ZZ_CRC_OUT_MASK (0xFFFFFFFF) +#define JASPER_ZZ_CRC_CR_JASPER_ZZ_CRC_OUT_SHIFT (0) + +#define JASPER_QUANT_CRC_OFFSET (0x00E0) +#define JASPER_QUANT_CRC_CR_JASPER_QUANT_CRC_OUT_MASK (0xFFFFFFFF) +#define JASPER_QUANT_CRC_CR_JASPER_QUANT_CRC_OUT_SHIFT (0) + +#define JASPER_ENTROPY_ENCODER_CRC_OFFSET (0x00E4) +#define JASPER_ENTROPY_ENCODER_CRC_CR_JASPER_ENTROPY_CRC_OUT_MASK (0xFFFFFFFF) +#define JASPER_ENTROPY_ENCODER_CRC_CR_JASPER_ENTROPY_CRC_OUT_SHIFT (0) + +#define JASPER_PACKING_BUFFER_DATA_CRC_OFFSET (0x00E8) +#define JASPER_PACKING_BUFFER_DATA_CRC_CR_JASPER_PACKING_DATA_CRC_OUT_MASK (0xFFFFFFFF) +#define JASPER_PACKING_BUFFER_DATA_CRC_CR_JASPER_PACKING_DATA_CRC_OUT_SHIFT (0) + +#define JASPER_PACKING_BUFFER_ADDR_CRC_OFFSET (0x00EC) +#define JASPER_PACKING_BUFFER_ADDR_CRC_CR_JASPER_PACKING_ADDR_OUT_CRC_MASK (0xFFFFFFFF) +#define JASPER_PACKING_BUFFER_ADDR_CRC_CR_JASPER_PACKING_ADDR_OUT_CRC_SHIFT (0) + +#define JASPER_CORE_BYTE_SIZE (0x00F0) + +#endif diff --git a/drivers/media/platform/imagination/e5010-jpeg-enc-hw.c b/drivers/media/platform/imagination/e5010-jpeg-enc-hw.c new file mode 100644 index 000000000000..56d5941020fa --- /dev/null +++ b/drivers/media/platform/imagination/e5010-jpeg-enc-hw.c @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Imagination E5010 JPEG Encoder driver. + * + * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/ + * + * Author: David Huang <d-huang@ti.com> + * Author: Devarsh Thakkar <devarsht@ti.com> + */ + +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/dev_printk.h> +#include "e5010-jpeg-enc-hw.h" + +static void write_reg_field(void __iomem *base, unsigned int offset, u32 mask, + unsigned int shift, u32 value) +{ + u32 reg; + + value <<= shift; + if (mask != 0xffffffff) { + reg = readl(base + offset); + value = (value & mask) | (reg & ~mask); + } + writel(value, (base + offset)); +} + +static int write_reg_field_not_busy(void __iomem *jasper_base, void __iomem *wr_base, + unsigned int offset, u32 mask, unsigned int shift, + u32 value) +{ + int ret; + u32 val; + + ret = readl_poll_timeout_atomic(jasper_base + JASPER_STATUS_OFFSET, val, + (val & JASPER_STATUS_CR_JASPER_BUSY_MASK) == 0, + 2000, 50000); + if (ret) + return ret; + + write_reg_field(wr_base, offset, mask, shift, value); + + return 0; +} + +void e5010_reset(struct device *dev, void __iomem *core_base, void __iomem *mmu_base) +{ + int ret = 0; + u32 val; + + write_reg_field(core_base, JASPER_RESET_OFFSET, + JASPER_RESET_CR_CORE_RESET_MASK, + JASPER_RESET_CR_CORE_RESET_SHIFT, 1); + + write_reg_field(mmu_base, MMU_MMU_CONTROL1_OFFSET, + MMU_MMU_CONTROL1_MMU_SOFT_RESET_MASK, + MMU_MMU_CONTROL1_MMU_SOFT_RESET_SHIFT, 1); + + ret = readl_poll_timeout_atomic(mmu_base + MMU_MMU_CONTROL1_OFFSET, val, + (val & MMU_MMU_CONTROL1_MMU_SOFT_RESET_MASK) == 0, + 2000, 50000); + if (ret) + dev_warn(dev, "MMU soft reset timed out, forcing system soft reset\n"); + + write_reg_field(core_base, JASPER_RESET_OFFSET, + JASPER_RESET_CR_SYS_RESET_MASK, + JASPER_RESET_CR_SYS_RESET_SHIFT, 1); +} + +void e5010_hw_bypass_mmu(void __iomem *mmu_base, u32 enable) +{ + /* Bypass MMU */ + write_reg_field(mmu_base, + MMU_MMU_ADDRESS_CONTROL_OFFSET, + MMU_MMU_ADDRESS_CONTROL_MMU_BYPASS_MASK, + MMU_MMU_ADDRESS_CONTROL_MMU_BYPASS_SHIFT, + enable); +} + +int e5010_hw_enable_output_address_error_irq(void __iomem *core_base, u32 enable) +{ + return write_reg_field_not_busy(core_base, core_base, + JASPER_INTERRUPT_MASK_OFFSET, + JASPER_INTERRUPT_MASK_CR_OUTPUT_ADDRESS_ERROR_ENABLE_MASK, + JASPER_INTERRUPT_MASK_CR_OUTPUT_ADDRESS_ERROR_ENABLE_SHIFT, + enable); +} + +bool e5010_hw_pic_done_irq(void __iomem *core_base) +{ + u32 reg; + + reg = readl(core_base + JASPER_INTERRUPT_STATUS_OFFSET); + return reg & JASPER_INTERRUPT_STATUS_CR_PICTURE_DONE_IRQ_MASK; +} + +bool e5010_hw_output_address_irq(void __iomem *core_base) +{ + u32 reg; + + reg = readl(core_base + JASPER_INTERRUPT_STATUS_OFFSET); + return reg & JASPER_INTERRUPT_STATUS_CR_OUTPUT_ADDRESS_ERROR_IRQ_MASK; +} + +int e5010_hw_enable_picture_done_irq(void __iomem *core_base, u32 enable) +{ + return write_reg_field_not_busy(core_base, core_base, + JASPER_INTERRUPT_MASK_OFFSET, + JASPER_INTERRUPT_MASK_CR_PICTURE_DONE_ENABLE_MASK, + JASPER_INTERRUPT_MASK_CR_PICTURE_DONE_ENABLE_SHIFT, + enable); +} + +int e5010_hw_enable_auto_clock_gating(void __iomem *core_base, u32 enable) +{ + return write_reg_field_not_busy(core_base, core_base, + JASPER_CLK_CONTROL_OFFSET, + JASPER_CLK_CONTROL_CR_JASPER_AUTO_CLKG_ENABLE_MASK, + JASPER_CLK_CONTROL_CR_JASPER_AUTO_CLKG_ENABLE_SHIFT, + enable); +} + +int e5010_hw_enable_manual_clock_gating(void __iomem *core_base, u32 enable) +{ + return write_reg_field_not_busy(core_base, core_base, + JASPER_CLK_CONTROL_OFFSET, + JASPER_CLK_CONTROL_CR_JASPER_MAN_CLKG_ENABLE_MASK, + JASPER_CLK_CONTROL_CR_JASPER_MAN_CLKG_ENABLE_SHIFT, 0); +} + +int e5010_hw_enable_crc_check(void __iomem *core_base, u32 enable) +{ + return write_reg_field_not_busy(core_base, core_base, + JASPER_CRC_CTRL_OFFSET, + JASPER_CRC_CTRL_JASPER_CRC_ENABLE_MASK, + JASPER_CRC_CTRL_JASPER_CRC_ENABLE_SHIFT, enable); +} + +int e5010_hw_set_input_source_to_memory(void __iomem *core_base, u32 set) +{ + return write_reg_field_not_busy(core_base, core_base, + JASPER_INPUT_CTRL0_OFFSET, + JASPER_INPUT_CTRL0_CR_INPUT_SOURCE_MASK, + JASPER_INPUT_CTRL0_CR_INPUT_SOURCE_SHIFT, set); +} + +int e5010_hw_set_input_luma_addr(void __iomem *core_base, u32 val) +{ + return write_reg_field_not_busy(core_base, core_base, + INPUT_LUMA_BASE_OFFSET, + INPUT_LUMA_BASE_CR_INPUT_LUMA_BASE_MASK, 0, val); +} + +int e5010_hw_set_input_chroma_addr(void __iomem *core_base, u32 val) +{ + return write_reg_field_not_busy(core_base, core_base, + INPUT_CHROMA_BASE_OFFSET, + INPUT_CHROMA_BASE_CR_INPUT_CHROMA_BASE_MASK, 0, val); +} + +int e5010_hw_set_output_base_addr(void __iomem *core_base, u32 val) +{ + return write_reg_field_not_busy(core_base, core_base, + JASPER_OUTPUT_BASE_OFFSET, + JASPER_OUTPUT_BASE_CR_OUTPUT_BASE_MASK, + JASPER_OUTPUT_BASE_CR_OUTPUT_BASE_SHIFT, val); +} + +int e5010_hw_set_horizontal_size(void __iomem *core_base, u32 val) +{ + return write_reg_field_not_busy(core_base, core_base, + JASPER_IMAGE_SIZE_OFFSET, + JASPER_IMAGE_SIZE_CR_IMAGE_HORIZONTAL_SIZE_MASK, + JASPER_IMAGE_SIZE_CR_IMAGE_HORIZONTAL_SIZE_SHIFT, + val); +} + +int e5010_hw_set_vertical_size(void __iomem *core_base, u32 val) +{ + return write_reg_field_not_busy(core_base, core_base, + JASPER_IMAGE_SIZE_OFFSET, + JASPER_IMAGE_SIZE_CR_IMAGE_VERTICAL_SIZE_MASK, + JASPER_IMAGE_SIZE_CR_IMAGE_VERTICAL_SIZE_SHIFT, + val); +} + +int e5010_hw_set_luma_stride(void __iomem *core_base, u32 bytesperline) +{ + u32 val = bytesperline / 64; + + return write_reg_field_not_busy(core_base, core_base, + JASPER_INPUT_CTRL1_OFFSET, + JASPER_INPUT_CTRL1_CR_INPUT_LUMA_STRIDE_MASK, + JASPER_INPUT_CTRL1_CR_INPUT_LUMA_STRIDE_SHIFT, + val); +} + +int e5010_hw_set_chroma_stride(void __iomem *core_base, u32 bytesperline) +{ + u32 val = bytesperline / 64; + + return write_reg_field_not_busy(core_base, core_base, + JASPER_INPUT_CTRL1_OFFSET, + JASPER_INPUT_CTRL1_CR_INPUT_CHROMA_STRIDE_MASK, + JASPER_INPUT_CTRL1_CR_INPUT_CHROMA_STRIDE_SHIFT, + val); +} + +int e5010_hw_set_input_subsampling(void __iomem *core_base, u32 val) +{ + return write_reg_field_not_busy(core_base, core_base, + JASPER_INPUT_CTRL0_OFFSET, + JASPER_INPUT_CTRL0_CR_INPUT_SUBSAMPLING_MASK, + JASPER_INPUT_CTRL0_CR_INPUT_SUBSAMPLING_SHIFT, + val); +} + +int e5010_hw_set_chroma_order(void __iomem *core_base, u32 val) +{ + return write_reg_field_not_busy(core_base, core_base, + JASPER_INPUT_CTRL0_OFFSET, + JASPER_INPUT_CTRL0_CR_INPUT_CHROMA_ORDER_MASK, + JASPER_INPUT_CTRL0_CR_INPUT_CHROMA_ORDER_SHIFT, + val); +} + +void e5010_hw_set_output_max_size(void __iomem *core_base, u32 val) +{ + write_reg_field(core_base, JASPER_OUTPUT_MAX_SIZE_OFFSET, + JASPER_OUTPUT_MAX_SIZE_CR_OUTPUT_MAX_SIZE_MASK, + JASPER_OUTPUT_MAX_SIZE_CR_OUTPUT_MAX_SIZE_SHIFT, + val); +} + +int e5010_hw_set_qpvalue(void __iomem *core_base, u32 offset, u32 val) +{ + return write_reg_field_not_busy(core_base, core_base, offset, 0xffffffff, 0, val); +} + +void e5010_hw_clear_output_error(void __iomem *core_base, u32 clear) +{ + /* Make sure interrupts are clear */ + write_reg_field(core_base, JASPER_INTERRUPT_CLEAR_OFFSET, + JASPER_INTERRUPT_CLEAR_CR_OUTPUT_ERROR_CLEAR_MASK, + JASPER_INTERRUPT_CLEAR_CR_OUTPUT_ERROR_CLEAR_SHIFT, clear); +} + +void e5010_hw_clear_picture_done(void __iomem *core_base, u32 clear) +{ + write_reg_field(core_base, + JASPER_INTERRUPT_CLEAR_OFFSET, + JASPER_INTERRUPT_CLEAR_CR_PICTURE_DONE_CLEAR_MASK, + JASPER_INTERRUPT_CLEAR_CR_PICTURE_DONE_CLEAR_SHIFT, clear); +} + +int e5010_hw_get_output_size(void __iomem *core_base) +{ + return readl(core_base + JASPER_OUTPUT_SIZE_OFFSET); +} + +void e5010_hw_encode_start(void __iomem *core_base, u32 start) +{ + write_reg_field(core_base, JASPER_CORE_CTRL_OFFSET, + JASPER_CORE_CTRL_CR_JASPER_ENCODE_START_MASK, + JASPER_CORE_CTRL_CR_JASPER_ENCODE_START_SHIFT, start); +} diff --git a/drivers/media/platform/imagination/e5010-jpeg-enc-hw.h b/drivers/media/platform/imagination/e5010-jpeg-enc-hw.h new file mode 100644 index 000000000000..781d353c3226 --- /dev/null +++ b/drivers/media/platform/imagination/e5010-jpeg-enc-hw.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Imagination E5010 JPEG Encoder driver. + * + * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/ + * + * Author: David Huang <d-huang@ti.com> + * Author: Devarsh Thakkar <devarsht@ti.com> + */ + +#ifndef _E5010_JPEG_ENC_HW_H +#define _E5010_JPEG_ENC_HW_H + +#include "e5010-core-regs.h" +#include "e5010-mmu-regs.h" + +int e5010_hw_enable_output_address_error_irq(void __iomem *core_offset, u32 enable); +int e5010_hw_enable_picture_done_irq(void __iomem *core_offset, u32 enable); +int e5010_hw_enable_auto_clock_gating(void __iomem *core_offset, u32 enable); +int e5010_hw_enable_manual_clock_gating(void __iomem *core_offset, u32 enable); +int e5010_hw_enable_crc_check(void __iomem *core_offset, u32 enable); +int e5010_hw_set_input_source_to_memory(void __iomem *core_offset, u32 set); +int e5010_hw_set_input_luma_addr(void __iomem *core_offset, u32 val); +int e5010_hw_set_input_chroma_addr(void __iomem *core_offset, u32 val); +int e5010_hw_set_output_base_addr(void __iomem *core_offset, u32 val); +int e5010_hw_get_output_size(void __iomem *core_offset); +int e5010_hw_set_horizontal_size(void __iomem *core_offset, u32 val); +int e5010_hw_set_vertical_size(void __iomem *core_offset, u32 val); +int e5010_hw_set_luma_stride(void __iomem *core_offset, u32 bytesperline); +int e5010_hw_set_chroma_stride(void __iomem *core_offset, u32 bytesperline); +int e5010_hw_set_input_subsampling(void __iomem *core_offset, u32 val); +int e5010_hw_set_chroma_order(void __iomem *core_offset, u32 val); +int e5010_hw_set_qpvalue(void __iomem *core_offset, u32 offset, u32 value); +void e5010_reset(struct device *dev, void __iomem *core_offset, void __iomem *mmu_offset); +void e5010_hw_set_output_max_size(void __iomem *core_offset, u32 val); +void e5010_hw_clear_picture_done(void __iomem *core_offset, u32 clear); +void e5010_hw_encode_start(void __iomem *core_offset, u32 start); +void e5010_hw_clear_output_error(void __iomem *core_offset, u32 clear); +void e5010_hw_bypass_mmu(void __iomem *mmu_base, u32 enable); +bool e5010_hw_pic_done_irq(void __iomem *core_base); +bool e5010_hw_output_address_irq(void __iomem *core_base); +#endif diff --git a/drivers/media/platform/imagination/e5010-jpeg-enc.c b/drivers/media/platform/imagination/e5010-jpeg-enc.c new file mode 100644 index 000000000000..187f2d8abfbb --- /dev/null +++ b/drivers/media/platform/imagination/e5010-jpeg-enc.c @@ -0,0 +1,1632 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Imagination E5010 JPEG Encoder driver. + * + * TODO: Add MMU and memory tiling support + * + * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/ + * + * Author: David Huang <d-huang@ti.com> + * Author: Devarsh Thakkar <devarsht@ti.com> + */ + +#include <linux/clk.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/ioctl.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/pm_runtime.h> +#include <media/jpeg.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-jpeg.h> +#include <media/v4l2-rect.h> +#include <media/v4l2-mem2mem.h> +#include <media/videobuf2-dma-contig.h> +#include <media/videobuf2-v4l2.h> +#include "e5010-jpeg-enc.h" +#include "e5010-jpeg-enc-hw.h" + +/* forward declarations */ +static const struct of_device_id e5010_of_match[]; + +static const struct v4l2_file_operations e5010_fops; + +static const struct v4l2_ioctl_ops e5010_ioctl_ops; + +static const struct vb2_ops e5010_video_ops; + +static const struct v4l2_m2m_ops e5010_m2m_ops; + +static struct e5010_fmt e5010_formats[] = { + { + .fourcc = V4L2_PIX_FMT_NV12, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, + .chroma_order = CHROMA_ORDER_CB_CR, + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64, + MIN_DIMENSION, MAX_DIMENSION, 8 }, + }, + { + .fourcc = V4L2_PIX_FMT_NV12M, + .num_planes = 2, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, + .chroma_order = CHROMA_ORDER_CB_CR, + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64, + MIN_DIMENSION, MAX_DIMENSION, 8 }, + }, + { + .fourcc = V4L2_PIX_FMT_NV21, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, + .chroma_order = CHROMA_ORDER_CR_CB, + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64, + MIN_DIMENSION, MAX_DIMENSION, 8 }, + }, + { + .fourcc = V4L2_PIX_FMT_NV21M, + .num_planes = 2, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, + .chroma_order = CHROMA_ORDER_CR_CB, + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64, + MIN_DIMENSION, MAX_DIMENSION, 8 }, + }, + { + .fourcc = V4L2_PIX_FMT_NV16, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, + .chroma_order = CHROMA_ORDER_CB_CR, + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64, + MIN_DIMENSION, MAX_DIMENSION, 8 }, + }, + { + .fourcc = V4L2_PIX_FMT_NV16M, + .num_planes = 2, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, + .chroma_order = CHROMA_ORDER_CB_CR, + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64, + MIN_DIMENSION, MAX_DIMENSION, 8 }, + + }, + { + .fourcc = V4L2_PIX_FMT_NV61, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, + .chroma_order = CHROMA_ORDER_CR_CB, + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64, + MIN_DIMENSION, MAX_DIMENSION, 8 }, + }, + { + .fourcc = V4L2_PIX_FMT_NV61M, + .num_planes = 2, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, + .chroma_order = CHROMA_ORDER_CR_CB, + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64, + MIN_DIMENSION, MAX_DIMENSION, 8 }, + }, + { + .fourcc = V4L2_PIX_FMT_JPEG, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + .subsampling = 0, + .chroma_order = 0, + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 16, + MIN_DIMENSION, MAX_DIMENSION, 8 }, + }, +}; + +static unsigned int debug; +module_param(debug, uint, 0644); +MODULE_PARM_DESC(debug, "debug level"); + +#define dprintk(dev, lvl, fmt, arg...) \ + v4l2_dbg(lvl, debug, &(dev)->v4l2_dev, "%s: " fmt, __func__, ## arg) + +static const struct v4l2_event e5010_eos_event = { + .type = V4L2_EVENT_EOS +}; + +static const char *type_name(enum v4l2_buf_type type) +{ + switch (type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + return "Output"; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + return "Capture"; + default: + return "Invalid"; + } +} + +static struct e5010_q_data *get_queue(struct e5010_context *ctx, enum v4l2_buf_type type) +{ + return (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ? &ctx->out_queue : &ctx->cap_queue; +} + +static void calculate_qp_tables(struct e5010_context *ctx) +{ + long long luminosity, contrast; + int quality, i; + + quality = 50 - ctx->quality; + + luminosity = LUMINOSITY * quality / 50; + contrast = CONTRAST * quality / 50; + + if (quality > 0) { + luminosity *= INCREASE; + contrast *= INCREASE; + } + + for (i = 0; i < V4L2_JPEG_PIXELS_IN_BLOCK; i++) { + long long delta = v4l2_jpeg_ref_table_chroma_qt[i] * contrast + luminosity; + int val = (int)(v4l2_jpeg_ref_table_chroma_qt[i] + delta); + + clamp(val, 1, 255); + ctx->chroma_qp[i] = quality == -50 ? 1 : val; + + delta = v4l2_jpeg_ref_table_luma_qt[i] * contrast + luminosity; + val = (int)(v4l2_jpeg_ref_table_luma_qt[i] + delta); + clamp(val, 1, 255); + ctx->luma_qp[i] = quality == -50 ? 1 : val; + } + + ctx->update_qp = true; +} + +static int update_qp_tables(struct e5010_context *ctx) +{ + struct e5010_dev *e5010 = ctx->e5010; + int i, ret = 0; + u32 lvalue, cvalue; + + lvalue = 0; + cvalue = 0; + + for (i = 0; i < QP_TABLE_SIZE; i++) { + lvalue |= ctx->luma_qp[i] << (8 * (i % 4)); + cvalue |= ctx->chroma_qp[i] << (8 * (i % 4)); + if (i % 4 == 3) { + ret |= e5010_hw_set_qpvalue(e5010->core_base, + JASPER_LUMA_QUANTIZATION_TABLE0_OFFSET + + QP_TABLE_FIELD_OFFSET * ((i - 3) / 4), + lvalue); + ret |= e5010_hw_set_qpvalue(e5010->core_base, + JASPER_CHROMA_QUANTIZATION_TABLE0_OFFSET + + QP_TABLE_FIELD_OFFSET * ((i - 3) / 4), + cvalue); + lvalue = 0; + cvalue = 0; + } + } + + return ret; +} + +static int e5010_set_input_subsampling(void __iomem *core_base, int subsampling) +{ + switch (subsampling) { + case V4L2_JPEG_CHROMA_SUBSAMPLING_420: + return e5010_hw_set_input_subsampling(core_base, SUBSAMPLING_420); + case V4L2_JPEG_CHROMA_SUBSAMPLING_422: + return e5010_hw_set_input_subsampling(core_base, SUBSAMPLING_422); + default: + return -EINVAL; + }; +} + +static int e5010_querycap(struct file *file, void *priv, struct v4l2_capability *cap) +{ + strscpy(cap->driver, E5010_MODULE_NAME, sizeof(cap->driver)); + strscpy(cap->card, E5010_MODULE_NAME, sizeof(cap->card)); + + return 0; +} + +static struct e5010_fmt *find_format(struct v4l2_format *f) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(e5010_formats); ++i) { + if (e5010_formats[i].fourcc == f->fmt.pix_mp.pixelformat && + e5010_formats[i].type == f->type) + return &e5010_formats[i]; + } + + return NULL; +} + +static int e5010_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f) +{ + int i, index = 0; + struct e5010_fmt *fmt = NULL; + struct e5010_context *ctx = file->private_data; + + if (!V4L2_TYPE_IS_MULTIPLANAR(f->type)) { + v4l2_err(&ctx->e5010->v4l2_dev, "ENUMFMT with Invalid type: %d\n", f->type); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(e5010_formats); ++i) { + if (e5010_formats[i].type == f->type) { + if (index == f->index) { + fmt = &e5010_formats[i]; + break; + } + index++; + } + } + + if (!fmt) + return -EINVAL; + + f->pixelformat = fmt->fourcc; + return 0; +} + +static int e5010_g_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct e5010_context *ctx = file->private_data; + struct e5010_q_data *queue; + int i; + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + struct v4l2_plane_pix_format *plane_fmt = pix_mp->plane_fmt; + + queue = get_queue(ctx, f->type); + + pix_mp->flags = 0; + pix_mp->field = V4L2_FIELD_NONE; + pix_mp->pixelformat = queue->fmt->fourcc; + pix_mp->width = queue->width_adjusted; + pix_mp->height = queue->height_adjusted; + pix_mp->num_planes = queue->fmt->num_planes; + + if (V4L2_TYPE_IS_OUTPUT(f->type)) { + if (!pix_mp->colorspace) + pix_mp->colorspace = V4L2_COLORSPACE_SRGB; + + for (i = 0; i < queue->fmt->num_planes; i++) { + plane_fmt[i].sizeimage = queue->sizeimage[i]; + plane_fmt[i].bytesperline = queue->bytesperline[i]; + } + + } else { + pix_mp->colorspace = V4L2_COLORSPACE_JPEG; + plane_fmt[0].bytesperline = 0; + plane_fmt[0].sizeimage = queue->sizeimage[0]; + } + pix_mp->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + pix_mp->xfer_func = V4L2_XFER_FUNC_DEFAULT; + pix_mp->quantization = V4L2_QUANTIZATION_DEFAULT; + + return 0; +} + +static int e5010_jpeg_try_fmt(struct v4l2_format *f, struct e5010_context *ctx) +{ + struct e5010_fmt *fmt; + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + struct v4l2_plane_pix_format *plane_fmt = pix_mp->plane_fmt; + + fmt = find_format(f); + if (!fmt) { + if (V4L2_TYPE_IS_OUTPUT(f->type)) + pix_mp->pixelformat = V4L2_PIX_FMT_NV12; + else + pix_mp->pixelformat = V4L2_PIX_FMT_JPEG; + fmt = find_format(f); + if (!fmt) + return -EINVAL; + } + + if (V4L2_TYPE_IS_OUTPUT(f->type)) { + if (!pix_mp->colorspace) + pix_mp->colorspace = V4L2_COLORSPACE_JPEG; + if (!pix_mp->ycbcr_enc) + pix_mp->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + if (!pix_mp->quantization) + pix_mp->quantization = V4L2_QUANTIZATION_DEFAULT; + if (!pix_mp->xfer_func) + pix_mp->xfer_func = V4L2_XFER_FUNC_DEFAULT; + + v4l2_apply_frmsize_constraints(&pix_mp->width, + &pix_mp->height, + &fmt->frmsize); + + v4l2_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat, + pix_mp->width, pix_mp->height); + + } else { + pix_mp->colorspace = V4L2_COLORSPACE_JPEG; + pix_mp->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + pix_mp->quantization = V4L2_QUANTIZATION_DEFAULT; + pix_mp->xfer_func = V4L2_XFER_FUNC_DEFAULT; + v4l2_apply_frmsize_constraints(&pix_mp->width, + &pix_mp->height, + &fmt->frmsize); + plane_fmt[0].sizeimage = pix_mp->width * pix_mp->height * JPEG_MAX_BYTES_PER_PIXEL; + plane_fmt[0].sizeimage += HEADER_SIZE; + plane_fmt[0].bytesperline = 0; + pix_mp->pixelformat = fmt->fourcc; + pix_mp->num_planes = fmt->num_planes; + } + pix_mp->flags = 0; + pix_mp->field = V4L2_FIELD_NONE; + + dprintk(ctx->e5010, 2, + "ctx: 0x%p: format type %s:, wxh: %dx%d (plane0 : %d bytes, plane1 : %d bytes),fmt: %c%c%c%c\n", + ctx, type_name(f->type), pix_mp->width, pix_mp->height, + plane_fmt[0].sizeimage, plane_fmt[1].sizeimage, + (fmt->fourcc & 0xff), + (fmt->fourcc >> 8) & 0xff, + (fmt->fourcc >> 16) & 0xff, + (fmt->fourcc >> 24) & 0xff); + + return 0; +} + +static int e5010_try_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct e5010_context *ctx = file->private_data; + + return e5010_jpeg_try_fmt(f, ctx); +} + +static int e5010_s_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct e5010_context *ctx = file->private_data; + struct vb2_queue *vq; + int ret = 0, i = 0; + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + struct v4l2_plane_pix_format *plane_fmt = pix_mp->plane_fmt; + struct e5010_q_data *queue; + struct e5010_fmt *fmt; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + if (vb2_is_busy(vq)) { + v4l2_err(&ctx->e5010->v4l2_dev, "queue busy\n"); + return -EBUSY; + } + + ret = e5010_jpeg_try_fmt(f, ctx); + if (ret) + return ret; + + fmt = find_format(f); + queue = get_queue(ctx, f->type); + + queue->fmt = fmt; + queue->width = pix_mp->width; + queue->height = pix_mp->height; + + if (V4L2_TYPE_IS_OUTPUT(f->type)) { + for (i = 0; i < fmt->num_planes; i++) { + queue->bytesperline[i] = plane_fmt[i].bytesperline; + queue->sizeimage[i] = plane_fmt[i].sizeimage; + } + queue->crop.left = 0; + queue->crop.top = 0; + queue->crop.width = queue->width; + queue->crop.height = queue->height; + } else { + queue->sizeimage[0] = plane_fmt[0].sizeimage; + queue->sizeimage[1] = 0; + queue->bytesperline[0] = 0; + queue->bytesperline[1] = 0; + } + + return 0; +} + +static int e5010_enum_framesizes(struct file *file, void *priv, struct v4l2_frmsizeenum *fsize) +{ + struct v4l2_format f; + struct e5010_fmt *fmt; + + if (fsize->index != 0) + return -EINVAL; + + f.fmt.pix_mp.pixelformat = fsize->pixel_format; + if (f.fmt.pix_mp.pixelformat == V4L2_PIX_FMT_JPEG) + f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + else + f.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + + fmt = find_format(&f); + if (!fmt) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise = fmt->frmsize; + fsize->reserved[0] = 0; + fsize->reserved[1] = 0; + + return 0; +} + +static int e5010_g_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct e5010_context *ctx = file->private_data; + struct e5010_q_data *queue; + + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + queue = get_queue(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + + switch (s->target) { + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + s->r.left = 0; + s->r.top = 0; + s->r.width = queue->width; + s->r.height = queue->height; + break; + case V4L2_SEL_TGT_CROP: + memcpy(&s->r, &queue->crop, sizeof(s->r)); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int e5010_s_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct e5010_context *ctx = file->private_data; + struct e5010_q_data *queue; + struct vb2_queue *vq; + struct v4l2_rect base_rect; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, s->type); + if (!vq) + return -EINVAL; + + if (vb2_is_streaming(vq)) + return -EBUSY; + + if (s->target != V4L2_SEL_TGT_CROP || + s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + queue = get_queue(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + base_rect.top = 0; + base_rect.left = 0; + base_rect.width = queue->width; + base_rect.height = queue->height; + + switch (s->flags) { + case 0: + s->r.width = round_down(s->r.width, queue->fmt->frmsize.step_width); + s->r.height = round_down(s->r.height, queue->fmt->frmsize.step_height); + s->r.left = round_down(s->r.left, queue->fmt->frmsize.step_width); + s->r.top = round_down(s->r.top, 2); + + if (s->r.left + s->r.width > queue->width) + s->r.width = round_down(s->r.width + s->r.left - queue->width, + queue->fmt->frmsize.step_width); + if (s->r.top + s->r.height > queue->height) + s->r.top = round_down(s->r.top + s->r.height - queue->height, 2); + break; + case V4L2_SEL_FLAG_GE: + s->r.width = round_up(s->r.width, queue->fmt->frmsize.step_width); + s->r.height = round_up(s->r.height, queue->fmt->frmsize.step_height); + s->r.left = round_up(s->r.left, queue->fmt->frmsize.step_width); + s->r.top = round_up(s->r.top, 2); + break; + case V4L2_SEL_FLAG_LE: + s->r.width = round_down(s->r.width, queue->fmt->frmsize.step_width); + s->r.height = round_down(s->r.height, queue->fmt->frmsize.step_height); + s->r.left = round_down(s->r.left, queue->fmt->frmsize.step_width); + s->r.top = round_down(s->r.top, 2); + break; + case V4L2_SEL_FLAG_LE | V4L2_SEL_FLAG_GE: + if (!IS_ALIGNED(s->r.width, queue->fmt->frmsize.step_width) || + !IS_ALIGNED(s->r.height, queue->fmt->frmsize.step_height) || + !IS_ALIGNED(s->r.left, queue->fmt->frmsize.step_width) || + !IS_ALIGNED(s->r.top, 2)) + return -ERANGE; + break; + default: + return -EINVAL; + } + + if (!v4l2_rect_enclosed(&s->r, &base_rect)) + return -ERANGE; + + memcpy(&queue->crop, &s->r, sizeof(s->r)); + + if (!v4l2_rect_equal(&s->r, &base_rect)) + queue->crop_set = true; + + dprintk(ctx->e5010, 2, "ctx: 0x%p: crop rectangle: w: %d, h : %d, l : %d, t : %d\n", + ctx, queue->crop.width, queue->crop.height, queue->crop.left, queue->crop.top); + + return 0; +} + +static int e5010_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_EOS: + return v4l2_event_subscribe(fh, sub, 0, NULL); + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subscribe_event(fh, sub); + default: + return -EINVAL; + } + + return 0; +} + +static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) +{ + struct e5010_context *ctx = priv; + struct e5010_dev *e5010 = ctx->e5010; + int ret = 0; + + /* src_vq */ + memset(src_vq, 0, sizeof(*src_vq)); + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; + src_vq->drv_priv = ctx; + src_vq->buf_struct_size = sizeof(struct e5010_buffer); + src_vq->ops = &e5010_video_ops; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &e5010->mutex; + src_vq->dev = e5010->v4l2_dev.dev; + + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + /* dst_vq */ + memset(dst_vq, 0, sizeof(*dst_vq)); + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; + dst_vq->drv_priv = ctx; + dst_vq->buf_struct_size = sizeof(struct e5010_buffer); + dst_vq->ops = &e5010_video_ops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &e5010->mutex; + dst_vq->dev = e5010->v4l2_dev.dev; + + ret = vb2_queue_init(dst_vq); + if (ret) { + vb2_queue_release(src_vq); + return ret; + } + + return 0; +} + +static int e5010_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct e5010_context *ctx = + container_of(ctrl->handler, struct e5010_context, ctrl_handler); + + switch (ctrl->id) { + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + ctx->quality = ctrl->val; + calculate_qp_tables(ctx); + dprintk(ctx->e5010, 2, "ctx: 0x%p compression quality set to : %d\n", ctx, + ctx->quality); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_ctrl_ops e5010_ctrl_ops = { + .s_ctrl = e5010_s_ctrl, +}; + +static void e5010_encode_ctrls(struct e5010_context *ctx) +{ + v4l2_ctrl_new_std(&ctx->ctrl_handler, &e5010_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, 1, 100, 1, 75); +} + +static int e5010_ctrls_setup(struct e5010_context *ctx) +{ + int err; + + v4l2_ctrl_handler_init(&ctx->ctrl_handler, 1); + + e5010_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 void e5010_jpeg_set_default_params(struct e5010_context *ctx) +{ + struct e5010_q_data *queue; + struct v4l2_format f; + struct e5010_fmt *fmt; + struct v4l2_pix_format_mplane *pix_mp = &f.fmt.pix_mp; + struct v4l2_plane_pix_format *plane_fmt = pix_mp->plane_fmt; + int i = 0; + + f.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12; + fmt = find_format(&f); + queue = &ctx->out_queue; + queue->fmt = fmt; + queue->width = DEFAULT_WIDTH; + queue->height = DEFAULT_HEIGHT; + pix_mp->width = queue->width; + pix_mp->height = queue->height; + queue->crop.left = 0; + queue->crop.top = 0; + queue->crop.width = queue->width; + queue->crop.height = queue->height; + v4l2_apply_frmsize_constraints(&pix_mp->width, + &pix_mp->height, + &fmt->frmsize); + v4l2_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat, + pix_mp->width, pix_mp->height); + for (i = 0; i < fmt->num_planes; i++) { + queue->bytesperline[i] = plane_fmt[i].bytesperline; + queue->sizeimage[i] = plane_fmt[i].sizeimage; + } + queue->width_adjusted = pix_mp->width; + queue->height_adjusted = pix_mp->height; + + f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_JPEG; + fmt = find_format(&f); + queue = &ctx->cap_queue; + queue->fmt = fmt; + queue->width = DEFAULT_WIDTH; + queue->height = DEFAULT_HEIGHT; + pix_mp->width = queue->width; + pix_mp->height = queue->height; + v4l2_apply_frmsize_constraints(&pix_mp->width, + &pix_mp->height, + &fmt->frmsize); + queue->sizeimage[0] = pix_mp->width * pix_mp->height * JPEG_MAX_BYTES_PER_PIXEL; + queue->sizeimage[0] += HEADER_SIZE; + queue->sizeimage[1] = 0; + queue->bytesperline[0] = 0; + queue->bytesperline[1] = 0; + queue->width_adjusted = pix_mp->width; + queue->height_adjusted = pix_mp->height; +} + +static int e5010_open(struct file *file) +{ + struct e5010_dev *e5010 = video_drvdata(file); + struct video_device *vdev = video_devdata(file); + struct e5010_context *ctx; + int ret = 0; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + if (mutex_lock_interruptible(&e5010->mutex)) { + ret = -ERESTARTSYS; + goto free; + } + + v4l2_fh_init(&ctx->fh, vdev); + file->private_data = ctx; + v4l2_fh_add(&ctx->fh); + + ctx->e5010 = e5010; + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(e5010->m2m_dev, ctx, queue_init); + if (IS_ERR(ctx->fh.m2m_ctx)) { + v4l2_err(&e5010->v4l2_dev, "failed to init m2m ctx\n"); + ret = PTR_ERR(ctx->fh.m2m_ctx); + goto exit; + } + + ret = e5010_ctrls_setup(ctx); + if (ret) { + v4l2_err(&e5010->v4l2_dev, "failed to setup e5010 jpeg controls\n"); + goto err_ctrls_setup; + } + ctx->fh.ctrl_handler = &ctx->ctrl_handler; + + e5010_jpeg_set_default_params(ctx); + + dprintk(e5010, 1, "Created instance: 0x%p, m2m_ctx: 0x%p\n", ctx, ctx->fh.m2m_ctx); + + mutex_unlock(&e5010->mutex); + return 0; + +err_ctrls_setup: + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); +exit: + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + mutex_unlock(&e5010->mutex); +free: + kfree(ctx); + return ret; +} + +static int e5010_release(struct file *file) +{ + struct e5010_dev *e5010 = video_drvdata(file); + struct e5010_context *ctx = file->private_data; + + dprintk(e5010, 1, "Releasing instance: 0x%p, m2m_ctx: 0x%p\n", ctx, ctx->fh.m2m_ctx); + mutex_lock(&e5010->mutex); + 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); + kfree(ctx); + mutex_unlock(&e5010->mutex); + + return 0; +} + +static void header_write(struct e5010_context *ctx, u8 *addr, unsigned int *offset, + unsigned int no_bytes, unsigned long bits) +{ + u8 *w_addr = addr + *offset; + int i; + + if ((*offset + no_bytes) > HEADER_SIZE) { + v4l2_warn(&ctx->e5010->v4l2_dev, "%s: %s: %d: Problem writing header. %d > HEADER_SIZE %d\n", + __FILE__, __func__, __LINE__, *offset + no_bytes, HEADER_SIZE); + return; + } + + for (i = no_bytes - 1; i >= 0; i--) + *(w_addr++) = ((u8 *)&bits)[i]; + + *offset += no_bytes; +} + +static void encode_marker_segment(struct e5010_context *ctx, void *addr, unsigned int *offset) +{ + u8 *buffer = (u8 *)addr; + int i; + + header_write(ctx, buffer, offset, 2, START_OF_IMAGE); + header_write(ctx, buffer, offset, 2, DQT_MARKER); + header_write(ctx, buffer, offset, 3, LQPQ << 4); + for (i = 0; i < V4L2_JPEG_PIXELS_IN_BLOCK; i++) + header_write(ctx, buffer, offset, 1, ctx->luma_qp[v4l2_jpeg_zigzag_scan_index[i]]); + + header_write(ctx, buffer, offset, 2, DQT_MARKER); + header_write(ctx, buffer, offset, 3, (LQPQ << 4) | 1); + for (i = 0; i < V4L2_JPEG_PIXELS_IN_BLOCK; i++) + header_write(ctx, buffer, offset, 1, + ctx->chroma_qp[v4l2_jpeg_zigzag_scan_index[i]]); + + /* Huffman tables */ + header_write(ctx, buffer, offset, 2, DHT_MARKER); + header_write(ctx, buffer, offset, 2, LH_DC); + header_write(ctx, buffer, offset, 1, V4L2_JPEG_LUM_HT | V4L2_JPEG_DC_HT); + for (i = 0 ; i < V4L2_JPEG_REF_HT_DC_LEN; i++) + header_write(ctx, buffer, offset, 1, v4l2_jpeg_ref_table_luma_dc_ht[i]); + + header_write(ctx, buffer, offset, 2, DHT_MARKER); + header_write(ctx, buffer, offset, 2, LH_AC); + header_write(ctx, buffer, offset, 1, V4L2_JPEG_LUM_HT | V4L2_JPEG_AC_HT); + for (i = 0 ; i < V4L2_JPEG_REF_HT_AC_LEN; i++) + header_write(ctx, buffer, offset, 1, v4l2_jpeg_ref_table_luma_ac_ht[i]); + + header_write(ctx, buffer, offset, 2, DHT_MARKER); + header_write(ctx, buffer, offset, 2, LH_DC); + header_write(ctx, buffer, offset, 1, V4L2_JPEG_CHR_HT | V4L2_JPEG_DC_HT); + for (i = 0 ; i < V4L2_JPEG_REF_HT_DC_LEN; i++) + header_write(ctx, buffer, offset, 1, v4l2_jpeg_ref_table_chroma_dc_ht[i]); + + header_write(ctx, buffer, offset, 2, DHT_MARKER); + header_write(ctx, buffer, offset, 2, LH_AC); + header_write(ctx, buffer, offset, 1, V4L2_JPEG_CHR_HT | V4L2_JPEG_AC_HT); + for (i = 0 ; i < V4L2_JPEG_REF_HT_AC_LEN; i++) + header_write(ctx, buffer, offset, 1, v4l2_jpeg_ref_table_chroma_ac_ht[i]); +} + +static void encode_frame_header(struct e5010_context *ctx, void *addr, unsigned int *offset) +{ + u8 *buffer = (u8 *)addr; + + header_write(ctx, buffer, offset, 2, SOF_BASELINE_DCT); + header_write(ctx, buffer, offset, 2, 8 + (3 * UC_NUM_COMP)); + header_write(ctx, buffer, offset, 1, PRECISION); + header_write(ctx, buffer, offset, 2, ctx->out_queue.crop.height); + header_write(ctx, buffer, offset, 2, ctx->out_queue.crop.width); + header_write(ctx, buffer, offset, 1, UC_NUM_COMP); + + /* Luma details */ + header_write(ctx, buffer, offset, 1, 1); + if (ctx->out_queue.fmt->subsampling == V4L2_JPEG_CHROMA_SUBSAMPLING_422) + header_write(ctx, buffer, offset, 1, + HORZ_SAMPLING_FACTOR | (VERT_SAMPLING_FACTOR_422)); + else + header_write(ctx, buffer, offset, 1, + HORZ_SAMPLING_FACTOR | (VERT_SAMPLING_FACTOR_420)); + header_write(ctx, buffer, offset, 1, 0); + /* Chroma details */ + header_write(ctx, buffer, offset, 1, 2); + header_write(ctx, buffer, offset, 1, (HORZ_SAMPLING_FACTOR >> 1) | 1); + header_write(ctx, buffer, offset, 1, 1); + header_write(ctx, buffer, offset, 1, 3); + header_write(ctx, buffer, offset, 1, (HORZ_SAMPLING_FACTOR >> 1) | 1); + header_write(ctx, buffer, offset, 1, 1); +} + +static void jpg_encode_sos_header(struct e5010_context *ctx, void *addr, unsigned int *offset) +{ + u8 *buffer = (u8 *)addr; + int i; + + header_write(ctx, buffer, offset, 2, START_OF_SCAN); + header_write(ctx, buffer, offset, 2, 6 + (COMPONENTS_IN_SCAN << 1)); + header_write(ctx, buffer, offset, 1, COMPONENTS_IN_SCAN); + + for (i = 0; i < COMPONENTS_IN_SCAN; i++) { + header_write(ctx, buffer, offset, 1, i + 1); + if (i == 0) + header_write(ctx, buffer, offset, 1, 0); + else + header_write(ctx, buffer, offset, 1, 17); + } + + header_write(ctx, buffer, offset, 1, 0); + header_write(ctx, buffer, offset, 1, 63); + header_write(ctx, buffer, offset, 1, 0); +} + +static void write_header(struct e5010_context *ctx, void *addr) +{ + unsigned int offset = 0; + + encode_marker_segment(ctx, addr, &offset); + encode_frame_header(ctx, addr, &offset); + jpg_encode_sos_header(ctx, addr, &offset); +} + +static irqreturn_t e5010_irq(int irq, void *data) +{ + struct e5010_dev *e5010 = data; + struct e5010_context *ctx; + int output_size; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + bool pic_done, out_addr_err; + + spin_lock(&e5010->hw_lock); + pic_done = e5010_hw_pic_done_irq(e5010->core_base); + out_addr_err = e5010_hw_output_address_irq(e5010->core_base); + + if (!pic_done && !out_addr_err) { + spin_unlock(&e5010->hw_lock); + return IRQ_NONE; + } + + ctx = v4l2_m2m_get_curr_priv(e5010->m2m_dev); + if (WARN_ON(!ctx)) + goto job_unlock; + + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + if (!dst_buf || !src_buf) { + v4l2_err(&e5010->v4l2_dev, "ctx: 0x%p No source or destination buffer\n", ctx); + goto job_unlock; + } + + if (out_addr_err) { + e5010_hw_clear_output_error(e5010->core_base, 1); + v4l2_warn(&e5010->v4l2_dev, + "ctx: 0x%p Output bitstream size exceeded max size\n", ctx); + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, dst_buf->planes[0].length); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); + 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); + v4l2_event_queue_fh(&ctx->fh, &e5010_eos_event); + dprintk(e5010, 2, "ctx: 0x%p Sending EOS\n", ctx); + } + } + + if (pic_done) { + e5010_hw_clear_picture_done(e5010->core_base, 1); + dprintk(e5010, 3, "ctx: 0x%p Got output bitstream of size %d bytes\n", + ctx, readl(e5010->core_base + JASPER_OUTPUT_SIZE_OFFSET)); + + 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); + v4l2_event_queue_fh(&ctx->fh, &e5010_eos_event); + dprintk(e5010, 2, "ctx: 0x%p Sending EOS\n", ctx); + } + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + output_size = e5010_hw_get_output_size(e5010->core_base); + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, output_size + HEADER_SIZE); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); + dprintk(e5010, 3, + "ctx: 0x%p frame done for dst_buf->sequence: %d src_buf->sequence: %d\n", + ctx, dst_buf->sequence, src_buf->sequence); + } + + v4l2_m2m_job_finish(e5010->m2m_dev, ctx->fh.m2m_ctx); + dprintk(e5010, 3, "ctx: 0x%p Finish job\n", ctx); + +job_unlock: + spin_unlock(&e5010->hw_lock); + return IRQ_HANDLED; +} + +static int e5010_init_device(struct e5010_dev *e5010) +{ + int ret = 0; + + /*TODO: Set MMU in bypass mode until support for the same is added in driver*/ + e5010_hw_bypass_mmu(e5010->mmu_base, 1); + + if (e5010_hw_enable_auto_clock_gating(e5010->core_base, 1)) + v4l2_warn(&e5010->v4l2_dev, "failed to enable auto clock gating\n"); + + if (e5010_hw_enable_manual_clock_gating(e5010->core_base, 0)) + v4l2_warn(&e5010->v4l2_dev, "failed to disable manual clock gating\n"); + + if (e5010_hw_enable_crc_check(e5010->core_base, 0)) + v4l2_warn(&e5010->v4l2_dev, "failed to disable CRC check\n"); + + if (e5010_hw_enable_output_address_error_irq(e5010->core_base, 1)) + v4l2_err(&e5010->v4l2_dev, "failed to enable Output Address Error interrupts\n"); + + ret = e5010_hw_set_input_source_to_memory(e5010->core_base, 1); + if (ret) { + v4l2_err(&e5010->v4l2_dev, "failed to set input source to memory\n"); + return ret; + } + + ret = e5010_hw_enable_picture_done_irq(e5010->core_base, 1); + if (ret) + v4l2_err(&e5010->v4l2_dev, "failed to enable Picture Done interrupts\n"); + + return ret; +} + +static int e5010_probe(struct platform_device *pdev) +{ + struct e5010_dev *e5010; + int irq, ret = 0; + struct device *dev = &pdev->dev; + + ret = dma_set_mask(dev, DMA_BIT_MASK(32)); + if (ret) + return dev_err_probe(dev, ret, "32-bit consistent DMA enable failed\n"); + + e5010 = devm_kzalloc(dev, sizeof(*e5010), GFP_KERNEL); + if (!e5010) + return -ENOMEM; + + platform_set_drvdata(pdev, e5010); + + e5010->dev = dev; + + mutex_init(&e5010->mutex); + spin_lock_init(&e5010->hw_lock); + + e5010->vdev = video_device_alloc(); + if (!e5010->vdev) { + dev_err(dev, "failed to allocate video device\n"); + return -ENOMEM; + } + + snprintf(e5010->vdev->name, sizeof(e5010->vdev->name), "%s", E5010_MODULE_NAME); + e5010->vdev->fops = &e5010_fops; + e5010->vdev->ioctl_ops = &e5010_ioctl_ops; + e5010->vdev->minor = -1; + e5010->vdev->release = video_device_release; + e5010->vdev->vfl_dir = VFL_DIR_M2M; + e5010->vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; + e5010->vdev->v4l2_dev = &e5010->v4l2_dev; + e5010->vdev->lock = &e5010->mutex; + + ret = v4l2_device_register(dev, &e5010->v4l2_dev); + if (ret) + return dev_err_probe(dev, ret, "failed to register v4l2 device\n"); + + e5010->m2m_dev = v4l2_m2m_init(&e5010_m2m_ops); + if (IS_ERR(e5010->m2m_dev)) { + ret = PTR_ERR(e5010->m2m_dev); + e5010->m2m_dev = NULL; + dev_err_probe(dev, ret, "failed to init mem2mem device\n"); + goto fail_after_v4l2_register; + } + + video_set_drvdata(e5010->vdev, e5010); + + e5010->core_base = devm_platform_ioremap_resource_byname(pdev, "core"); + if (IS_ERR(e5010->core_base)) { + ret = PTR_ERR(e5010->core_base); + dev_err_probe(dev, ret, "Missing 'core' resources area\n"); + goto fail_after_v4l2_register; + } + + e5010->mmu_base = devm_platform_ioremap_resource_byname(pdev, "mmu"); + if (IS_ERR(e5010->mmu_base)) { + ret = PTR_ERR(e5010->mmu_base); + dev_err_probe(dev, ret, "Missing 'mmu' resources area\n"); + goto fail_after_v4l2_register; + } + + e5010->last_context_run = NULL; + + irq = platform_get_irq(pdev, 0); + ret = devm_request_irq(dev, irq, e5010_irq, 0, + E5010_MODULE_NAME, e5010); + if (ret) { + dev_err_probe(dev, ret, "failed to register IRQ %d\n", irq); + goto fail_after_v4l2_register; + } + + e5010->clk = devm_clk_get(dev, NULL); + if (IS_ERR(e5010->clk)) { + ret = PTR_ERR(e5010->clk); + dev_err_probe(dev, ret, "failed to get clock\n"); + goto fail_after_v4l2_register; + } + + pm_runtime_enable(dev); + + ret = video_register_device(e5010->vdev, VFL_TYPE_VIDEO, 0); + if (ret) { + dev_err_probe(dev, ret, "failed to register video device\n"); + goto fail_after_video_register_device; + } + + v4l2_info(&e5010->v4l2_dev, "Device registered as /dev/video%d\n", + e5010->vdev->num); + + return 0; + +fail_after_video_register_device: + v4l2_m2m_release(e5010->m2m_dev); +fail_after_v4l2_register: + v4l2_device_unregister(&e5010->v4l2_dev); + return ret; +} + +static void e5010_remove(struct platform_device *pdev) +{ + struct e5010_dev *e5010 = platform_get_drvdata(pdev); + + pm_runtime_disable(e5010->dev); + video_unregister_device(e5010->vdev); + v4l2_m2m_release(e5010->m2m_dev); + v4l2_device_unregister(&e5010->v4l2_dev); +} + +static void e5010_vb2_buffers_return(struct vb2_queue *q, enum vb2_buffer_state state) +{ + struct vb2_v4l2_buffer *vbuf; + struct e5010_context *ctx = vb2_get_drv_priv(q); + + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + while ((vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx))) { + dprintk(ctx->e5010, 2, "ctx: 0x%p, buf type %s | index %d\n", + ctx, type_name(vbuf->vb2_buf.type), vbuf->vb2_buf.index); + v4l2_m2m_buf_done(vbuf, state); + } + } else { + while ((vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx))) { + dprintk(ctx->e5010, 2, "ctx: 0x%p, buf type %s | index %d\n", + ctx, type_name(vbuf->vb2_buf.type), vbuf->vb2_buf.index); + vb2_set_plane_payload(&vbuf->vb2_buf, 0, 0); + v4l2_m2m_buf_done(vbuf, state); + } + } +} + +static int e5010_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct e5010_context *ctx = vb2_get_drv_priv(vq); + struct e5010_q_data *queue; + int i; + + queue = get_queue(ctx, vq->type); + + if (*nplanes) { + if (*nplanes != queue->fmt->num_planes) + return -EINVAL; + for (i = 0; i < *nplanes; i++) { + if (sizes[i] < queue->sizeimage[i]) + return -EINVAL; + } + return 0; + } + + *nplanes = queue->fmt->num_planes; + for (i = 0; i < *nplanes; i++) + sizes[i] = queue->sizeimage[i]; + + dprintk(ctx->e5010, 2, + "ctx: 0x%p, type %s, buffer(s): %d, planes %d, plane1: bytes %d plane2: %d bytes\n", + ctx, type_name(vq->type), *nbuffers, *nplanes, sizes[0], sizes[1]); + + return 0; +} + +static void e5010_buf_finish(struct vb2_buffer *vb) +{ + struct e5010_context *ctx = vb2_get_drv_priv(vb->vb2_queue); + void *d_addr; + + if (vb->state != VB2_BUF_STATE_DONE || V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) + return; + + d_addr = vb2_plane_vaddr(vb, 0); + write_header(ctx, d_addr); +} + +static int e5010_buf_out_validate(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct e5010_context *ctx = vb2_get_drv_priv(vb->vb2_queue); + + if (vbuf->field != V4L2_FIELD_NONE) + dprintk(ctx->e5010, 1, "ctx: 0x%p, field isn't supported\n", ctx); + + vbuf->field = V4L2_FIELD_NONE; + + return 0; +} + +static int e5010_buf_prepare(struct vb2_buffer *vb) +{ + struct e5010_context *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct e5010_q_data *queue; + int i; + + vbuf->field = V4L2_FIELD_NONE; + + queue = get_queue(ctx, vb->vb2_queue->type); + + for (i = 0; i < queue->fmt->num_planes; i++) { + if (vb2_plane_size(vb, i) < (unsigned long)queue->sizeimage[i]) { + v4l2_err(&ctx->e5010->v4l2_dev, "plane %d too small (%lu < %lu)", i, + vb2_plane_size(vb, i), (unsigned long)queue->sizeimage[i]); + + return -EINVAL; + } + } + + 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 void e5010_buf_queue(struct vb2_buffer *vb) +{ + struct e5010_context *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + 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 e5010_q_data *queue = get_queue(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + + vbuf->sequence = queue->sequence++; + v4l2_m2m_last_buffer_done(ctx->fh.m2m_ctx, vbuf); + v4l2_event_queue_fh(&ctx->fh, &e5010_eos_event); + return; + } + + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); +} + +static int e5010_encoder_cmd(struct file *file, void *priv, + struct v4l2_encoder_cmd *cmd) +{ + struct e5010_context *ctx = file->private_data; + int ret; + struct vb2_queue *cap_vq; + + cap_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + + ret = v4l2_m2m_ioctl_try_encoder_cmd(file, &ctx->fh, cmd); + if (ret < 0) + return ret; + + if (!vb2_is_streaming(v4l2_m2m_get_src_vq(ctx->fh.m2m_ctx)) || + !vb2_is_streaming(v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx))) + return 0; + + ret = v4l2_m2m_ioctl_encoder_cmd(file, &ctx->fh, cmd); + if (ret < 0) + return ret; + + if (cmd->cmd == V4L2_ENC_CMD_STOP && + v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) + v4l2_event_queue_fh(&ctx->fh, &e5010_eos_event); + + if (cmd->cmd == V4L2_ENC_CMD_START && + v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) + vb2_clear_last_buffer_dequeued(cap_vq); + + return 0; +} + +static int e5010_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct e5010_context *ctx = vb2_get_drv_priv(q); + int ret; + + struct e5010_q_data *queue = get_queue(ctx, q->type); + + v4l2_m2m_update_start_streaming_state(ctx->fh.m2m_ctx, q); + queue->sequence = 0; + + ret = pm_runtime_resume_and_get(ctx->e5010->dev); + if (ret < 0) { + v4l2_err(&ctx->e5010->v4l2_dev, "failed to power up jpeg\n"); + goto fail; + } + + ret = e5010_init_device(ctx->e5010); + if (ret) { + v4l2_err(&ctx->e5010->v4l2_dev, "failed to Enable e5010 device\n"); + goto fail; + } + + return 0; + +fail: + e5010_vb2_buffers_return(q, VB2_BUF_STATE_QUEUED); + + return ret; +} + +static void e5010_stop_streaming(struct vb2_queue *q) +{ + struct e5010_context *ctx = vb2_get_drv_priv(q); + + e5010_vb2_buffers_return(q, VB2_BUF_STATE_ERROR); + + if (V4L2_TYPE_IS_OUTPUT(q->type)) + 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)) { + v4l2_event_queue_fh(&ctx->fh, &e5010_eos_event); + } + + pm_runtime_put_sync(ctx->e5010->dev); +} + +static void e5010_device_run(void *priv) +{ + struct e5010_context *ctx = priv; + struct e5010_dev *e5010 = ctx->e5010; + struct vb2_v4l2_buffer *s_vb, *d_vb; + u32 reg = 0; + int ret = 0, luma_crop_offset = 0, chroma_crop_offset = 0; + unsigned long flags; + int num_planes = ctx->out_queue.fmt->num_planes; + + spin_lock_irqsave(&e5010->hw_lock, flags); + s_vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + WARN_ON(!s_vb); + d_vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + WARN_ON(!d_vb); + if (!s_vb || !d_vb) + goto no_ready_buf_err; + + s_vb->sequence = ctx->out_queue.sequence++; + d_vb->sequence = ctx->cap_queue.sequence++; + + v4l2_m2m_buf_copy_metadata(s_vb, d_vb, false); + + if (ctx != e5010->last_context_run || ctx->update_qp) { + dprintk(e5010, 1, "ctx updated: 0x%p -> 0x%p, updating qp tables\n", + e5010->last_context_run, ctx); + ret = update_qp_tables(ctx); + } + + if (ret) { + ctx->update_qp = true; + v4l2_err(&e5010->v4l2_dev, "failed to update QP tables\n"); + goto device_busy_err; + } else { + e5010->last_context_run = ctx; + ctx->update_qp = false; + } + + /* Set I/O Buffer addresses */ + reg = (u32)vb2_dma_contig_plane_dma_addr(&s_vb->vb2_buf, 0); + + if (ctx->out_queue.crop_set) { + luma_crop_offset = ctx->out_queue.bytesperline[0] * ctx->out_queue.crop.top + + ctx->out_queue.crop.left; + + if (ctx->out_queue.fmt->subsampling == V4L2_JPEG_CHROMA_SUBSAMPLING_422) { + chroma_crop_offset = + ctx->out_queue.bytesperline[0] * ctx->out_queue.crop.top + + ctx->out_queue.crop.left; + } else { + chroma_crop_offset = + ctx->out_queue.bytesperline[0] * ctx->out_queue.crop.top / 2 + + ctx->out_queue.crop.left; + } + + dprintk(e5010, 1, "Luma crop offset : %x, chroma crop offset : %x\n", + luma_crop_offset, chroma_crop_offset); + } + + ret = e5010_hw_set_input_luma_addr(e5010->core_base, reg + luma_crop_offset); + if (ret || !reg) { + v4l2_err(&e5010->v4l2_dev, "failed to set input luma address\n"); + goto device_busy_err; + } + + if (num_planes == 1) + reg += (ctx->out_queue.bytesperline[0]) * (ctx->out_queue.height); + else + reg = (u32)vb2_dma_contig_plane_dma_addr(&s_vb->vb2_buf, 1); + + dprintk(e5010, 3, + "ctx: 0x%p, luma_addr: 0x%x, chroma_addr: 0x%x, out_addr: 0x%x\n", + ctx, (u32)vb2_dma_contig_plane_dma_addr(&s_vb->vb2_buf, 0) + luma_crop_offset, + reg + chroma_crop_offset, (u32)vb2_dma_contig_plane_dma_addr(&d_vb->vb2_buf, 0)); + + dprintk(e5010, 3, + "ctx: 0x%p, buf indices: src_index: %d, dst_index: %d\n", + ctx, s_vb->vb2_buf.index, d_vb->vb2_buf.index); + + ret = e5010_hw_set_input_chroma_addr(e5010->core_base, reg + chroma_crop_offset); + if (ret || !reg) { + v4l2_err(&e5010->v4l2_dev, "failed to set input chroma address\n"); + goto device_busy_err; + } + + reg = (u32)vb2_dma_contig_plane_dma_addr(&d_vb->vb2_buf, 0); + reg += HEADER_SIZE; + ret = e5010_hw_set_output_base_addr(e5010->core_base, reg); + if (ret || !reg) { + v4l2_err(&e5010->v4l2_dev, "failed to set output base address\n"); + goto device_busy_err; + } + + /* Set input settings */ + ret = e5010_hw_set_horizontal_size(e5010->core_base, ctx->out_queue.crop.width - 1); + if (ret) { + v4l2_err(&e5010->v4l2_dev, "failed to set input width\n"); + goto device_busy_err; + } + + ret = e5010_hw_set_vertical_size(e5010->core_base, ctx->out_queue.crop.height - 1); + if (ret) { + v4l2_err(&e5010->v4l2_dev, "failed to set input width\n"); + goto device_busy_err; + } + + ret = e5010_hw_set_luma_stride(e5010->core_base, ctx->out_queue.bytesperline[0]); + if (ret) { + v4l2_err(&e5010->v4l2_dev, "failed to set luma stride\n"); + goto device_busy_err; + } + + ret = e5010_hw_set_chroma_stride(e5010->core_base, ctx->out_queue.bytesperline[0]); + if (ret) { + v4l2_err(&e5010->v4l2_dev, "failed to set chroma stride\n"); + goto device_busy_err; + } + + ret = e5010_set_input_subsampling(e5010->core_base, ctx->out_queue.fmt->subsampling); + if (ret) { + v4l2_err(&e5010->v4l2_dev, "failed to set input subsampling\n"); + goto device_busy_err; + } + + ret = e5010_hw_set_chroma_order(e5010->core_base, ctx->out_queue.fmt->chroma_order); + if (ret) { + v4l2_err(&e5010->v4l2_dev, "failed to set chroma order\n"); + goto device_busy_err; + } + + e5010_hw_set_output_max_size(e5010->core_base, d_vb->planes[0].length); + e5010_hw_encode_start(e5010->core_base, 1); + + spin_unlock_irqrestore(&e5010->hw_lock, flags); + + return; + +device_busy_err: + e5010_reset(e5010->dev, e5010->core_base, e5010->mmu_base); + +no_ready_buf_err: + if (s_vb) { + v4l2_m2m_src_buf_remove_by_buf(ctx->fh.m2m_ctx, s_vb); + v4l2_m2m_buf_done(s_vb, VB2_BUF_STATE_ERROR); + } + + if (d_vb) { + v4l2_m2m_dst_buf_remove_by_buf(ctx->fh.m2m_ctx, d_vb); + /* Payload set to 1 since 0 payload can trigger EOS */ + vb2_set_plane_payload(&d_vb->vb2_buf, 0, 1); + v4l2_m2m_buf_done(d_vb, VB2_BUF_STATE_ERROR); + } + v4l2_m2m_job_finish(e5010->m2m_dev, ctx->fh.m2m_ctx); + spin_unlock_irqrestore(&e5010->hw_lock, flags); +} + +#ifdef CONFIG_PM +static int e5010_runtime_resume(struct device *dev) +{ + struct e5010_dev *e5010 = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(e5010->clk); + if (ret < 0) { + v4l2_err(&e5010->v4l2_dev, "failed to enable clock\n"); + return ret; + } + + return 0; +} + +static int e5010_runtime_suspend(struct device *dev) +{ + struct e5010_dev *e5010 = dev_get_drvdata(dev); + + clk_disable_unprepare(e5010->clk); + + return 0; +} +#endif + +#ifdef CONFIG_PM_SLEEP +static int e5010_suspend(struct device *dev) +{ + struct e5010_dev *e5010 = dev_get_drvdata(dev); + + v4l2_m2m_suspend(e5010->m2m_dev); + + return pm_runtime_force_suspend(dev); +} + +static int e5010_resume(struct device *dev) +{ + struct e5010_dev *e5010 = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret < 0) + return ret; + + ret = e5010_init_device(e5010); + if (ret) { + dev_err(dev, "Failed to re-enable e5010 device\n"); + return ret; + } + + v4l2_m2m_resume(e5010->m2m_dev); + + return ret; +} +#endif + +static const struct dev_pm_ops e5010_pm_ops = { + SET_RUNTIME_PM_OPS(e5010_runtime_suspend, + e5010_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(e5010_suspend, e5010_resume) +}; + +static const struct v4l2_ioctl_ops e5010_ioctl_ops = { + .vidioc_querycap = e5010_querycap, + + .vidioc_enum_fmt_vid_cap = e5010_enum_fmt, + .vidioc_g_fmt_vid_cap_mplane = e5010_g_fmt, + .vidioc_try_fmt_vid_cap_mplane = e5010_try_fmt, + .vidioc_s_fmt_vid_cap_mplane = e5010_s_fmt, + + .vidioc_enum_fmt_vid_out = e5010_enum_fmt, + .vidioc_g_fmt_vid_out_mplane = e5010_g_fmt, + .vidioc_try_fmt_vid_out_mplane = e5010_try_fmt, + .vidioc_s_fmt_vid_out_mplane = e5010_s_fmt, + + .vidioc_g_selection = e5010_g_selection, + .vidioc_s_selection = e5010_s_selection, + + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + .vidioc_log_status = v4l2_ctrl_log_status, + + .vidioc_subscribe_event = e5010_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd, + .vidioc_encoder_cmd = e5010_encoder_cmd, + + .vidioc_enum_framesizes = e5010_enum_framesizes, +}; + +static const struct vb2_ops e5010_video_ops = { + .queue_setup = e5010_queue_setup, + .buf_queue = e5010_buf_queue, + .buf_finish = e5010_buf_finish, + .buf_prepare = e5010_buf_prepare, + .buf_out_validate = e5010_buf_out_validate, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = e5010_start_streaming, + .stop_streaming = e5010_stop_streaming, +}; + +static const struct v4l2_file_operations e5010_fops = { + .owner = THIS_MODULE, + .open = e5010_open, + .release = e5010_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static const struct v4l2_m2m_ops e5010_m2m_ops = { + .device_run = e5010_device_run, +}; + +static const struct of_device_id e5010_of_match[] = { + {.compatible = "img,e5010-jpeg-enc"}, { /* end */}, +}; +MODULE_DEVICE_TABLE(of, e5010_of_match); + +static struct platform_driver e5010_driver = { + .probe = e5010_probe, + .remove_new = e5010_remove, + .driver = { + .name = E5010_MODULE_NAME, + .of_match_table = e5010_of_match, + .pm = &e5010_pm_ops, + }, +}; +module_platform_driver(e5010_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Imagination E5010 JPEG encoder driver"); diff --git a/drivers/media/platform/imagination/e5010-jpeg-enc.h b/drivers/media/platform/imagination/e5010-jpeg-enc.h new file mode 100644 index 000000000000..71f49ead6898 --- /dev/null +++ b/drivers/media/platform/imagination/e5010-jpeg-enc.h @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Imagination E5010 JPEG Encoder driver. + * + * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/ + * + * Author: David Huang <d-huang@ti.com> + * Author: Devarsh Thakkar <devarsht@ti.com> + */ + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fh.h> + +#ifndef _E5010_JPEG_ENC_H +#define _E5010_JPEG_ENC_H + +#define MAX_PLANES 2 +#define HEADER_SIZE 0x025D +#define MIN_DIMENSION 64 +#define MAX_DIMENSION 8192 +#define DEFAULT_WIDTH 640 +#define DEFAULT_HEIGHT 480 +#define E5010_MODULE_NAME "e5010" +#define JPEG_MAX_BYTES_PER_PIXEL 2 + +/* JPEG marker definitions */ +#define START_OF_IMAGE 0xFFD8 +#define SOF_BASELINE_DCT 0xFFC0 +#define END_OF_IMAGE 0xFFD9 +#define START_OF_SCAN 0xFFDA + +/* Definitions for the huffman table specification in the Marker segment */ +#define DHT_MARKER 0xFFC4 +#define LH_DC 0x001F +#define LH_AC 0x00B5 + +/* Definitions for the quantization table specification in the Marker segment */ +#define DQT_MARKER 0xFFDB +#define ACMAX 0x03FF +#define DCMAX 0x07FF + +/* Length and precision of the quantization table parameters */ +#define LQPQ 0x00430 +#define QMAX 255 + +/* Misc JPEG header definitions */ +#define UC_NUM_COMP 3 +#define PRECISION 8 +#define HORZ_SAMPLING_FACTOR (2 << 4) +#define VERT_SAMPLING_FACTOR_422 1 +#define VERT_SAMPLING_FACTOR_420 2 +#define COMPONENTS_IN_SCAN 3 +#define PELS_IN_BLOCK 64 + +/* Used for Qp table generation */ +#define LUMINOSITY 10 +#define CONTRAST 1 +#define INCREASE 2 +#define QP_TABLE_SIZE (8 * 8) +#define QP_TABLE_FIELD_OFFSET 0x04 + +/* + * vb2 queue structure + * contains queue data information + * + * @fmt: format info + * @width: frame width + * @height: frame height + * @bytesperline: bytes per line in memory + * @size_image: image size in memory + */ +struct e5010_q_data { + struct e5010_fmt *fmt; + u32 width; + u32 height; + u32 width_adjusted; + u32 height_adjusted; + u32 sizeimage[MAX_PLANES]; + u32 bytesperline[MAX_PLANES]; + u32 sequence; + struct v4l2_rect crop; + bool crop_set; +}; + +/* + * Driver device structure + * Holds all memory handles and global parameters + * Shared by all instances + */ +struct e5010_dev { + struct device *dev; + struct v4l2_device v4l2_dev; + struct v4l2_m2m_dev *m2m_dev; + struct video_device *vdev; + void __iomem *core_base; + void __iomem *mmu_base; + struct clk *clk; + struct e5010_context *last_context_run; + /* Protect access to device data */ + struct mutex mutex; + /* Protect access to hardware*/ + spinlock_t hw_lock; +}; + +/* + * Driver context structure + * One of these exists for every m2m context + * Holds context specific data + */ +struct e5010_context { + struct v4l2_fh fh; + struct e5010_dev *e5010; + struct e5010_q_data out_queue; + struct e5010_q_data cap_queue; + int quality; + bool update_qp; + struct v4l2_ctrl_handler ctrl_handler; + u8 luma_qp[QP_TABLE_SIZE]; + u8 chroma_qp[QP_TABLE_SIZE]; +}; + +/* + * Buffer structure + * Contains info for all buffers + */ +struct e5010_buffer { + struct v4l2_m2m_buffer buffer; +}; + +enum { + CHROMA_ORDER_CB_CR = 0, //UV ordering + CHROMA_ORDER_CR_CB = 1, //VU ordering +}; + +enum { + SUBSAMPLING_420 = 1, + SUBSAMPLING_422 = 2, +}; + +/* + * e5010 format structure + * contains format information + */ +struct e5010_fmt { + u32 fourcc; + unsigned int num_planes; + unsigned int type; + u32 subsampling; + u32 chroma_order; + const struct v4l2_frmsize_stepwise frmsize; +}; + +/* + * struct e5010_ctrl - contains info for each supported v4l2 control + */ +struct e5010_ctrl { + unsigned int cid; + enum v4l2_ctrl_type type; + unsigned char name[32]; + int minimum; + int maximum; + int step; + int default_value; + unsigned char compound; +}; + +#endif diff --git a/drivers/media/platform/imagination/e5010-mmu-regs.h b/drivers/media/platform/imagination/e5010-mmu-regs.h new file mode 100644 index 000000000000..bfba06956cf2 --- /dev/null +++ b/drivers/media/platform/imagination/e5010-mmu-regs.h @@ -0,0 +1,311 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Imagination E5010 JPEG Encoder driver. + * + * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/ + * + * Author: David Huang <d-huang@ti.com> + * Author: Devarsh Thakkar <devarsht@ti.com> + */ + +#ifndef _E5010_MMU_REGS_H +#define _E5010_MMU_REGS_H + +#define MMU_MMU_DIR_BASE_ADDR_OFFSET (0x0020) +#define MMU_MMU_DIR_BASE_ADDR_STRIDE (4) +#define MMU_MMU_DIR_BASE_ADDR_NO_ENTRIES (4) + +#define MMU_MMU_DIR_BASE_ADDR_MMU_DIR_BASE_ADDR_MASK (0xFFFFFFFF) +#define MMU_MMU_DIR_BASE_ADDR_MMU_DIR_BASE_ADDR_SHIFT (0) + +#define MMU_MMU_TILE_CFG_OFFSET (0x0040) +#define MMU_MMU_TILE_CFG_STRIDE (4) +#define MMU_MMU_TILE_CFG_NO_ENTRIES (4) + +#define MMU_MMU_TILE_CFG_TILE_128INTERLEAVE_MASK (0x00000010) +#define MMU_MMU_TILE_CFG_TILE_128INTERLEAVE_SHIFT (4) + +#define MMU_MMU_TILE_CFG_TILE_ENABLE_MASK (0x00000008) +#define MMU_MMU_TILE_CFG_TILE_ENABLE_SHIFT (3) + +#define MMU_MMU_TILE_CFG_TILE_STRIDE_MASK (0x00000007) +#define MMU_MMU_TILE_CFG_TILE_STRIDE_SHIFT (0) + +#define MMU_MMU_TILE_MIN_ADDR_OFFSET (0x0050) +#define MMU_MMU_TILE_MIN_ADDR_STRIDE (4) +#define MMU_MMU_TILE_MIN_ADDR_NO_ENTRIES (4) + +#define MMU_MMU_TILE_MIN_ADDR_TILE_MIN_ADDR_MASK (0xFFFFFFFF) +#define MMU_MMU_TILE_MIN_ADDR_TILE_MIN_ADDR_SHIFT (0) + +#define MMU_MMU_TILE_MAX_ADDR_OFFSET (0x0060) +#define MMU_MMU_TILE_MAX_ADDR_STRIDE (4) +#define MMU_MMU_TILE_MAX_ADDR_NO_ENTRIES (4) + +#define MMU_MMU_TILE_MAX_ADDR_TILE_MAX_ADDR_MASK (0xFFFFFFFF) +#define MMU_MMU_TILE_MAX_ADDR_TILE_MAX_ADDR_SHIFT (0) + +#define MMU_MMU_CONTROL0_OFFSET (0x0000) + +#define MMU_MMU_CONTROL0_MMU_TILING_SCHEME_MASK (0x00000001) +#define MMU_MMU_CONTROL0_MMU_TILING_SCHEME_SHIFT (0) + +#define MMU_MMU_CONTROL0_MMU_CACHE_POLICY_MASK (0x00000100) +#define MMU_MMU_CONTROL0_MMU_CACHE_POLICY_SHIFT (8) + +#define MMU_MMU_CONTROL0_FORCE_CACHE_POLICY_BYPASS_MASK (0x00000200) +#define MMU_MMU_CONTROL0_FORCE_CACHE_POLICY_BYPASS_SHIFT (9) + +#define MMU_MMU_CONTROL0_STALL_ON_PROTOCOL_FAULT_MASK (0x00001000) +#define MMU_MMU_CONTROL0_STALL_ON_PROTOCOL_FAULT_SHIFT (12) + +#define MMU_MMU_CONTROL1_OFFSET (0x0008) + +#define MMU_MMU_CONTROL1_MMU_FLUSH_MASK (0x00000008) +#define MMU_MMU_CONTROL1_MMU_FLUSH_SHIFT (3) +#define MMU_MMU_CONTROL1_MMU_FLUSH_NO_REPS (4) +#define MMU_MMU_CONTROL1_MMU_FLUSH_SIZE (1) + +#define MMU_MMU_CONTROL1_MMU_INVALDC_MASK (0x00000800) +#define MMU_MMU_CONTROL1_MMU_INVALDC_SHIFT (11) +#define MMU_MMU_CONTROL1_MMU_INVALDC_NO_REPS (4) +#define MMU_MMU_CONTROL1_MMU_INVALDC_SIZE (1) + +#define MMU_MMU_CONTROL1_MMU_FAULT_CLEAR_MASK (0x00010000) +#define MMU_MMU_CONTROL1_MMU_FAULT_CLEAR_SHIFT (16) + +#define MMU_MMU_CONTROL1_PROTOCOL_FAULT_CLEAR_MASK (0x00100000) +#define MMU_MMU_CONTROL1_PROTOCOL_FAULT_CLEAR_SHIFT (20) + +#define MMU_MMU_CONTROL1_MMU_PAUSE_SET_MASK (0x01000000) +#define MMU_MMU_CONTROL1_MMU_PAUSE_SET_SHIFT (24) + +#define MMU_MMU_CONTROL1_MMU_PAUSE_CLEAR_MASK (0x02000000) +#define MMU_MMU_CONTROL1_MMU_PAUSE_CLEAR_SHIFT (25) + +#define MMU_MMU_CONTROL1_MMU_SOFT_RESET_MASK (0x10000000) +#define MMU_MMU_CONTROL1_MMU_SOFT_RESET_SHIFT (28) + +#define MMU_MMU_BANK_INDEX_OFFSET (0x0010) + +#define MMU_MMU_BANK_INDEX_MMU_BANK_INDEX_MASK (0xC0000000) +#define MMU_MMU_BANK_INDEX_MMU_BANK_INDEX_SHIFT (30) +#define MMU_MMU_BANK_INDEX_MMU_BANK_INDEX_NO_REPS (16) +#define MMU_MMU_BANK_INDEX_MMU_BANK_INDEX_SIZE (2) + +#define MMU_REQUEST_PRIORITY_ENABLE_OFFSET (0x0018) + +#define MMU_REQUEST_PRIORITY_ENABLE_CMD_PRIORITY_ENABLE_MASK (0x00008000) +#define MMU_REQUEST_PRIORITY_ENABLE_CMD_PRIORITY_ENABLE_SHIFT (15) +#define MMU_REQUEST_PRIORITY_ENABLE_CMD_PRIORITY_ENABLE_NO_REPS (16) +#define MMU_REQUEST_PRIORITY_ENABLE_CMD_PRIORITY_ENABLE_SIZE (1) + +#define MMU_REQUEST_PRIORITY_ENABLE_CMD_MMU_PRIORITY_ENABLE_MASK (0x00010000) +#define MMU_REQUEST_PRIORITY_ENABLE_CMD_MMU_PRIORITY_ENABLE_SHIFT (16) + +#define MMU_REQUEST_LIMITED_THROUGHPUT_OFFSET (0x001C) + +#define MMU_REQUEST_LIMITED_THROUGHPUT_LIMITED_WORDS_MASK (0x000003FF) +#define MMU_REQUEST_LIMITED_THROUGHPUT_LIMITED_WORDS_SHIFT (0) + +#define MMU_REQUEST_LIMITED_THROUGHPUT_REQUEST_GAP_MASK (0x0FFF0000) +#define MMU_REQUEST_LIMITED_THROUGHPUT_REQUEST_GAP_SHIFT (16) + +#define MMU_MMU_ADDRESS_CONTROL_OFFSET (0x0070) +#define MMU_MMU_ADDRESS_CONTROL_TRUSTED (IMG_TRUE) + +#define MMU_MMU_ADDRESS_CONTROL_MMU_BYPASS_MASK (0x00000001) +#define MMU_MMU_ADDRESS_CONTROL_MMU_BYPASS_SHIFT (0) + +#define MMU_MMU_ADDRESS_CONTROL_MMU_ENABLE_EXT_ADDRESSING_MASK (0x00000010) +#define MMU_MMU_ADDRESS_CONTROL_MMU_ENABLE_EXT_ADDRESSING_SHIFT (4) + +#define MMU_MMU_ADDRESS_CONTROL_UPPER_ADDRESS_FIXED_MASK (0x00FF0000) +#define MMU_MMU_ADDRESS_CONTROL_UPPER_ADDRESS_FIXED_SHIFT (16) + +#define MMU_MMU_CONFIG0_OFFSET (0x0080) + +#define MMU_MMU_CONFIG0_NUM_REQUESTORS_MASK (0x0000000F) +#define MMU_MMU_CONFIG0_NUM_REQUESTORS_SHIFT (0) + +#define MMU_MMU_CONFIG0_EXTENDED_ADDR_RANGE_MASK (0x000000F0) +#define MMU_MMU_CONFIG0_EXTENDED_ADDR_RANGE_SHIFT (4) + +#define MMU_MMU_CONFIG0_GROUP_OVERRIDE_SIZE_MASK (0x00000700) +#define MMU_MMU_CONFIG0_GROUP_OVERRIDE_SIZE_SHIFT (8) + +#define MMU_MMU_CONFIG0_ADDR_COHERENCY_SUPPORTED_MASK (0x00001000) +#define MMU_MMU_CONFIG0_ADDR_COHERENCY_SUPPORTED_SHIFT (12) + +#define MMU_MMU_CONFIG0_MMU_SUPPORTED_MASK (0x00002000) +#define MMU_MMU_CONFIG0_MMU_SUPPORTED_SHIFT (13) + +#define MMU_MMU_CONFIG0_TILE_ADDR_GRANULARITY_MASK (0x001F0000) +#define MMU_MMU_CONFIG0_TILE_ADDR_GRANULARITY_SHIFT (16) + +#define MMU_MMU_CONFIG0_NO_READ_REORDER_MASK (0x00200000) +#define MMU_MMU_CONFIG0_NO_READ_REORDER_SHIFT (21) + +#define MMU_MMU_CONFIG0_TAGS_SUPPORTED_MASK (0xFFC00000) +#define MMU_MMU_CONFIG0_TAGS_SUPPORTED_SHIFT (22) + +#define MMU_MMU_CONFIG1_OFFSET (0x0084) + +#define MMU_MMU_CONFIG1_PAGE_SIZE_MASK (0x0000000F) +#define MMU_MMU_CONFIG1_PAGE_SIZE_SHIFT (0) + +#define MMU_MMU_CONFIG1_PAGE_CACHE_ENTRIES_MASK (0x0000FF00) +#define MMU_MMU_CONFIG1_PAGE_CACHE_ENTRIES_SHIFT (8) + +#define MMU_MMU_CONFIG1_DIR_CACHE_ENTRIES_MASK (0x001F0000) +#define MMU_MMU_CONFIG1_DIR_CACHE_ENTRIES_SHIFT (16) + +#define MMU_MMU_CONFIG1_BANDWIDTH_COUNT_SUPPORTED_MASK (0x01000000) +#define MMU_MMU_CONFIG1_BANDWIDTH_COUNT_SUPPORTED_SHIFT (24) + +#define MMU_MMU_CONFIG1_STALL_COUNT_SUPPORTED_MASK (0x02000000) +#define MMU_MMU_CONFIG1_STALL_COUNT_SUPPORTED_SHIFT (25) + +#define MMU_MMU_CONFIG1_LATENCY_COUNT_SUPPORTED_MASK (0x04000000) +#define MMU_MMU_CONFIG1_LATENCY_COUNT_SUPPORTED_SHIFT (26) + +#define MMU_MMU_STATUS0_OFFSET (0x0088) + +#define MMU_MMU_STATUS0_MMU_PF_N_RW_MASK (0x00000001) +#define MMU_MMU_STATUS0_MMU_PF_N_RW_SHIFT (0) + +#define MMU_MMU_STATUS0_MMU_FAULT_ADDR_MASK (0xFFFFF000) +#define MMU_MMU_STATUS0_MMU_FAULT_ADDR_SHIFT (12) + +#define MMU_MMU_STATUS1_OFFSET (0x008C) + +#define MMU_MMU_STATUS1_MMU_FAULT_REQ_STAT_MASK (0x0000FFFF) +#define MMU_MMU_STATUS1_MMU_FAULT_REQ_STAT_SHIFT (0) + +#define MMU_MMU_STATUS1_MMU_FAULT_REQ_ID_MASK (0x000F0000) +#define MMU_MMU_STATUS1_MMU_FAULT_REQ_ID_SHIFT (16) + +#define MMU_MMU_STATUS1_MMU_FAULT_INDEX_MASK (0x03000000) +#define MMU_MMU_STATUS1_MMU_FAULT_INDEX_SHIFT (24) + +#define MMU_MMU_STATUS1_MMU_FAULT_RNW_MASK (0x10000000) +#define MMU_MMU_STATUS1_MMU_FAULT_RNW_SHIFT (28) + +#define MMU_MMU_MEM_REQ_OFFSET (0x0090) + +#define MMU_MMU_MEM_REQ_TAG_OUTSTANDING_MASK (0x000003FF) +#define MMU_MMU_MEM_REQ_TAG_OUTSTANDING_SHIFT (0) + +#define MMU_MMU_MEM_REQ_EXT_WRRESP_FAULT_MASK (0x00001000) +#define MMU_MMU_MEM_REQ_EXT_WRRESP_FAULT_SHIFT (12) + +#define MMU_MMU_MEM_REQ_EXT_RDRESP_FAULT_MASK (0x00002000) +#define MMU_MMU_MEM_REQ_EXT_RDRESP_FAULT_SHIFT (13) + +#define MMU_MMU_MEM_REQ_EXT_READ_BURST_FAULT_MASK (0x00004000) +#define MMU_MMU_MEM_REQ_EXT_READ_BURST_FAULT_SHIFT (14) + +#define MMU_MMU_MEM_REQ_INT_PROTOCOL_FAULT_MASK (0x80000000) +#define MMU_MMU_MEM_REQ_INT_PROTOCOL_FAULT_SHIFT (31) +#define MMU_MMU_MEM_REQ_INT_PROTOCOL_FAULT_NO_REPS (16) +#define MMU_MMU_MEM_REQ_INT_PROTOCOL_FAULT_SIZE (1) + +#define MMU_MMU_FAULT_SELECT_OFFSET (0x00A0) + +#define MMU_MMU_FAULT_SELECT_MMU_FAULT_SELECT_MASK (0x0000000F) +#define MMU_MMU_FAULT_SELECT_MMU_FAULT_SELECT_SHIFT (0) + +#define MMU_PROTOCOL_FAULT_OFFSET (0x00A8) + +#define MMU_PROTOCOL_FAULT_FAULT_PAGE_BREAK_MASK (0x00000001) +#define MMU_PROTOCOL_FAULT_FAULT_PAGE_BREAK_SHIFT (0) + +#define MMU_PROTOCOL_FAULT_FAULT_WRITE_MASK (0x00000010) +#define MMU_PROTOCOL_FAULT_FAULT_WRITE_SHIFT (4) + +#define MMU_PROTOCOL_FAULT_FAULT_READ_MASK (0x00000020) +#define MMU_PROTOCOL_FAULT_FAULT_READ_SHIFT (5) + +#define MMU_TOTAL_READ_REQ_OFFSET (0x0100) + +#define MMU_TOTAL_READ_REQ_TOTAL_READ_REQ_MASK (0xFFFFFFFF) +#define MMU_TOTAL_READ_REQ_TOTAL_READ_REQ_SHIFT (0) + +#define MMU_TOTAL_WRITE_REQ_OFFSET (0x0104) + +#define MMU_TOTAL_WRITE_REQ_TOTAL_WRITE_REQ_MASK (0xFFFFFFFF) +#define MMU_TOTAL_WRITE_REQ_TOTAL_WRITE_REQ_SHIFT (0) + +#define MMU_READS_LESS_64_REQ_OFFSET (0x0108) + +#define MMU_READS_LESS_64_REQ_READS_LESS_64_REQ_MASK (0xFFFFFFFF) +#define MMU_READS_LESS_64_REQ_READS_LESS_64_REQ_SHIFT (0) + +#define MMU_WRITES_LESS_64_REQ_OFFSET (0x010C) + +#define MMU_WRITES_LESS_64_REQ_WRITES_LESS_64_REQ_MASK (0xFFFFFFFF) +#define MMU_WRITES_LESS_64_REQ_WRITES_LESS_64_REQ_SHIFT (0) + +#define MMU_EXT_CMD_STALL_OFFSET (0x0120) + +#define MMU_EXT_CMD_STALL_EXT_CMD_STALL_MASK (0xFFFFFFFF) +#define MMU_EXT_CMD_STALL_EXT_CMD_STALL_SHIFT (0) + +#define MMU_WRITE_REQ_STALL_OFFSET (0x0124) + +#define MMU_WRITE_REQ_STALL_WRITE_REQ_STALL_MASK (0xFFFFFFFF) +#define MMU_WRITE_REQ_STALL_WRITE_REQ_STALL_SHIFT (0) + +#define MMU_MMU_MISS_STALL_OFFSET (0x0128) + +#define MMU_MMU_MISS_STALL_MMU_MISS_STALL_MASK (0xFFFFFFFF) +#define MMU_MMU_MISS_STALL_MMU_MISS_STALL_SHIFT (0) + +#define MMU_ADDRESS_STALL_OFFSET (0x012C) + +#define MMU_ADDRESS_STALL_ADDRESS_STALL_MASK (0xFFFFFFFF) +#define MMU_ADDRESS_STALL_ADDRESS_STALL_SHIFT (0) + +#define MMU_TAG_STALL_OFFSET (0x0130) + +#define MMU_TAG_STALL_TAG_STALL_MASK (0xFFFFFFFF) +#define MMU_TAG_STALL_TAG_STALL_SHIFT (0) + +#define MMU_PEAK_READ_OUTSTANDING_OFFSET (0x0140) + +#define MMU_PEAK_READ_OUTSTANDING_PEAK_TAG_OUTSTANDING_MASK (0x000003FF) +#define MMU_PEAK_READ_OUTSTANDING_PEAK_TAG_OUTSTANDING_SHIFT (0) + +#define MMU_PEAK_READ_OUTSTANDING_PEAK_READ_LATENCY_MASK (0xFFFF0000) +#define MMU_PEAK_READ_OUTSTANDING_PEAK_READ_LATENCY_SHIFT (16) + +#define MMU_AVERAGE_READ_LATENCY_OFFSET (0x0144) + +#define MMU_AVERAGE_READ_LATENCY_AVERAGE_READ_LATENCY_MASK (0xFFFFFFFF) +#define MMU_AVERAGE_READ_LATENCY_AVERAGE_READ_LATENCY_SHIFT (0) + +#define MMU_STATISTICS_CONTROL_OFFSET (0x0160) + +#define MMU_STATISTICS_CONTROL_BANDWIDTH_STATS_INIT_MASK (0x00000001) +#define MMU_STATISTICS_CONTROL_BANDWIDTH_STATS_INIT_SHIFT (0) + +#define MMU_STATISTICS_CONTROL_STALL_STATS_INIT_MASK (0x00000002) +#define MMU_STATISTICS_CONTROL_STALL_STATS_INIT_SHIFT (1) + +#define MMU_STATISTICS_CONTROL_LATENCY_STATS_INIT_MASK (0x00000004) +#define MMU_STATISTICS_CONTROL_LATENCY_STATS_INIT_SHIFT (2) + +#define MMU_MMU_VERSION_OFFSET (0x01D0) + +#define MMU_MMU_VERSION_MMU_MAJOR_REV_MASK (0x00FF0000) +#define MMU_MMU_VERSION_MMU_MAJOR_REV_SHIFT (16) + +#define MMU_MMU_VERSION_MMU_MINOR_REV_MASK (0x0000FF00) +#define MMU_MMU_VERSION_MMU_MINOR_REV_SHIFT (8) + +#define MMU_MMU_VERSION_MMU_MAINT_REV_MASK (0x000000FF) +#define MMU_MMU_VERSION_MMU_MAINT_REV_SHIFT (0) + +#define MMU_BYTE_SIZE (0x01D4) + +#endif diff --git a/drivers/media/platform/intel/pxa_camera.c b/drivers/media/platform/intel/pxa_camera.c index d904952bf00e..f118aaac0b38 100644 --- a/drivers/media/platform/intel/pxa_camera.c +++ b/drivers/media/platform/intel/pxa_camera.c @@ -43,6 +43,7 @@ #include <linux/videodev2.h> #include <linux/platform_data/media/camera-pxa.h> +#include <linux/workqueue.h> #define PXA_CAM_VERSION "0.0.6" #define PXA_CAM_DRV_NAME "pxa27x-camera" @@ -683,7 +684,7 @@ struct pxa_camera_dev { unsigned int buf_sequence; struct pxa_buffer *active; - struct tasklet_struct task_eof; + struct work_struct eof_bh_work; u32 save_cicr[5]; }; @@ -1146,9 +1147,9 @@ static void pxa_camera_deactivate(struct pxa_camera_dev *pcdev) clk_disable_unprepare(pcdev->clk); } -static void pxa_camera_eof(struct tasklet_struct *t) +static void pxa_camera_eof_bh_work(struct work_struct *t) { - struct pxa_camera_dev *pcdev = from_tasklet(pcdev, t, task_eof); + struct pxa_camera_dev *pcdev = from_work(pcdev, t, eof_bh_work); unsigned long cifr; struct pxa_buffer *buf; @@ -1185,7 +1186,7 @@ static irqreturn_t pxa_camera_irq(int irq, void *data) if (status & CISR_EOF) { cicr0 = __raw_readl(pcdev->base + CICR0) | CICR0_EOFM; __raw_writel(cicr0, pcdev->base + CICR0); - tasklet_schedule(&pcdev->task_eof); + queue_work(system_bh_wq, &pcdev->eof_bh_work); } return IRQ_HANDLED; @@ -2383,7 +2384,7 @@ static int pxa_camera_probe(struct platform_device *pdev) } } - tasklet_setup(&pcdev->task_eof, pxa_camera_eof); + INIT_WORK(&pcdev->eof_bh_work, pxa_camera_eof_bh_work); pxa_camera_activate(pcdev); @@ -2409,7 +2410,7 @@ static int pxa_camera_probe(struct platform_device *pdev) return 0; exit_deactivate: pxa_camera_deactivate(pcdev); - tasklet_kill(&pcdev->task_eof); + cancel_work_sync(&pcdev->eof_bh_work); exit_free_dma: dma_release_channel(pcdev->dma_chans[2]); exit_free_dma_u: @@ -2428,7 +2429,7 @@ static void pxa_camera_remove(struct platform_device *pdev) struct pxa_camera_dev *pcdev = platform_get_drvdata(pdev); pxa_camera_deactivate(pcdev); - tasklet_kill(&pcdev->task_eof); + cancel_work_sync(&pcdev->eof_bh_work); dma_release_channel(pcdev->dma_chans[0]); dma_release_channel(pcdev->dma_chans[1]); dma_release_channel(pcdev->dma_chans[2]); diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c index 96b35a5d6174..5adcef80c698 100644 --- a/drivers/media/platform/m2m-deinterlace.c +++ b/drivers/media/platform/m2m-deinterlace.c @@ -724,10 +724,6 @@ static const struct v4l2_ioctl_ops deinterlace_ioctl_ops = { /* * Queue operations */ -struct vb2_dc_conf { - struct device *dev; -}; - static int deinterlace_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], struct device *alloc_devs[]) diff --git a/drivers/media/platform/marvell/mcam-core.c b/drivers/media/platform/marvell/mcam-core.c index 66688b4aece5..c81593c969e0 100644 --- a/drivers/media/platform/marvell/mcam-core.c +++ b/drivers/media/platform/marvell/mcam-core.c @@ -439,9 +439,9 @@ static void mcam_ctlr_dma_vmalloc(struct mcam_camera *cam) /* * Copy data out to user space in the vmalloc case */ -static void mcam_frame_tasklet(struct tasklet_struct *t) +static void mcam_frame_work(struct work_struct *t) { - struct mcam_camera *cam = from_tasklet(cam, t, s_tasklet); + struct mcam_camera *cam = from_work(cam, t, s_bh_work); int i; unsigned long flags; struct mcam_vb_buffer *buf; @@ -493,7 +493,7 @@ static int mcam_check_dma_buffers(struct mcam_camera *cam) static void mcam_vmalloc_done(struct mcam_camera *cam, int frame) { - tasklet_schedule(&cam->s_tasklet); + queue_work(system_bh_wq, &cam->s_bh_work); } #else /* MCAM_MODE_VMALLOC */ @@ -1305,7 +1305,7 @@ static int mcam_setup_vb2(struct mcam_camera *cam) break; case B_vmalloc: #ifdef MCAM_MODE_VMALLOC - tasklet_setup(&cam->s_tasklet, mcam_frame_tasklet); + INIT_WORK(&cam->s_bh_work, mcam_frame_work); vq->ops = &mcam_vb2_ops; vq->mem_ops = &vb2_vmalloc_memops; cam->dma_setup = mcam_ctlr_dma_vmalloc; @@ -1981,5 +1981,6 @@ int mccic_resume(struct mcam_camera *cam) } EXPORT_SYMBOL_GPL(mccic_resume); +MODULE_DESCRIPTION("Marvell camera core driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>"); diff --git a/drivers/media/platform/marvell/mcam-core.h b/drivers/media/platform/marvell/mcam-core.h index 51e66db45af6..989dc6859a53 100644 --- a/drivers/media/platform/marvell/mcam-core.h +++ b/drivers/media/platform/marvell/mcam-core.h @@ -9,6 +9,7 @@ #include <linux/list.h> #include <linux/clk-provider.h> +#include <linux/workqueue.h> #include <media/v4l2-common.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-dev.h> @@ -167,7 +168,7 @@ struct mcam_camera { unsigned int dma_buf_size; /* allocated size */ void *dma_bufs[MAX_DMA_BUFS]; /* Internal buffer addresses */ dma_addr_t dma_handles[MAX_DMA_BUFS]; /* Buffer bus addresses */ - struct tasklet_struct s_tasklet; + struct work_struct s_bh_work; #endif unsigned int sequence; /* Frame sequence number */ unsigned int buf_seq[MAX_DMA_BUFS]; /* Sequence for individual bufs */ diff --git a/drivers/media/platform/marvell/mmp-driver.c b/drivers/media/platform/marvell/mmp-driver.c index 170907cc1885..ff9d151121d5 100644 --- a/drivers/media/platform/marvell/mmp-driver.c +++ b/drivers/media/platform/marvell/mmp-driver.c @@ -30,6 +30,7 @@ MODULE_ALIAS("platform:mmp-camera"); MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>"); +MODULE_DESCRIPTION("Support for the camera device found on Marvell MMP processors"); MODULE_LICENSE("GPL"); static char *mcam_clks[] = {"axi", "func", "phy"}; diff --git a/drivers/media/platform/mediatek/mdp3/mdp_cfg_data.c b/drivers/media/platform/mediatek/mdp3/mdp_cfg_data.c index ecca52b45307..0b4c50bc1776 100644 --- a/drivers/media/platform/mediatek/mdp3/mdp_cfg_data.c +++ b/drivers/media/platform/mediatek/mdp3/mdp_cfg_data.c @@ -46,6 +46,53 @@ enum mt8183_mdp_comp_id { MT8183_MDP_COMP_WROT1, /* 25 */ }; +enum mt8188_mdp_comp_id { + /* MT8188 Comp id */ + /* ISP */ + MT8188_MDP_COMP_WPEI = 0, + MT8188_MDP_COMP_WPEO, /* 1 */ + + /* MDP */ + MT8188_MDP_COMP_CAMIN, /* 2 */ + MT8188_MDP_COMP_RDMA0, /* 3 */ + MT8188_MDP_COMP_RDMA2, /* 4 */ + MT8188_MDP_COMP_RDMA3, /* 5 */ + MT8188_MDP_COMP_FG0, /* 6 */ + MT8188_MDP_COMP_FG2, /* 7 */ + MT8188_MDP_COMP_FG3, /* 8 */ + MT8188_MDP_COMP_TO_SVPP2MOUT, /* 9 */ + MT8188_MDP_COMP_TO_SVPP3MOUT, /* 10 */ + MT8188_MDP_COMP_TO_WARP0MOUT, /* 11 */ + MT8188_MDP_COMP_VPP0_SOUT, /* 12 */ + MT8188_MDP_COMP_VPP1_SOUT, /* 13 */ + MT8188_MDP_COMP_PQ0_SOUT, /* 14 */ + MT8188_MDP_COMP_HDR0, /* 15 */ + MT8188_MDP_COMP_HDR2, /* 16 */ + MT8188_MDP_COMP_HDR3, /* 17 */ + MT8188_MDP_COMP_AAL0, /* 18 */ + MT8188_MDP_COMP_AAL2, /* 19 */ + MT8188_MDP_COMP_AAL3, /* 20 */ + MT8188_MDP_COMP_RSZ0, /* 21 */ + MT8188_MDP_COMP_RSZ2, /* 22 */ + MT8188_MDP_COMP_RSZ3, /* 23 */ + MT8188_MDP_COMP_TDSHP0, /* 24 */ + MT8188_MDP_COMP_TDSHP2, /* 25 */ + MT8188_MDP_COMP_TDSHP3, /* 26 */ + MT8188_MDP_COMP_COLOR0, /* 27 */ + MT8188_MDP_COMP_COLOR2, /* 28 */ + MT8188_MDP_COMP_COLOR3, /* 29 */ + MT8188_MDP_COMP_OVL0, /* 30 */ + MT8188_MDP_COMP_PAD0, /* 31 */ + MT8188_MDP_COMP_PAD2, /* 32 */ + MT8188_MDP_COMP_PAD3, /* 33 */ + MT8188_MDP_COMP_TCC0, /* 34 */ + MT8188_MDP_COMP_WROT0, /* 35 */ + MT8188_MDP_COMP_WROT2, /* 36 */ + MT8188_MDP_COMP_WROT3, /* 37 */ + MT8188_MDP_COMP_MERGE2, /* 38 */ + MT8188_MDP_COMP_MERGE3, /* 39 */ +}; + enum mt8195_mdp_comp_id { /* MT8195 Comp id */ /* ISP */ @@ -123,6 +170,13 @@ static const struct of_device_id mt8183_mdp_probe_infra[MDP_INFRA_MAX] = { [MDP_INFRA_SCP] = { .compatible = "mediatek,mt8183-scp" } }; +static const struct of_device_id mt8188_mdp_probe_infra[MDP_INFRA_MAX] = { + [MDP_INFRA_MMSYS] = { .compatible = "mediatek,mt8188-vppsys0" }, + [MDP_INFRA_MMSYS2] = { .compatible = "mediatek,mt8188-vppsys1" }, + [MDP_INFRA_MUTEX] = { .compatible = "mediatek,mt8188-vpp-mutex" }, + [MDP_INFRA_MUTEX2] = { .compatible = "mediatek,mt8188-vpp-mutex" }, +}; + static const struct of_device_id mt8195_mdp_probe_infra[MDP_INFRA_MAX] = { [MDP_INFRA_MMSYS] = { .compatible = "mediatek,mt8195-vppsys0" }, [MDP_INFRA_MMSYS2] = { .compatible = "mediatek,mt8195-vppsys1" }, @@ -167,6 +221,40 @@ static const u32 mt8183_mutex_idx[MDP_MAX_COMP_COUNT] = { [MDP_COMP_CCORR0] = MUTEX_MOD_IDX_MDP_CCORR0, }; +static const u32 mt8188_mutex_idx[MDP_MAX_COMP_COUNT] = { + [MDP_COMP_RDMA0] = MUTEX_MOD_IDX_MDP_RDMA0, + [MDP_COMP_RDMA2] = MUTEX_MOD_IDX_MDP_RDMA2, + [MDP_COMP_RDMA3] = MUTEX_MOD_IDX_MDP_RDMA3, + [MDP_COMP_FG0] = MUTEX_MOD_IDX_MDP_FG0, + [MDP_COMP_FG2] = MUTEX_MOD_IDX_MDP_FG2, + [MDP_COMP_FG3] = MUTEX_MOD_IDX_MDP_FG3, + [MDP_COMP_HDR0] = MUTEX_MOD_IDX_MDP_HDR0, + [MDP_COMP_HDR2] = MUTEX_MOD_IDX_MDP_HDR2, + [MDP_COMP_HDR3] = MUTEX_MOD_IDX_MDP_HDR3, + [MDP_COMP_AAL0] = MUTEX_MOD_IDX_MDP_AAL0, + [MDP_COMP_AAL2] = MUTEX_MOD_IDX_MDP_AAL2, + [MDP_COMP_AAL3] = MUTEX_MOD_IDX_MDP_AAL3, + [MDP_COMP_RSZ0] = MUTEX_MOD_IDX_MDP_RSZ0, + [MDP_COMP_RSZ2] = MUTEX_MOD_IDX_MDP_RSZ2, + [MDP_COMP_RSZ3] = MUTEX_MOD_IDX_MDP_RSZ3, + [MDP_COMP_MERGE2] = MUTEX_MOD_IDX_MDP_MERGE2, + [MDP_COMP_MERGE3] = MUTEX_MOD_IDX_MDP_MERGE3, + [MDP_COMP_TDSHP0] = MUTEX_MOD_IDX_MDP_TDSHP0, + [MDP_COMP_TDSHP2] = MUTEX_MOD_IDX_MDP_TDSHP2, + [MDP_COMP_TDSHP3] = MUTEX_MOD_IDX_MDP_TDSHP3, + [MDP_COMP_COLOR0] = MUTEX_MOD_IDX_MDP_COLOR0, + [MDP_COMP_COLOR2] = MUTEX_MOD_IDX_MDP_COLOR2, + [MDP_COMP_COLOR3] = MUTEX_MOD_IDX_MDP_COLOR3, + [MDP_COMP_OVL0] = MUTEX_MOD_IDX_MDP_OVL0, + [MDP_COMP_PAD0] = MUTEX_MOD_IDX_MDP_PAD0, + [MDP_COMP_PAD2] = MUTEX_MOD_IDX_MDP_PAD2, + [MDP_COMP_PAD3] = MUTEX_MOD_IDX_MDP_PAD3, + [MDP_COMP_TCC0] = MUTEX_MOD_IDX_MDP_TCC0, + [MDP_COMP_WROT0] = MUTEX_MOD_IDX_MDP_WROT0, + [MDP_COMP_WROT2] = MUTEX_MOD_IDX_MDP_WROT2, + [MDP_COMP_WROT3] = MUTEX_MOD_IDX_MDP_WROT3, +}; + static const u32 mt8195_mutex_idx[MDP_MAX_COMP_COUNT] = { [MDP_COMP_RDMA0] = MUTEX_MOD_IDX_MDP_RDMA0, [MDP_COMP_RDMA1] = MUTEX_MOD_IDX_MDP_RDMA1, @@ -288,6 +376,171 @@ static const struct mdp_comp_data mt8183_mdp_comp_data[MDP_MAX_COMP_COUNT] = { }, }; +static const struct mdp_comp_data mt8188_mdp_comp_data[MDP_MAX_COMP_COUNT] = { + [MDP_COMP_WPEI] = { + {MDP_COMP_TYPE_WPEI, 0, MT8188_MDP_COMP_WPEI, MDP_MM_SUBSYS_0}, + {0, 0, 0} + }, + [MDP_COMP_WPEO] = { + {MDP_COMP_TYPE_EXTO, 0, MT8188_MDP_COMP_WPEO, MDP_MM_SUBSYS_0}, + {0, 0, 0} + }, + [MDP_COMP_CAMIN] = { + {MDP_COMP_TYPE_DL_PATH, 0, MT8188_MDP_COMP_CAMIN, MDP_MM_SUBSYS_0}, + {3, 3, 0} + }, + [MDP_COMP_RDMA0] = { + {MDP_COMP_TYPE_RDMA, 0, MT8188_MDP_COMP_RDMA0, MDP_MM_SUBSYS_0}, + {3, 0, 0} + }, + [MDP_COMP_RDMA2] = { + {MDP_COMP_TYPE_RDMA, 1, MT8188_MDP_COMP_RDMA2, MDP_MM_SUBSYS_1}, + {3, 0, 0} + }, + [MDP_COMP_RDMA3] = { + {MDP_COMP_TYPE_RDMA, 2, MT8188_MDP_COMP_RDMA3, MDP_MM_SUBSYS_1}, + {3, 0, 0} + }, + [MDP_COMP_FG0] = { + {MDP_COMP_TYPE_FG, 0, MT8188_MDP_COMP_FG0, MDP_MM_SUBSYS_0}, + {1, 0, 0} + }, + [MDP_COMP_FG2] = { + {MDP_COMP_TYPE_FG, 1, MT8188_MDP_COMP_FG2, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_FG3] = { + {MDP_COMP_TYPE_FG, 2, MT8188_MDP_COMP_FG3, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_HDR0] = { + {MDP_COMP_TYPE_HDR, 0, MT8188_MDP_COMP_HDR0, MDP_MM_SUBSYS_0}, + {1, 0, 0} + }, + [MDP_COMP_HDR2] = { + {MDP_COMP_TYPE_HDR, 1, MT8188_MDP_COMP_HDR2, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_HDR3] = { + {MDP_COMP_TYPE_HDR, 2, MT8188_MDP_COMP_HDR3, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_AAL0] = { + {MDP_COMP_TYPE_AAL, 0, MT8188_MDP_COMP_AAL0, MDP_MM_SUBSYS_0}, + {1, 0, 0} + }, + [MDP_COMP_AAL2] = { + {MDP_COMP_TYPE_AAL, 1, MT8188_MDP_COMP_AAL2, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_AAL3] = { + {MDP_COMP_TYPE_AAL, 2, MT8188_MDP_COMP_AAL3, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_RSZ0] = { + {MDP_COMP_TYPE_RSZ, 0, MT8188_MDP_COMP_RSZ0, MDP_MM_SUBSYS_0}, + {1, 0, 0} + }, + [MDP_COMP_RSZ2] = { + {MDP_COMP_TYPE_RSZ, 1, MT8188_MDP_COMP_RSZ2, MDP_MM_SUBSYS_1}, + {2, 0, 0}, + {MDP_COMP_MERGE2, true, true} + }, + [MDP_COMP_RSZ3] = { + {MDP_COMP_TYPE_RSZ, 2, MT8188_MDP_COMP_RSZ3, MDP_MM_SUBSYS_1}, + {2, 0, 0}, + {MDP_COMP_MERGE3, true, true} + }, + [MDP_COMP_TDSHP0] = { + {MDP_COMP_TYPE_TDSHP, 0, MT8188_MDP_COMP_TDSHP0, MDP_MM_SUBSYS_0}, + {1, 0, 0} + }, + [MDP_COMP_TDSHP2] = { + {MDP_COMP_TYPE_TDSHP, 1, MT8188_MDP_COMP_TDSHP2, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_TDSHP3] = { + {MDP_COMP_TYPE_TDSHP, 2, MT8188_MDP_COMP_TDSHP3, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_COLOR0] = { + {MDP_COMP_TYPE_COLOR, 0, MT8188_MDP_COMP_COLOR0, MDP_MM_SUBSYS_0}, + {1, 0, 0} + }, + [MDP_COMP_COLOR2] = { + {MDP_COMP_TYPE_COLOR, 1, MT8188_MDP_COMP_COLOR2, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_COLOR3] = { + {MDP_COMP_TYPE_COLOR, 2, MT8188_MDP_COMP_COLOR3, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_OVL0] = { + {MDP_COMP_TYPE_OVL, 0, MT8188_MDP_COMP_OVL0, MDP_MM_SUBSYS_0}, + {1, 0, 0} + }, + [MDP_COMP_PAD0] = { + {MDP_COMP_TYPE_PAD, 0, MT8188_MDP_COMP_PAD0, MDP_MM_SUBSYS_0}, + {1, 0, 0} + }, + [MDP_COMP_PAD2] = { + {MDP_COMP_TYPE_PAD, 1, MT8188_MDP_COMP_PAD2, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_PAD3] = { + {MDP_COMP_TYPE_PAD, 2, MT8188_MDP_COMP_PAD3, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_TCC0] = { + {MDP_COMP_TYPE_TCC, 0, MT8188_MDP_COMP_TCC0, MDP_MM_SUBSYS_0}, + {1, 0, 0} + }, + [MDP_COMP_WROT0] = { + {MDP_COMP_TYPE_WROT, 0, MT8188_MDP_COMP_WROT0, MDP_MM_SUBSYS_0}, + {1, 0, 0} + }, + [MDP_COMP_WROT2] = { + {MDP_COMP_TYPE_WROT, 1, MT8188_MDP_COMP_WROT2, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_WROT3] = { + {MDP_COMP_TYPE_WROT, 2, MT8188_MDP_COMP_WROT3, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_MERGE2] = { + {MDP_COMP_TYPE_MERGE, 0, MT8188_MDP_COMP_MERGE2, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_MERGE3] = { + {MDP_COMP_TYPE_MERGE, 1, MT8188_MDP_COMP_MERGE3, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_PQ0_SOUT] = { + {MDP_COMP_TYPE_DUMMY, 0, MT8188_MDP_COMP_PQ0_SOUT, MDP_MM_SUBSYS_0}, + {0, 0, 0} + }, + [MDP_COMP_TO_WARP0MOUT] = { + {MDP_COMP_TYPE_DUMMY, 1, MT8188_MDP_COMP_TO_WARP0MOUT, MDP_MM_SUBSYS_0}, + {0, 0, 0} + }, + [MDP_COMP_TO_SVPP2MOUT] = { + {MDP_COMP_TYPE_DUMMY, 2, MT8188_MDP_COMP_TO_SVPP2MOUT, MDP_MM_SUBSYS_1}, + {0, 0, 0} + }, + [MDP_COMP_TO_SVPP3MOUT] = { + {MDP_COMP_TYPE_DUMMY, 3, MT8188_MDP_COMP_TO_SVPP3MOUT, MDP_MM_SUBSYS_1}, + {0, 0, 0} + }, + [MDP_COMP_VPP0_SOUT] = { + {MDP_COMP_TYPE_PATH, 0, MT8188_MDP_COMP_VPP0_SOUT, MDP_MM_SUBSYS_1}, + {2, 6, 0} + }, + [MDP_COMP_VPP1_SOUT] = { + {MDP_COMP_TYPE_PATH, 1, MT8188_MDP_COMP_VPP1_SOUT, MDP_MM_SUBSYS_0}, + {2, 8, 0} + }, +}; + static const struct mdp_comp_data mt8195_mdp_comp_data[MDP_MAX_COMP_COUNT] = { [MDP_COMP_WPEI] = { {MDP_COMP_TYPE_WPEI, 0, MT8195_MDP_COMP_WPEI, MDP_MM_SUBSYS_0}, @@ -1046,6 +1299,15 @@ static const struct mdp_pipe_info mt8183_pipe_info[] = { [MDP_PIPE_RDMA0] = {MDP_PIPE_RDMA0, MDP_MM_SUBSYS_0, 3} }; +static const struct mdp_pipe_info mt8188_pipe_info[] = { + [MDP_PIPE_WPEI] = {MDP_PIPE_WPEI, MDP_MM_SUBSYS_0, 0}, + [MDP_PIPE_RDMA0] = {MDP_PIPE_RDMA0, MDP_MM_SUBSYS_0, 1}, + [MDP_PIPE_RDMA2] = {MDP_PIPE_RDMA2, MDP_MM_SUBSYS_1, 0}, + [MDP_PIPE_RDMA3] = {MDP_PIPE_RDMA3, MDP_MM_SUBSYS_1, 1}, + [MDP_PIPE_VPP1_SOUT] = {MDP_PIPE_VPP1_SOUT, MDP_MM_SUBSYS_0, 2}, + [MDP_PIPE_VPP0_SOUT] = {MDP_PIPE_VPP0_SOUT, MDP_MM_SUBSYS_1, 2}, +}; + static const struct mdp_pipe_info mt8195_pipe_info[] = { [MDP_PIPE_WPEI] = {MDP_PIPE_WPEI, MDP_MM_SUBSYS_0, 0}, [MDP_PIPE_WPEI2] = {MDP_PIPE_WPEI2, MDP_MM_SUBSYS_0, 1}, @@ -1082,6 +1344,24 @@ const struct mtk_mdp_driver_data mt8183_mdp_driver_data = { .pp_used = MDP_PP_USED_1, }; +const struct mtk_mdp_driver_data mt8188_mdp_driver_data = { + .mdp_plat_id = MT8188, + .mdp_con_res = 0x14001000, + .mdp_probe_infra = mt8188_mdp_probe_infra, + .mdp_sub_comp_dt_ids = mt8195_sub_comp_dt_ids, + .mdp_cfg = &mt8195_plat_cfg, + .mdp_mutex_table_idx = mt8188_mutex_idx, + .comp_data = mt8188_mdp_comp_data, + .comp_data_len = ARRAY_SIZE(mt8188_mdp_comp_data), + .format = mt8195_formats, + .format_len = ARRAY_SIZE(mt8195_formats), + .def_limit = &mt8195_mdp_def_limit, + .pipe_info = mt8188_pipe_info, + .pipe_info_len = ARRAY_SIZE(mt8188_pipe_info), + .pp_criteria = &mt8195_mdp_pp_criteria, + .pp_used = MDP_PP_USED_2, +}; + const struct mtk_mdp_driver_data mt8195_mdp_driver_data = { .mdp_plat_id = MT8195, .mdp_con_res = 0x14001000, diff --git a/drivers/media/platform/mediatek/mdp3/mtk-img-ipi.h b/drivers/media/platform/mediatek/mdp3/mtk-img-ipi.h index f83ac408306e..4764c5b5107b 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-img-ipi.h +++ b/drivers/media/platform/mediatek/mdp3/mtk-img-ipi.h @@ -116,6 +116,7 @@ struct img_frameparam { /* Platform config indicator */ #define MT8183 8183 +#define MT8188 8195 #define MT8195 8195 #define CFG_CHECK(plat, p_id) ((plat) == (p_id)) diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cfg.h b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cfg.h index 49cdf45f6e59..7f7625299ce7 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cfg.h +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cfg.h @@ -10,6 +10,7 @@ #include <linux/types.h> extern const struct mtk_mdp_driver_data mt8183_mdp_driver_data; +extern const struct mtk_mdp_driver_data mt8188_mdp_driver_data; extern const struct mtk_mdp_driver_data mt8195_mdp_driver_data; struct mdp_dev; diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c index c1f3bf98120a..37e7b985d52c 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c @@ -21,6 +21,9 @@ static const struct of_device_id mdp_of_ids[] = { { .compatible = "mediatek,mt8183-mdp3-rdma", .data = &mt8183_mdp_driver_data, }, + { .compatible = "mediatek,mt8188-mdp3-rdma", + .data = &mt8188_mdp_driver_data, + }, { .compatible = "mediatek,mt8195-mdp3-rdma", .data = &mt8195_mdp_driver_data, }, diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.c index c60e4c193b25..fc4e34c29192 100644 --- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.c +++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.c @@ -63,7 +63,8 @@ int mtk_vcodec_mem_alloc(void *priv, struct mtk_vcodec_mem *mem) id = dec_ctx->id; } - mem->va = dma_alloc_coherent(&plat_dev->dev, mem->size, &mem->dma_addr, GFP_KERNEL); + mem->va = dma_alloc_attrs(&plat_dev->dev, mem->size, &mem->dma_addr, + GFP_KERNEL, DMA_ATTR_ALLOC_SINGLE_PAGES); if (!mem->va) { mtk_v4l2_err(plat_dev, "%s dma_alloc size=0x%zx failed!", __func__, mem->size); diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c index 9107707de6c4..98838217b97d 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c @@ -80,21 +80,18 @@ static struct mtk_q_data *mtk_vdec_get_q_data(struct mtk_vcodec_dec_ctx *ctx, return &ctx->q_data[MTK_Q_DATA_DST]; } -static int vidioc_try_decoder_cmd(struct file *file, void *priv, - struct v4l2_decoder_cmd *cmd) +static int stateful_try_decoder_cmd(struct file *file, void *priv, struct v4l2_decoder_cmd *cmd) { return v4l2_m2m_ioctl_try_decoder_cmd(file, priv, cmd); } - -static int vidioc_decoder_cmd(struct file *file, void *priv, - struct v4l2_decoder_cmd *cmd) +static int stateful_decoder_cmd(struct file *file, void *priv, struct v4l2_decoder_cmd *cmd) { struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(priv); struct vb2_queue *src_vq, *dst_vq; int ret; - ret = vidioc_try_decoder_cmd(file, priv, cmd); + ret = stateful_try_decoder_cmd(file, priv, cmd); if (ret) return ret; @@ -128,6 +125,57 @@ static int vidioc_decoder_cmd(struct file *file, void *priv, return 0; } +static int stateless_try_decoder_cmd(struct file *file, void *priv, struct v4l2_decoder_cmd *cmd) +{ + return v4l2_m2m_ioctl_stateless_try_decoder_cmd(file, priv, cmd); +} + +static int stateless_decoder_cmd(struct file *file, void *priv, struct v4l2_decoder_cmd *cmd) +{ + struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(priv); + int ret; + + ret = v4l2_m2m_ioctl_stateless_try_decoder_cmd(file, priv, cmd); + if (ret) + return ret; + + mtk_v4l2_vdec_dbg(3, ctx, "decoder cmd=%u", cmd->cmd); + switch (cmd->cmd) { + case V4L2_DEC_CMD_FLUSH: + /* + * If the flag of the output buffer is equals V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF, + * this command will prevent dequeueing the capture buffer containing the last + * decoded frame. Or do nothing + */ + break; + default: + mtk_v4l2_vdec_err(ctx, "invalid stateless decoder cmd=%u", cmd->cmd); + return -EINVAL; + } + + return 0; +} + +static int vidioc_try_decoder_cmd(struct file *file, void *priv, struct v4l2_decoder_cmd *cmd) +{ + struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(priv); + + if (ctx->dev->vdec_pdata->uses_stateless_api) + return stateless_try_decoder_cmd(file, priv, cmd); + + return stateful_try_decoder_cmd(file, priv, cmd); +} + +static int vidioc_decoder_cmd(struct file *file, void *priv, struct v4l2_decoder_cmd *cmd) +{ + struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(priv); + + if (ctx->dev->vdec_pdata->uses_stateless_api) + return stateless_decoder_cmd(file, priv, cmd); + + return stateful_decoder_cmd(file, priv, cmd); +} + void mtk_vdec_unlock(struct mtk_vcodec_dec_ctx *ctx) { mutex_unlock(&ctx->dev->dec_mutex[ctx->hw_id]); diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_if.c index 4bc89c8644fe..5f848691cea4 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_if.c @@ -449,7 +449,7 @@ static int vdec_vp8_decode(void *h_vdec, struct mtk_vcodec_mem *bs, inst->frm_cnt, y_fb_dma, c_fb_dma, fb); inst->cur_fb = fb; - dec->bs_dma = (uint64_t)bs->dma_addr; + dec->bs_dma = bs->dma_addr; dec->bs_sz = bs->size; dec->cur_y_fb_dma = y_fb_dma; dec->cur_c_fb_dma = c_fb_dma; diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c index da6be556727b..145958206e38 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c @@ -233,6 +233,12 @@ int vpu_dec_init(struct vdec_vpu_inst *vpu) mtk_vdec_debug(vpu->ctx, "vdec_inst=%p", vpu); err = vcodec_vpu_send_msg(vpu, (void *)&msg, sizeof(msg)); + + if (IS_ERR_OR_NULL(vpu->vsi)) { + mtk_vdec_err(vpu->ctx, "invalid vdec vsi, status=%d", err); + return -EINVAL; + } + mtk_vdec_debug(vpu->ctx, "- ret=%d", err); return err; } diff --git a/drivers/media/platform/nvidia/tegra-vde/h264.c b/drivers/media/platform/nvidia/tegra-vde/h264.c index cfea5572a1b8..d8812fc06c67 100644 --- a/drivers/media/platform/nvidia/tegra-vde/h264.c +++ b/drivers/media/platform/nvidia/tegra-vde/h264.c @@ -19,11 +19,6 @@ #define FLAG_B_FRAME 0x1 #define FLAG_REFERENCE 0x2 -struct tegra_vde_h264_frame { - unsigned int frame_num; - unsigned int flags; -}; - struct tegra_vde_h264_decoder_ctx { unsigned int dpb_frames_nb; unsigned int dpb_ref_frames_with_earlier_poc_nb; diff --git a/drivers/media/platform/nvidia/tegra-vde/vde.h b/drivers/media/platform/nvidia/tegra-vde/vde.h index 0fbb1f3d2c88..b2890484b7c3 100644 --- a/drivers/media/platform/nvidia/tegra-vde/vde.h +++ b/drivers/media/platform/nvidia/tegra-vde/vde.h @@ -47,7 +47,6 @@ struct iommu_group; struct iommu_domain; struct reset_control; struct dma_buf_attachment; -struct tegra_vde_h264_frame; struct tegra_vde_h264_decoder_ctx; struct tegra_video_frame { diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c index cc97790ed30f..1d8913813037 100644 --- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c @@ -1634,6 +1634,9 @@ static int mxc_jpeg_start_streaming(struct vb2_queue *q, unsigned int count) dev_dbg(ctx->mxc_jpeg->dev, "Start streaming ctx=%p", ctx); q_data->sequence = 0; + if (V4L2_TYPE_IS_CAPTURE(q->type)) + ctx->need_initial_source_change_evt = false; + ret = pm_runtime_resume_and_get(ctx->mxc_jpeg->dev); if (ret < 0) { dev_err(ctx->mxc_jpeg->dev, "Failed to power up jpeg\n"); @@ -1840,17 +1843,6 @@ static int mxc_jpeg_parse(struct mxc_jpeg_ctx *ctx, struct vb2_buffer *vb) q_data_out = mxc_jpeg_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - if (q_data_out->w == 0 && q_data_out->h == 0) { - dev_warn(dev, "Invalid user resolution 0x0"); - dev_warn(dev, "Keeping resolution from JPEG: %dx%d", - header.frame.width, header.frame.height); - } else if (header.frame.width != q_data_out->w || - header.frame.height != q_data_out->h) { - dev_err(dev, - "Resolution mismatch: %dx%d (JPEG) versus %dx%d(user)", - header.frame.width, header.frame.height, - q_data_out->w, q_data_out->h); - } q_data_out->w = header.frame.width; q_data_out->h = header.frame.height; if (header.frame.width > MXC_JPEG_MAX_WIDTH || diff --git a/drivers/media/platform/nxp/imx-mipi-csis.c b/drivers/media/platform/nxp/imx-mipi-csis.c index f49b06978f14..b9729a8883d6 100644 --- a/drivers/media/platform/nxp/imx-mipi-csis.c +++ b/drivers/media/platform/nxp/imx-mipi-csis.c @@ -320,7 +320,11 @@ struct mipi_csis_device { struct v4l2_subdev sd; struct media_pad pads[CSIS_PADS_NUM]; struct v4l2_async_notifier notifier; - struct v4l2_subdev *src_sd; + + struct { + struct v4l2_subdev *sd; + const struct media_pad *pad; + } source; struct v4l2_mbus_config_mipi_csi2 bus; u32 clk_frequency; @@ -597,7 +601,7 @@ static int mipi_csis_calculate_params(struct mipi_csis_device *csis, u32 lane_rate; /* Calculate the line rate from the pixel rate. */ - link_freq = v4l2_get_link_freq(csis->src_sd->ctrl_handler, + link_freq = v4l2_get_link_freq(csis->source.sd->ctrl_handler, csis_fmt->width, csis->bus.num_data_lanes * 2); if (link_freq < 0) { @@ -958,7 +962,8 @@ static int mipi_csis_s_stream(struct v4l2_subdev *sd, int enable) int ret; if (!enable) { - v4l2_subdev_call(csis->src_sd, video, s_stream, 0); + v4l2_subdev_disable_streams(csis->source.sd, + csis->source.pad->index, BIT(0)); mipi_csis_stop_stream(csis); if (csis->debug.enable) @@ -986,7 +991,8 @@ static int mipi_csis_s_stream(struct v4l2_subdev *sd, int enable) mipi_csis_start_stream(csis, format, csis_fmt); - ret = v4l2_subdev_call(csis->src_sd, video, s_stream, 1); + ret = v4l2_subdev_enable_streams(csis->source.sd, + csis->source.pad->index, BIT(0)); if (ret < 0) goto err_stop; @@ -1233,12 +1239,14 @@ static int mipi_csis_link_setup(struct media_entity *entity, remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); if (flags & MEDIA_LNK_FL_ENABLED) { - if (csis->src_sd) + if (csis->source.sd) return -EBUSY; - csis->src_sd = remote_sd; + csis->source.sd = remote_sd; + csis->source.pad = remote_pad; } else { - csis->src_sd = NULL; + csis->source.sd = NULL; + csis->source.pad = NULL; } return 0; diff --git a/drivers/media/platform/nxp/imx-pxp.c b/drivers/media/platform/nxp/imx-pxp.c index e62dc5c1a4ae..e4427e6487fb 100644 --- a/drivers/media/platform/nxp/imx-pxp.c +++ b/drivers/media/platform/nxp/imx-pxp.c @@ -1805,6 +1805,9 @@ static int pxp_probe(struct platform_device *pdev) return PTR_ERR(mmio); dev->regmap = devm_regmap_init_mmio(&pdev->dev, mmio, &pxp_regmap_config); + if (IS_ERR(dev->regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(dev->regmap), + "Failed to init regmap\n"); irq = platform_get_irq(pdev, 0); if (irq < 0) diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile index 0d4389ab312d..e636968a1126 100644 --- a/drivers/media/platform/qcom/camss/Makefile +++ b/drivers/media/platform/qcom/camss/Makefile @@ -19,5 +19,6 @@ qcom-camss-objs += \ camss-vfe-gen1.o \ camss-vfe.o \ camss-video.o \ + camss-format.o \ obj-$(CONFIG_VIDEO_QCOM_CAMSS) += qcom-camss.o diff --git a/drivers/media/platform/qcom/camss/camss-csid-4-1.c b/drivers/media/platform/qcom/camss/camss-csid-4-1.c index dd49a40e6a70..c95861420502 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-4-1.c +++ b/drivers/media/platform/qcom/camss/camss-csid-4-1.c @@ -45,128 +45,6 @@ #define CAMSS_CSID_TG_DT_n_CGG_1(n) (0x0b0 + 0xc * (n)) #define CAMSS_CSID_TG_DT_n_CGG_2(n) (0x0b4 + 0xc * (n)) -static const struct csid_format csid_formats[] = { - { - MEDIA_BUS_FMT_UYVY8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_VYUY8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_YUYV8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_YVYU8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_SBGGR8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SBGGR10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SBGGR12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_Y10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, -}; - static void csid_configure_stream(struct csid_device *csid, u8 enable) { struct csid_testgen_config *tg = &csid->testgen; @@ -174,7 +52,7 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) if (enable) { struct v4l2_mbus_framefmt *input_format; - const struct csid_format *format; + const struct csid_format_info *format; u8 vc = 0; /* Virtual Channel 0 */ u8 cid = vc * 4; /* id of Virtual Channel and Data Type set */ u8 dt_shift; @@ -184,7 +62,8 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) u32 num_lines, num_bytes_per_line; input_format = &csid->fmt[MSM_CSID_PAD_SRC]; - format = csid_get_fmt_entry(csid->formats, csid->nformats, + format = csid_get_fmt_entry(csid->res->formats->formats, + csid->res->formats->nformats, input_format->code); num_bytes_per_line = input_format->width * format->bpp * format->spp / 8; num_lines = input_format->height; @@ -211,7 +90,8 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) struct csid_phy_config *phy = &csid->phy; input_format = &csid->fmt[MSM_CSID_PAD_SINK]; - format = csid_get_fmt_entry(csid->formats, csid->nformats, + format = csid_get_fmt_entry(csid->res->formats->formats, + csid->res->formats->nformats, input_format->code); val = phy->lane_cnt - 1; @@ -311,8 +191,6 @@ static u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code, static void csid_subdev_init(struct csid_device *csid) { - csid->formats = csid_formats; - csid->nformats = ARRAY_SIZE(csid_formats); csid->testgen.modes = csid_testgen_modes; csid->testgen.nmodes = CSID_PAYLOAD_MODE_NUM_SUPPORTED_GEN1; } diff --git a/drivers/media/platform/qcom/camss/camss-csid-4-7.c b/drivers/media/platform/qcom/camss/camss-csid-4-7.c index 6b26e036294e..08578a143688 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-4-7.c +++ b/drivers/media/platform/qcom/camss/camss-csid-4-7.c @@ -44,156 +44,6 @@ #define CAMSS_CSID_TG_DT_n_CGG_1(n) (0x0b8 + 0xc * (n)) #define CAMSS_CSID_TG_DT_n_CGG_2(n) (0x0bc + 0xc * (n)) -static const struct csid_format csid_formats[] = { - { - MEDIA_BUS_FMT_UYVY8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_VYUY8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_YUYV8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_YVYU8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_SBGGR8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SBGGR10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SBGGR12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SBGGR14_1X14, - DATA_TYPE_RAW_14BIT, - DECODE_FORMAT_UNCOMPRESSED_14_BIT, - 14, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG14_1X14, - DATA_TYPE_RAW_14BIT, - DECODE_FORMAT_UNCOMPRESSED_14_BIT, - 14, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG14_1X14, - DATA_TYPE_RAW_14BIT, - DECODE_FORMAT_UNCOMPRESSED_14_BIT, - 14, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB14_1X14, - DATA_TYPE_RAW_14BIT, - DECODE_FORMAT_UNCOMPRESSED_14_BIT, - 14, - 1, - }, - { - MEDIA_BUS_FMT_Y10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, -}; - static void csid_configure_stream(struct csid_device *csid, u8 enable) { struct csid_testgen_config *tg = &csid->testgen; @@ -203,7 +53,7 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) if (enable) { struct v4l2_mbus_framefmt *input_format; - const struct csid_format *format; + const struct csid_format_info *format; u8 vc = 0; /* Virtual Channel 0 */ u8 cid = vc * 4; /* id of Virtual Channel and Data Type set */ u8 dt_shift; @@ -213,7 +63,8 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) u32 num_bytes_per_line, num_lines; input_format = &csid->fmt[MSM_CSID_PAD_SRC]; - format = csid_get_fmt_entry(csid->formats, csid->nformats, + format = csid_get_fmt_entry(csid->res->formats->formats, + csid->res->formats->nformats, input_format->code); num_bytes_per_line = input_format->width * format->bpp * format->spp / 8; num_lines = input_format->height; @@ -240,7 +91,8 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) struct csid_phy_config *phy = &csid->phy; input_format = &csid->fmt[MSM_CSID_PAD_SINK]; - format = csid_get_fmt_entry(csid->formats, csid->nformats, + format = csid_get_fmt_entry(csid->res->formats->formats, + csid->res->formats->nformats, input_format->code); val = phy->lane_cnt - 1; @@ -387,8 +239,6 @@ static u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code, static void csid_subdev_init(struct csid_device *csid) { - csid->formats = csid_formats; - csid->nformats = ARRAY_SIZE(csid_formats); csid->testgen.modes = csid_testgen_modes; csid->testgen.nmodes = CSID_PAYLOAD_MODE_NUM_SUPPORTED_GEN1; } diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen2.c b/drivers/media/platform/qcom/camss/camss-csid-gen2.c index b11de4797cca..e1c757933e27 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-gen2.c +++ b/drivers/media/platform/qcom/camss/camss-csid-gen2.c @@ -176,306 +176,171 @@ #define TPG_COLOR_BOX_CFG_MODE 0 #define TPG_COLOR_BOX_PATTERN_SEL 2 -static const struct csid_format csid_formats[] = { - { - MEDIA_BUS_FMT_UYVY8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_VYUY8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_YUYV8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_YVYU8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_SBGGR8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SBGGR10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_Y8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_Y10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SBGGR12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SBGGR14_1X14, - DATA_TYPE_RAW_14BIT, - DECODE_FORMAT_UNCOMPRESSED_14_BIT, - 14, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG14_1X14, - DATA_TYPE_RAW_14BIT, - DECODE_FORMAT_UNCOMPRESSED_14_BIT, - 14, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG14_1X14, - DATA_TYPE_RAW_14BIT, - DECODE_FORMAT_UNCOMPRESSED_14_BIT, - 14, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB14_1X14, - DATA_TYPE_RAW_14BIT, - DECODE_FORMAT_UNCOMPRESSED_14_BIT, - 14, - 1, - }, -}; - -static void __csid_configure_stream(struct csid_device *csid, u8 enable, u8 vc) +static void __csid_configure_rx(struct csid_device *csid, + struct csid_phy_config *phy, int vc) { - struct csid_testgen_config *tg = &csid->testgen; - u32 val; - u32 phy_sel = 0; u8 lane_cnt = csid->phy.lane_cnt; - /* Source pads matching RDI channels on hardware. Pad 1 -> RDI0, Pad 2 -> RDI1, etc. */ - struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc]; - const struct csid_format *format = csid_get_fmt_entry(csid->formats, csid->nformats, - input_format->code); + int val; if (!lane_cnt) lane_cnt = 4; - if (!tg->enabled) - phy_sel = csid->phy.csiphy_id; - - if (enable) { - /* - * DT_ID is a two bit bitfield that is concatenated with - * the four least significant bits of the five bit VC - * bitfield to generate an internal CID value. - * - * CSID_RDI_CFG0(vc) - * DT_ID : 28:27 - * VC : 26:22 - * DT : 21:16 - * - * CID : VC 3:0 << 2 | DT_ID 1:0 - */ - u8 dt_id = vc & 0x03; - - if (tg->enabled) { - /* configure one DT, infinite frames */ - val = vc << TPG_VC_CFG0_VC_NUM; - val |= INTELEAVING_MODE_ONE_SHOT << TPG_VC_CFG0_LINE_INTERLEAVING_MODE; - val |= 0 << TPG_VC_CFG0_NUM_FRAMES; - writel_relaxed(val, csid->base + CSID_TPG_VC_CFG0); - - val = 0x740 << TPG_VC_CFG1_H_BLANKING_COUNT; - val |= 0x3ff << TPG_VC_CFG1_V_BLANKING_COUNT; - writel_relaxed(val, csid->base + CSID_TPG_VC_CFG1); - - writel_relaxed(0x12345678, csid->base + CSID_TPG_LFSR_SEED); - - val = (input_format->height & 0x1fff) << TPG_DT_n_CFG_0_FRAME_HEIGHT; - val |= (input_format->width & 0x1fff) << TPG_DT_n_CFG_0_FRAME_WIDTH; - writel_relaxed(val, csid->base + CSID_TPG_DT_n_CFG_0(0)); - - val = format->data_type << TPG_DT_n_CFG_1_DATA_TYPE; - writel_relaxed(val, csid->base + CSID_TPG_DT_n_CFG_1(0)); - - val = (tg->mode - 1) << TPG_DT_n_CFG_2_PAYLOAD_MODE; - val |= 0xBE << TPG_DT_n_CFG_2_USER_SPECIFIED_PAYLOAD; - val |= format->decode_format << TPG_DT_n_CFG_2_ENCODE_FORMAT; - writel_relaxed(val, csid->base + CSID_TPG_DT_n_CFG_2(0)); - - writel_relaxed(0, csid->base + CSID_TPG_COLOR_BARS_CFG); - - writel_relaxed(0, csid->base + CSID_TPG_COLOR_BOX_CFG); - } + val = (lane_cnt - 1) << CSI2_RX_CFG0_NUM_ACTIVE_LANES; + val |= phy->lane_assign << CSI2_RX_CFG0_DL0_INPUT_SEL; + val |= phy->csiphy_id << CSI2_RX_CFG0_PHY_NUM_SEL; + writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG0); - val = 1 << RDI_CFG0_BYTE_CNTR_EN; - val |= 1 << RDI_CFG0_FORMAT_MEASURE_EN; - val |= 1 << RDI_CFG0_TIMESTAMP_EN; - /* note: for non-RDI path, this should be format->decode_format */ - val |= DECODE_FORMAT_PAYLOAD_ONLY << RDI_CFG0_DECODE_FORMAT; - val |= format->data_type << RDI_CFG0_DATA_TYPE; - val |= vc << RDI_CFG0_VIRTUAL_CHANNEL; - val |= dt_id << RDI_CFG0_DT_ID; - writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc)); + val = 1 << CSI2_RX_CFG1_PACKET_ECC_CORRECTION_EN; + if (vc > 3) + val |= 1 << CSI2_RX_CFG1_VC_MODE; + val |= 1 << CSI2_RX_CFG1_MISR_EN; + writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG1); +} - /* CSID_TIMESTAMP_STB_POST_IRQ */ - val = 2 << RDI_CFG1_TIMESTAMP_STB_SEL; - writel_relaxed(val, csid->base + CSID_RDI_CFG1(vc)); +static void __csid_ctrl_rdi(struct csid_device *csid, int enable, u8 rdi) +{ + int val; - val = 1; - writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(vc)); + if (enable) + val = HALT_CMD_RESUME_AT_FRAME_BOUNDARY << RDI_CTRL_HALT_CMD; + else + val = HALT_CMD_HALT_AT_FRAME_BOUNDARY << RDI_CTRL_HALT_CMD; + writel_relaxed(val, csid->base + CSID_RDI_CTRL(rdi)); +} - val = 0; - writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PATTERN(vc)); +static void __csid_configure_testgen(struct csid_device *csid, u8 enable, u8 vc) +{ + struct csid_testgen_config *tg = &csid->testgen; + struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc]; + const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, + csid->res->formats->nformats, + input_format->code); + u8 lane_cnt = csid->phy.lane_cnt; + u32 val; - val = 1; - writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc)); + if (!lane_cnt) + lane_cnt = 4; - val = 0; - writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc)); + /* configure one DT, infinite frames */ + val = vc << TPG_VC_CFG0_VC_NUM; + val |= INTELEAVING_MODE_ONE_SHOT << TPG_VC_CFG0_LINE_INTERLEAVING_MODE; + val |= 0 << TPG_VC_CFG0_NUM_FRAMES; + writel_relaxed(val, csid->base + CSID_TPG_VC_CFG0); - val = 1; - writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PERIOD(vc)); + val = 0x740 << TPG_VC_CFG1_H_BLANKING_COUNT; + val |= 0x3ff << TPG_VC_CFG1_V_BLANKING_COUNT; + writel_relaxed(val, csid->base + CSID_TPG_VC_CFG1); - val = 0; - writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PATTERN(vc)); + writel_relaxed(0x12345678, csid->base + CSID_TPG_LFSR_SEED); - val = 1; - writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PERIOD(vc)); + val = (input_format->height & 0x1fff) << TPG_DT_n_CFG_0_FRAME_HEIGHT; + val |= (input_format->width & 0x1fff) << TPG_DT_n_CFG_0_FRAME_WIDTH; + writel_relaxed(val, csid->base + CSID_TPG_DT_n_CFG_0(0)); - val = 0; - writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PATTERN(vc)); + val = format->data_type << TPG_DT_n_CFG_1_DATA_TYPE; + writel_relaxed(val, csid->base + CSID_TPG_DT_n_CFG_1(0)); - val = 0; - writel_relaxed(val, csid->base + CSID_RDI_CTRL(vc)); + val = (tg->mode - 1) << TPG_DT_n_CFG_2_PAYLOAD_MODE; + val |= 0xBE << TPG_DT_n_CFG_2_USER_SPECIFIED_PAYLOAD; + val |= format->decode_format << TPG_DT_n_CFG_2_ENCODE_FORMAT; + writel_relaxed(val, csid->base + CSID_TPG_DT_n_CFG_2(0)); - val = readl_relaxed(csid->base + CSID_RDI_CFG0(vc)); - val |= 1 << RDI_CFG0_ENABLE; - writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc)); - } + writel_relaxed(0, csid->base + CSID_TPG_COLOR_BARS_CFG); - if (tg->enabled) { - val = enable << TPG_CTRL_TEST_EN; - val |= 1 << TPG_CTRL_FS_PKT_EN; - val |= 1 << TPG_CTRL_FE_PKT_EN; - val |= (lane_cnt - 1) << TPG_CTRL_NUM_ACTIVE_LANES; - val |= 0x64 << TPG_CTRL_CYCLES_BETWEEN_PKTS; - val |= 0xA << TPG_CTRL_NUM_TRAIL_BYTES; - writel_relaxed(val, csid->base + CSID_TPG_CTRL); - } + writel_relaxed(0, csid->base + CSID_TPG_COLOR_BOX_CFG); - val = (lane_cnt - 1) << CSI2_RX_CFG0_NUM_ACTIVE_LANES; - val |= csid->phy.lane_assign << CSI2_RX_CFG0_DL0_INPUT_SEL; - val |= phy_sel << CSI2_RX_CFG0_PHY_NUM_SEL; - writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG0); + val = enable << TPG_CTRL_TEST_EN; + val |= 1 << TPG_CTRL_FS_PKT_EN; + val |= 1 << TPG_CTRL_FE_PKT_EN; + val |= (lane_cnt - 1) << TPG_CTRL_NUM_ACTIVE_LANES; + val |= 0x64 << TPG_CTRL_CYCLES_BETWEEN_PKTS; + val |= 0xA << TPG_CTRL_NUM_TRAIL_BYTES; + writel_relaxed(val, csid->base + CSID_TPG_CTRL); +} - val = 1 << CSI2_RX_CFG1_PACKET_ECC_CORRECTION_EN; - if (vc > 3) - val |= 1 << CSI2_RX_CFG1_VC_MODE; - val |= 1 << CSI2_RX_CFG1_MISR_EN; - writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG1); +static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc) +{ + /* Source pads matching RDI channels on hardware. Pad 1 -> RDI0, Pad 2 -> RDI1, etc. */ + struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc]; + const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, + csid->res->formats->nformats, + input_format->code); + u32 val; - if (enable) - val = HALT_CMD_RESUME_AT_FRAME_BOUNDARY << RDI_CTRL_HALT_CMD; - else - val = HALT_CMD_HALT_AT_FRAME_BOUNDARY << RDI_CTRL_HALT_CMD; + /* + * DT_ID is a two bit bitfield that is concatenated with + * the four least significant bits of the five bit VC + * bitfield to generate an internal CID value. + * + * CSID_RDI_CFG0(vc) + * DT_ID : 28:27 + * VC : 26:22 + * DT : 21:16 + * + * CID : VC 3:0 << 2 | DT_ID 1:0 + */ + u8 dt_id = vc & 0x03; + + val = 1 << RDI_CFG0_BYTE_CNTR_EN; + val |= 1 << RDI_CFG0_FORMAT_MEASURE_EN; + val |= 1 << RDI_CFG0_TIMESTAMP_EN; + /* note: for non-RDI path, this should be format->decode_format */ + val |= DECODE_FORMAT_PAYLOAD_ONLY << RDI_CFG0_DECODE_FORMAT; + val |= format->data_type << RDI_CFG0_DATA_TYPE; + val |= vc << RDI_CFG0_VIRTUAL_CHANNEL; + val |= dt_id << RDI_CFG0_DT_ID; + writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc)); + + /* CSID_TIMESTAMP_STB_POST_IRQ */ + val = 2 << RDI_CFG1_TIMESTAMP_STB_SEL; + writel_relaxed(val, csid->base + CSID_RDI_CFG1(vc)); + + val = 1; + writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(vc)); + + val = 0; + writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PATTERN(vc)); + + val = 1; + writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc)); + + val = 0; + writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc)); + + val = 1; + writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PERIOD(vc)); + + val = 0; + writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PATTERN(vc)); + + val = 1; + writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PERIOD(vc)); + + val = 0; + writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PATTERN(vc)); + + val = 0; writel_relaxed(val, csid->base + CSID_RDI_CTRL(vc)); + + val = readl_relaxed(csid->base + CSID_RDI_CFG0(vc)); + val |= enable << RDI_CFG0_ENABLE; + writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc)); } static void csid_configure_stream(struct csid_device *csid, u8 enable) { + struct csid_testgen_config *tg = &csid->testgen; u8 i; /* Loop through all enabled VCs and configure stream for each */ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) - if (csid->phy.en_vc & BIT(i)) - __csid_configure_stream(csid, enable, i); + if (csid->phy.en_vc & BIT(i)) { + if (tg->enabled) + __csid_configure_testgen(csid, enable, i); + + __csid_configure_rdi_stream(csid, enable, i); + __csid_configure_rx(csid, &csid->phy, i); + __csid_ctrl_rdi(csid, enable, i); + } } static int csid_configure_testgen_pattern(struct csid_device *csid, s32 val) @@ -612,8 +477,6 @@ static u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code, static void csid_subdev_init(struct csid_device *csid) { - csid->formats = csid_formats; - csid->nformats = ARRAY_SIZE(csid_formats); csid->testgen.modes = csid_testgen_modes; csid->testgen.nmodes = CSID_PAYLOAD_MODE_NUM_SUPPORTED_GEN2; } diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index eb27d69e89a1..858db5d4ca75 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -45,6 +45,450 @@ const char * const csid_testgen_modes[] = { NULL }; +static const struct csid_format_info formats_4_1[] = { + { + MEDIA_BUS_FMT_UYVY8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_VYUY8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_YUYV8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_YVYU8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_SBGGR8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SBGGR10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SBGGR12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_Y10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, +}; + +static const struct csid_format_info formats_4_7[] = { + { + MEDIA_BUS_FMT_UYVY8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_VYUY8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_YUYV8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_YVYU8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_SBGGR8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SBGGR10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SBGGR12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SBGGR14_1X14, + DATA_TYPE_RAW_14BIT, + DECODE_FORMAT_UNCOMPRESSED_14_BIT, + 14, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG14_1X14, + DATA_TYPE_RAW_14BIT, + DECODE_FORMAT_UNCOMPRESSED_14_BIT, + 14, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG14_1X14, + DATA_TYPE_RAW_14BIT, + DECODE_FORMAT_UNCOMPRESSED_14_BIT, + 14, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB14_1X14, + DATA_TYPE_RAW_14BIT, + DECODE_FORMAT_UNCOMPRESSED_14_BIT, + 14, + 1, + }, + { + MEDIA_BUS_FMT_Y10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, +}; + +static const struct csid_format_info formats_gen2[] = { + { + MEDIA_BUS_FMT_UYVY8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_VYUY8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_YUYV8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_YVYU8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_SBGGR8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SBGGR10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_Y8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_Y10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SBGGR12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SBGGR14_1X14, + DATA_TYPE_RAW_14BIT, + DECODE_FORMAT_UNCOMPRESSED_14_BIT, + 14, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG14_1X14, + DATA_TYPE_RAW_14BIT, + DECODE_FORMAT_UNCOMPRESSED_14_BIT, + 14, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG14_1X14, + DATA_TYPE_RAW_14BIT, + DECODE_FORMAT_UNCOMPRESSED_14_BIT, + 14, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB14_1X14, + DATA_TYPE_RAW_14BIT, + DECODE_FORMAT_UNCOMPRESSED_14_BIT, + 14, + 1, + }, +}; + +const struct csid_formats csid_formats_4_1 = { + .nformats = ARRAY_SIZE(formats_4_1), + .formats = formats_4_1 +}; + +const struct csid_formats csid_formats_4_7 = { + .nformats = ARRAY_SIZE(formats_4_7), + .formats = formats_4_7 +}; + +const struct csid_formats csid_formats_gen2 = { + .nformats = ARRAY_SIZE(formats_gen2), + .formats = formats_gen2 +}; + u32 csid_find_code(u32 *codes, unsigned int ncodes, unsigned int match_format_idx, u32 match_code) { @@ -65,9 +509,9 @@ u32 csid_find_code(u32 *codes, unsigned int ncodes, return codes[0]; } -const struct csid_format *csid_get_fmt_entry(const struct csid_format *formats, - unsigned int nformats, - u32 code) +const struct csid_format_info *csid_get_fmt_entry(const struct csid_format_info *formats, + unsigned int nformats, + u32 code) { unsigned int i; @@ -87,12 +531,12 @@ const struct csid_format *csid_get_fmt_entry(const struct csid_format *formats, static int csid_set_clock_rates(struct csid_device *csid) { struct device *dev = csid->camss->dev; - const struct csid_format *fmt; + const struct csid_format_info *fmt; s64 link_freq; int i, j; int ret; - fmt = csid_get_fmt_entry(csid->formats, csid->nformats, + fmt = csid_get_fmt_entry(csid->res->formats->formats, csid->res->formats->nformats, csid->fmt[MSM_CSIPHY_PAD_SINK].code); link_freq = camss_get_link_freq(&csid->subdev.entity, fmt->bpp, csid->phy.lane_cnt); @@ -158,7 +602,6 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) struct csid_device *csid = v4l2_get_subdevdata(sd); struct camss *camss = csid->camss; struct device *dev = camss->dev; - struct vfe_device *vfe = &camss->vfe[csid->id]; int ret = 0; if (on) { @@ -167,7 +610,7 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) * switching on the CSID. Do so unconditionally, as there is no * drawback in following the same powering order on older SoCs. */ - ret = vfe_get(vfe); + ret = csid->res->parent_dev_ops->get(camss, csid->id); if (ret < 0) return ret; @@ -202,7 +645,7 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) enable_irq(csid->irq); - ret = csid->ops->reset(csid); + ret = csid->res->hw_ops->reset(csid); if (ret < 0) { disable_irq(csid->irq); camss_disable_clocks(csid->nclocks, csid->clock); @@ -212,14 +655,14 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) return ret; } - csid->ops->hw_version(csid); + csid->res->hw_ops->hw_version(csid); } else { disable_irq(csid->irq); camss_disable_clocks(csid->nclocks, csid->clock); regulator_bulk_disable(csid->num_supplies, csid->supplies); pm_runtime_put_sync(dev); - vfe_put(vfe); + csid->res->parent_dev_ops->put(camss, csid->id); } return ret; @@ -253,7 +696,7 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable) } if (csid->phy.need_vc_update) { - csid->ops->configure_stream(csid, enable); + csid->res->hw_ops->configure_stream(csid, enable); csid->phy.need_vc_update = false; } @@ -301,12 +744,12 @@ static void csid_try_format(struct csid_device *csid, case MSM_CSID_PAD_SINK: /* Set format on sink pad */ - for (i = 0; i < csid->nformats; i++) - if (fmt->code == csid->formats[i].code) + for (i = 0; i < csid->res->formats->nformats; i++) + if (fmt->code == csid->res->formats->formats[i].code) break; /* If not found, use UYVY as default */ - if (i >= csid->nformats) + if (i >= csid->res->formats->nformats) fmt->code = MEDIA_BUS_FMT_UYVY8_1X16; fmt->width = clamp_t(u32, fmt->width, 1, 8191); @@ -325,17 +768,17 @@ static void csid_try_format(struct csid_device *csid, *fmt = *__csid_get_format(csid, sd_state, MSM_CSID_PAD_SINK, which); - fmt->code = csid->ops->src_pad_code(csid, fmt->code, 0, code); + fmt->code = csid->res->hw_ops->src_pad_code(csid, fmt->code, 0, code); } else { /* Test generator is enabled, set format on source */ /* pad to allow test generator usage */ - for (i = 0; i < csid->nformats; i++) - if (csid->formats[i].code == fmt->code) + for (i = 0; i < csid->res->formats->nformats; i++) + if (csid->res->formats->formats[i].code == fmt->code) break; /* If not found, use UYVY as default */ - if (i >= csid->nformats) + if (i >= csid->res->formats->nformats) fmt->code = MEDIA_BUS_FMT_UYVY8_1X16; fmt->width = clamp_t(u32, fmt->width, 1, 8191); @@ -363,10 +806,10 @@ static int csid_enum_mbus_code(struct v4l2_subdev *sd, struct csid_device *csid = v4l2_get_subdevdata(sd); if (code->pad == MSM_CSID_PAD_SINK) { - if (code->index >= csid->nformats) + if (code->index >= csid->res->formats->nformats) return -EINVAL; - code->code = csid->formats[code->index].code; + code->code = csid->res->formats->formats[code->index].code; } else { if (csid->testgen_mode->cur.val == 0) { struct v4l2_mbus_framefmt *sink_fmt; @@ -375,15 +818,15 @@ static int csid_enum_mbus_code(struct v4l2_subdev *sd, MSM_CSID_PAD_SINK, code->which); - code->code = csid->ops->src_pad_code(csid, sink_fmt->code, - code->index, 0); + code->code = csid->res->hw_ops->src_pad_code(csid, sink_fmt->code, + code->index, 0); if (!code->code) return -EINVAL; } else { - if (code->index >= csid->nformats) + if (code->index >= csid->res->formats->nformats) return -EINVAL; - code->code = csid->formats[code->index].code; + code->code = csid->res->formats->formats[code->index].code; } } @@ -529,7 +972,7 @@ static int csid_set_test_pattern(struct csid_device *csid, s32 value) tg->enabled = !!value; - return csid->ops->configure_testgen_pattern(csid, value); + return csid->res->hw_ops->configure_testgen_pattern(csid, value); } /* @@ -575,9 +1018,14 @@ int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, csid->camss = camss; csid->id = id; - csid->ops = res->ops; + csid->res = &res->csid; + + if (dev_WARN_ONCE(dev, !csid->res->parent_dev_ops, + "Error: CSID depends on VFE/IFE device ops!\n")) { + return -EINVAL; + } - csid->ops->subdev_init(csid); + csid->res->hw_ops->subdev_init(csid); /* Memory */ @@ -587,9 +1035,11 @@ int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, * VFE to be initialized before CSID */ if (id >= 2) /* VFE/CSID lite */ - csid->base = camss->vfe[id].base + VFE_480_LITE_CSID_OFFSET; + csid->base = csid->res->parent_dev_ops->get_base_address(camss, id) + + VFE_480_LITE_CSID_OFFSET; else - csid->base = camss->vfe[id].base + VFE_480_CSID_OFFSET; + csid->base = csid->res->parent_dev_ops->get_base_address(camss, id) + + VFE_480_CSID_OFFSET; } else { csid->base = devm_platform_ioremap_resource_byname(pdev, res->reg[0]); if (IS_ERR(csid->base)) @@ -605,7 +1055,7 @@ int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, csid->irq = ret; snprintf(csid->irq_name, sizeof(csid->irq_name), "%s_%s%d", dev_name(dev), MSM_CSID_NAME, csid->id); - ret = devm_request_irq(dev, csid->irq, csid->ops->isr, + ret = devm_request_irq(dev, csid->irq, csid->res->hw_ops->isr, IRQF_TRIGGER_RISING | IRQF_NO_AUTOEN, csid->irq_name, csid); if (ret < 0) { @@ -899,5 +1349,5 @@ void msm_csid_unregister_entity(struct csid_device *csid) inline bool csid_is_lite(struct csid_device *csid) { - return csid->camss->res->csid_res[csid->id].is_lite; + return csid->camss->res->csid_res[csid->id].csid.is_lite; } diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h index fddccb69da13..8cdae98e4dca 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.h +++ b/drivers/media/platform/qcom/camss/camss-csid.h @@ -67,7 +67,7 @@ enum csid_testgen_mode { CSID_PAYLOAD_MODE_NUM_SUPPORTED_GEN2 = 9, /* excluding disabled */ }; -struct csid_format { +struct csid_format_info { u32 code; u8 data_type; u8 decode_format; @@ -75,6 +75,11 @@ struct csid_format { u8 spp; /* bus samples per pixel */ }; +struct csid_formats { + unsigned int nformats; + const struct csid_format_info *formats; +}; + struct csid_testgen_config { enum csid_testgen_mode mode; const char * const*modes; @@ -149,6 +154,13 @@ struct csid_hw_ops { void (*subdev_init)(struct csid_device *csid); }; +struct csid_subdev_resources { + bool is_lite; + const struct csid_hw_ops *hw_ops; + const struct parent_dev_ops *parent_dev_ops; + const struct csid_formats *formats; +}; + struct csid_device { struct camss *camss; u8 id; @@ -167,9 +179,7 @@ struct csid_device { struct v4l2_mbus_framefmt fmt[MSM_CSID_PADS_NUM]; struct v4l2_ctrl_handler ctrls; struct v4l2_ctrl *testgen_mode; - const struct csid_format *formats; - unsigned int nformats; - const struct csid_hw_ops *ops; + const struct csid_subdev_resources *res; }; struct camss_subdev_resources; @@ -188,16 +198,16 @@ u32 csid_find_code(u32 *codes, unsigned int ncode, unsigned int match_format_idx, u32 match_code); /* - * csid_get_fmt_entry - Find csid_format entry with matching format code - * @formats: Array of format csid_format entries + * csid_get_fmt_entry - Find csid_format_info entry with matching format code + * @formats: Array of format csid_format_info entries * @nformats: Length of @nformats array * @code: Desired format code * * Return formats[0] on failure to find code */ -const struct csid_format *csid_get_fmt_entry(const struct csid_format *formats, - unsigned int nformats, - u32 code); +const struct csid_format_info *csid_get_fmt_entry(const struct csid_format_info *formats, + unsigned int nformats, + u32 code); int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, const struct camss_subdev_resources *res, u8 id); @@ -211,6 +221,10 @@ void msm_csid_get_csid_id(struct media_entity *entity, u8 *id); extern const char * const csid_testgen_modes[]; +extern const struct csid_formats csid_formats_4_1; +extern const struct csid_formats csid_formats_4_7; +extern const struct csid_formats csid_formats_gen2; + extern const struct csid_hw_ops csid_ops_4_1; extern const struct csid_hw_ops csid_ops_4_7; extern const struct csid_hw_ops csid_ops_gen2; diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index 45b3a8e5dea4..2f7361dfd461 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -24,12 +24,7 @@ #define MSM_CSIPHY_NAME "msm_csiphy" -struct csiphy_format { - u32 code; - u8 bpp; -}; - -static const struct csiphy_format csiphy_formats_8x16[] = { +static const struct csiphy_format_info formats_8x16[] = { { MEDIA_BUS_FMT_UYVY8_1X16, 8 }, { MEDIA_BUS_FMT_VYUY8_1X16, 8 }, { MEDIA_BUS_FMT_YUYV8_1X16, 8 }, @@ -49,7 +44,7 @@ static const struct csiphy_format csiphy_formats_8x16[] = { { MEDIA_BUS_FMT_Y10_1X10, 10 }, }; -static const struct csiphy_format csiphy_formats_8x96[] = { +static const struct csiphy_format_info formats_8x96[] = { { MEDIA_BUS_FMT_UYVY8_1X16, 8 }, { MEDIA_BUS_FMT_VYUY8_1X16, 8 }, { MEDIA_BUS_FMT_YUYV8_1X16, 8 }, @@ -73,7 +68,7 @@ static const struct csiphy_format csiphy_formats_8x96[] = { { MEDIA_BUS_FMT_Y10_1X10, 10 }, }; -static const struct csiphy_format csiphy_formats_sdm845[] = { +static const struct csiphy_format_info formats_sdm845[] = { { MEDIA_BUS_FMT_UYVY8_1X16, 8 }, { MEDIA_BUS_FMT_VYUY8_1X16, 8 }, { MEDIA_BUS_FMT_YUYV8_1X16, 8 }, @@ -98,6 +93,21 @@ static const struct csiphy_format csiphy_formats_sdm845[] = { { MEDIA_BUS_FMT_Y10_1X10, 10 }, }; +const struct csiphy_formats csiphy_formats_8x16 = { + .nformats = ARRAY_SIZE(formats_8x16), + .formats = formats_8x16 +}; + +const struct csiphy_formats csiphy_formats_8x96 = { + .nformats = ARRAY_SIZE(formats_8x96), + .formats = formats_8x96 +}; + +const struct csiphy_formats csiphy_formats_sdm845 = { + .nformats = ARRAY_SIZE(formats_sdm845), + .formats = formats_sdm845 +}; + /* * csiphy_get_bpp - map media bus format to bits per pixel * @formats: supported media bus formats array @@ -106,7 +116,7 @@ static const struct csiphy_format csiphy_formats_sdm845[] = { * * Return number of bits per pixel */ -static u8 csiphy_get_bpp(const struct csiphy_format *formats, +static u8 csiphy_get_bpp(const struct csiphy_format_info *formats, unsigned int nformats, u32 code) { unsigned int i; @@ -131,7 +141,7 @@ static int csiphy_set_clock_rates(struct csiphy_device *csiphy) int i, j; int ret; - u8 bpp = csiphy_get_bpp(csiphy->formats, csiphy->nformats, + u8 bpp = csiphy_get_bpp(csiphy->res->formats->formats, csiphy->res->formats->nformats, csiphy->fmt[MSM_CSIPHY_PAD_SINK].code); u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data; @@ -216,9 +226,9 @@ static int csiphy_set_power(struct v4l2_subdev *sd, int on) enable_irq(csiphy->irq); - csiphy->ops->reset(csiphy); + csiphy->res->hw_ops->reset(csiphy); - csiphy->ops->hw_version_read(csiphy, dev); + csiphy->res->hw_ops->hw_version_read(csiphy, dev); } else { disable_irq(csiphy->irq); @@ -243,8 +253,8 @@ static int csiphy_stream_on(struct csiphy_device *csiphy) { struct csiphy_config *cfg = &csiphy->cfg; s64 link_freq; - u8 lane_mask = csiphy->ops->get_lane_mask(&cfg->csi2->lane_cfg); - u8 bpp = csiphy_get_bpp(csiphy->formats, csiphy->nformats, + u8 lane_mask = csiphy->res->hw_ops->get_lane_mask(&cfg->csi2->lane_cfg); + u8 bpp = csiphy_get_bpp(csiphy->res->formats->formats, csiphy->res->formats->nformats, csiphy->fmt[MSM_CSIPHY_PAD_SINK].code); u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data; u8 val; @@ -272,7 +282,7 @@ static int csiphy_stream_on(struct csiphy_device *csiphy) wmb(); } - csiphy->ops->lanes_enable(csiphy, cfg, link_freq, lane_mask); + csiphy->res->hw_ops->lanes_enable(csiphy, cfg, link_freq, lane_mask); return 0; } @@ -285,7 +295,7 @@ static int csiphy_stream_on(struct csiphy_device *csiphy) */ static void csiphy_stream_off(struct csiphy_device *csiphy) { - csiphy->ops->lanes_disable(csiphy, &csiphy->cfg); + csiphy->res->hw_ops->lanes_disable(csiphy, &csiphy->cfg); } @@ -350,12 +360,12 @@ static void csiphy_try_format(struct csiphy_device *csiphy, case MSM_CSIPHY_PAD_SINK: /* Set format on sink pad */ - for (i = 0; i < csiphy->nformats; i++) - if (fmt->code == csiphy->formats[i].code) + for (i = 0; i < csiphy->res->formats->nformats; i++) + if (fmt->code == csiphy->res->formats->formats[i].code) break; /* If not found, use UYVY as default */ - if (i >= csiphy->nformats) + if (i >= csiphy->res->formats->nformats) fmt->code = MEDIA_BUS_FMT_UYVY8_1X16; fmt->width = clamp_t(u32, fmt->width, 1, 8191); @@ -392,10 +402,10 @@ static int csiphy_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *format; if (code->pad == MSM_CSIPHY_PAD_SINK) { - if (code->index >= csiphy->nformats) + if (code->index >= csiphy->res->formats->nformats) return -EINVAL; - code->code = csiphy->formats[code->index].code; + code->code = csiphy->res->formats->formats[code->index].code; } else { if (code->index > 0) return -EINVAL; @@ -564,25 +574,7 @@ int msm_csiphy_subdev_init(struct camss *camss, csiphy->camss = camss; csiphy->id = id; csiphy->cfg.combo_mode = 0; - csiphy->ops = res->ops; - - switch (camss->res->version) { - case CAMSS_8x16: - csiphy->formats = csiphy_formats_8x16; - csiphy->nformats = ARRAY_SIZE(csiphy_formats_8x16); - break; - case CAMSS_8x96: - case CAMSS_660: - csiphy->formats = csiphy_formats_8x96; - csiphy->nformats = ARRAY_SIZE(csiphy_formats_8x96); - break; - case CAMSS_845: - case CAMSS_8250: - case CAMSS_8280XP: - csiphy->formats = csiphy_formats_sdm845; - csiphy->nformats = ARRAY_SIZE(csiphy_formats_sdm845); - break; - } + csiphy->res = &res->csiphy; /* Memory */ @@ -610,7 +602,7 @@ int msm_csiphy_subdev_init(struct camss *camss, snprintf(csiphy->irq_name, sizeof(csiphy->irq_name), "%s_%s%d", dev_name(dev), MSM_CSIPHY_NAME, csiphy->id); - ret = devm_request_irq(dev, csiphy->irq, csiphy->ops->isr, + ret = devm_request_irq(dev, csiphy->irq, csiphy->res->hw_ops->isr, IRQF_TRIGGER_RISING | IRQF_NO_AUTOEN, csiphy->irq_name, csiphy); if (ret < 0) { diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h index c9b7fe82b1f0..47f0b6b09eba 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.h +++ b/drivers/media/platform/qcom/camss/camss-csiphy.h @@ -42,6 +42,16 @@ struct csiphy_config { struct csiphy_csi2_cfg *csi2; }; +struct csiphy_format_info { + u32 code; + u8 bpp; +}; + +struct csiphy_formats { + unsigned int nformats; + const struct csiphy_format_info *formats; +}; + struct csiphy_device; struct csiphy_hw_ops { @@ -63,6 +73,11 @@ struct csiphy_hw_ops { irqreturn_t (*isr)(int irq, void *dev); }; +struct csiphy_subdev_resources { + const struct csiphy_hw_ops *hw_ops; + const struct csiphy_formats *formats; +}; + struct csiphy_device { struct camss *camss; u8 id; @@ -78,9 +93,7 @@ struct csiphy_device { u32 timer_clk_rate; struct csiphy_config cfg; struct v4l2_mbus_framefmt fmt[MSM_CSIPHY_PADS_NUM]; - const struct csiphy_hw_ops *ops; - const struct csiphy_format *formats; - unsigned int nformats; + const struct csiphy_subdev_resources *res; }; struct camss_subdev_resources; @@ -94,6 +107,10 @@ int msm_csiphy_register_entity(struct csiphy_device *csiphy, void msm_csiphy_unregister_entity(struct csiphy_device *csiphy); +extern const struct csiphy_formats csiphy_formats_8x16; +extern const struct csiphy_formats csiphy_formats_8x96; +extern const struct csiphy_formats csiphy_formats_sdm845; + extern const struct csiphy_hw_ops csiphy_ops_2ph_1_0; extern const struct csiphy_hw_ops csiphy_ops_3ph_1_0; diff --git a/drivers/media/platform/qcom/camss/camss-format.c b/drivers/media/platform/qcom/camss/camss-format.c new file mode 100644 index 000000000000..4a3d5549615c --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-format.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * camss-format.c + * + * Qualcomm MSM Camera Subsystem - Format helpers + * + * Copyright (c) 2023, The Linux Foundation. All rights reserved. + * Copyright (c) 2023 Qualcomm Technologies, Inc. + */ +#include <linux/bug.h> +#include <linux/errno.h> + +#include "camss-format.h" + +/* + * camss_format_get_bpp - Map media bus format to bits per pixel + * @formats: supported media bus formats array + * @nformats: size of @formats array + * @code: media bus format code + * + * Return number of bits per pixel + */ +u8 camss_format_get_bpp(const struct camss_format_info *formats, unsigned int nformats, u32 code) +{ + unsigned int i; + + for (i = 0; i < nformats; i++) + if (code == formats[i].code) + return formats[i].mbus_bpp; + + WARN(1, "Unknown format\n"); + + return formats[0].mbus_bpp; +} + +/* + * camss_format_find_code - Find a format code in an array + * @code: a pointer to media bus format codes array + * @n_code: size of @code array + * @index: index of code in the array + * @req_code: required code + * + * Return media bus format code + */ +u32 camss_format_find_code(u32 *code, unsigned int n_code, unsigned int index, u32 req_code) +{ + unsigned int i; + + if (!req_code && index >= n_code) + return 0; + + for (i = 0; i < n_code; i++) { + if (req_code) { + if (req_code == code[i]) + return req_code; + } else { + if (i == index) + return code[i]; + } + } + + return code[0]; +} + +/* + * camss_format_find_format - Find a format in an array + * @code: media bus format code + * @pixelformat: V4L2 pixel format FCC identifier + * @formats: a pointer to formats array + * @nformats: size of @formats array + * + * Return index of a format or a negative error code otherwise + */ +int camss_format_find_format(u32 code, u32 pixelformat, const struct camss_format_info *formats, + unsigned int nformats) +{ + unsigned int i; + + for (i = 0; i < nformats; i++) { + if (formats[i].code == code && + formats[i].pixelformat == pixelformat) + return i; + } + + for (i = 0; i < nformats; i++) { + if (formats[i].code == code) + return i; + } + + return -EINVAL; +} diff --git a/drivers/media/platform/qcom/camss/camss-format.h b/drivers/media/platform/qcom/camss/camss-format.h new file mode 100644 index 000000000000..923a48c9c3fb --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-format.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * camss-format.h + * + * Qualcomm MSM Camera Subsystem - Format helpers + * + * Copyright (c) 2023, The Linux Foundation. All rights reserved. + * Copyright (c) 2023 Qualcomm Technologies, Inc. + */ +#ifndef __CAMSS_FORMAT_H__ +#define __CAMSS_FORMAT_H__ + +#include <linux/types.h> + +#define PER_PLANE_DATA(plane, h_fract_num, h_fract_den, v_fract_num, v_fract_den, _bpp) \ + .hsub[(plane)].numerator = (h_fract_num), \ + .hsub[(plane)].denominator = (h_fract_den), \ + .vsub[(plane)].numerator = (v_fract_num), \ + .vsub[(plane)].denominator = (v_fract_den), \ + .bpp[(plane)] = (_bpp) + +/* + * struct fract - Represents a fraction + * @numerator: Store the numerator part of the fraction + * @denominator: Store the denominator part of the fraction + */ +struct fract { + u8 numerator; + u8 denominator; +}; + +/* + * struct camss_format_info - ISP media bus format information + * @code: V4L2 media bus format code + * @mbus_bpp: Media bus bits per pixel + * @pixelformat: V4L2 pixel format FCC identifier + * @planes: Number of planes + * @hsub: Horizontal subsampling (for each plane) + * @vsub: Vertical subsampling (for each plane) + * @bpp: Bits per pixel when stored in memory (for each plane) + */ +struct camss_format_info { + u32 code; + u32 mbus_bpp; + u32 pixelformat; + u8 planes; + struct fract hsub[3]; + struct fract vsub[3]; + unsigned int bpp[3]; +}; + +struct camss_formats { + unsigned int nformats; + const struct camss_format_info *formats; +}; + +u8 camss_format_get_bpp(const struct camss_format_info *formats, unsigned int nformats, u32 code); +u32 camss_format_find_code(u32 *code, unsigned int n_code, unsigned int index, u32 req_code); +int camss_format_find_format(u32 code, u32 pixelformat, const struct camss_format_info *formats, + unsigned int nformats); + +#endif /* __CAMSS_FORMAT_H__ */ diff --git a/drivers/media/platform/qcom/camss/camss-vfe-17x.c b/drivers/media/platform/qcom/camss/camss-vfe-17x.c index 795ac3815339..380c99321030 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-17x.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-17x.c @@ -353,7 +353,7 @@ static irqreturn_t vfe_isr(int irq, void *dev) writel_relaxed(status0, vfe->base + VFE_IRQ_CLEAR_0); writel_relaxed(status1, vfe->base + VFE_IRQ_CLEAR_1); - for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) { + for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++) { vfe_bus_status[i] = readl_relaxed(vfe->base + VFE_BUS_IRQ_STATUS(i)); writel_relaxed(vfe_bus_status[i], vfe->base + VFE_BUS_IRQ_CLEAR(i)); } @@ -367,11 +367,11 @@ static irqreturn_t vfe_isr(int irq, void *dev) if (status0 & STATUS_0_RESET_ACK) vfe->isr_ops.reset_ack(vfe); - for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) + for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++) if (status0 & STATUS_0_RDI_REG_UPDATE(i)) vfe->isr_ops.reg_update(vfe, i); - for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) + for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++) if (status0 & STATUS_1_RDI_SOF(i)) vfe->isr_ops.sof(vfe, i); @@ -442,7 +442,7 @@ static int vfe_enable_output(struct vfe_line *line) { struct vfe_device *vfe = to_vfe(line); struct vfe_output *output = &line->output; - const struct vfe_hw_ops *ops = vfe->ops; + const struct vfe_hw_ops *ops = vfe->res->hw_ops; struct media_entity *sensor; unsigned long flags; unsigned int frame_skip = 0; @@ -560,7 +560,7 @@ static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id) unsigned long flags; spin_lock_irqsave(&vfe->output_lock, flags); - vfe->ops->reg_update_clear(vfe, line_id); + vfe->res->hw_ops->reg_update_clear(vfe, line_id); output = &vfe->line[line_id].output; diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-1.c b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c index ef6b34c915df..1bd3a6ef1d04 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-4-1.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c @@ -892,7 +892,7 @@ static irqreturn_t vfe_isr(int irq, void *dev) u32 value0, value1; int i, j; - vfe->ops->isr_read(vfe, &value0, &value1); + vfe->res->hw_ops->isr_read(vfe, &value0, &value1); dev_dbg(vfe->camss->dev, "VFE: status0 = 0x%08x, status1 = 0x%08x\n", value0, value1); @@ -901,7 +901,7 @@ static irqreturn_t vfe_isr(int irq, void *dev) vfe->isr_ops.reset_ack(vfe); if (value1 & VFE_0_IRQ_STATUS_1_VIOLATION) - vfe->ops->violation_read(vfe); + vfe->res->hw_ops->violation_read(vfe); if (value1 & VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK) vfe->isr_ops.halt_ack(vfe); diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-7.c b/drivers/media/platform/qcom/camss/camss-vfe-4-7.c index 7655d22a9fda..ce0719106bd3 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-4-7.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-7.c @@ -1050,7 +1050,7 @@ static irqreturn_t vfe_isr(int irq, void *dev) u32 value0, value1; int i, j; - vfe->ops->isr_read(vfe, &value0, &value1); + vfe->res->hw_ops->isr_read(vfe, &value0, &value1); dev_dbg(vfe->camss->dev, "VFE: status0 = 0x%08x, status1 = 0x%08x\n", value0, value1); @@ -1059,12 +1059,12 @@ static irqreturn_t vfe_isr(int irq, void *dev) vfe->isr_ops.reset_ack(vfe); if (value1 & VFE_0_IRQ_STATUS_1_VIOLATION) - vfe->ops->violation_read(vfe); + vfe->res->hw_ops->violation_read(vfe); if (value1 & VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK) vfe->isr_ops.halt_ack(vfe); - for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) + for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++) if (value0 & VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(i)) vfe->isr_ops.reg_update(vfe, i); diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-8.c b/drivers/media/platform/qcom/camss/camss-vfe-4-8.c index f52fa30f3853..6b59c8107a3c 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-4-8.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-8.c @@ -980,7 +980,7 @@ static irqreturn_t vfe_isr(int irq, void *dev) u32 value0, value1; int i, j; - vfe->ops->isr_read(vfe, &value0, &value1); + vfe->res->hw_ops->isr_read(vfe, &value0, &value1); dev_dbg(vfe->camss->dev, "VFE: status0 = 0x%08x, status1 = 0x%08x\n", value0, value1); @@ -989,12 +989,12 @@ static irqreturn_t vfe_isr(int irq, void *dev) vfe->isr_ops.reset_ack(vfe); if (value1 & VFE_0_IRQ_STATUS_1_VIOLATION) - vfe->ops->violation_read(vfe); + vfe->res->hw_ops->violation_read(vfe); if (value1 & VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK) vfe->isr_ops.halt_ack(vfe); - for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) + for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++) if (value0 & VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(i)) vfe->isr_ops.reg_update(vfe, i); diff --git a/drivers/media/platform/qcom/camss/camss-vfe-gen1.c b/drivers/media/platform/qcom/camss/camss-vfe-gen1.c index 239d3d4ac666..eb33c03df27e 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-gen1.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-gen1.c @@ -37,7 +37,7 @@ static int vfe_disable_output(struct vfe_line *line) { struct vfe_device *vfe = to_vfe(line); struct vfe_output *output = &line->output; - const struct vfe_hw_ops *ops = vfe->ops; + const struct vfe_hw_ops *ops = vfe->res->hw_ops; unsigned long flags; unsigned long time; unsigned int i; @@ -162,14 +162,14 @@ static void vfe_output_frame_drop(struct vfe_device *vfe, vfe->ops_gen1->wm_set_framedrop_pattern(vfe, output->wm_idx[i], drop_pattern); } - vfe->ops->reg_update(vfe, container_of(output, struct vfe_line, output)->id); + vfe->res->hw_ops->reg_update(vfe, container_of(output, struct vfe_line, output)->id); } static int vfe_enable_output(struct vfe_line *line) { struct vfe_device *vfe = to_vfe(line); struct vfe_output *output = &line->output; - const struct vfe_hw_ops *ops = vfe->ops; + const struct vfe_hw_ops *ops = vfe->res->hw_ops; struct media_entity *sensor; unsigned long flags; unsigned int frame_skip = 0; @@ -545,7 +545,7 @@ static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id) unsigned long flags; spin_lock_irqsave(&vfe->output_lock, flags); - vfe->ops->reg_update_clear(vfe, line_id); + vfe->res->hw_ops->reg_update_clear(vfe, line_id); output = &line->output; diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index d875237cf244..83c5a36d071f 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -32,139 +32,251 @@ #define SCALER_RATIO_MAX 16 -struct vfe_format { - u32 code; - u8 bpp; +static const struct camss_format_info formats_rdi_8x16[] = { + { MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_UYVY, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_VYUY, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_YUYV, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_YVYU, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_SBGGR8_1X8, 8, V4L2_PIX_FMT_SBGGR8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SGBRG8_1X8, 8, V4L2_PIX_FMT_SGBRG8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SGRBG8_1X8, 8, V4L2_PIX_FMT_SGRBG8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SRGGB8_1X8, 8, V4L2_PIX_FMT_SRGGB8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SBGGR10_1X10, 10, V4L2_PIX_FMT_SBGGR10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SGBRG10_1X10, 10, V4L2_PIX_FMT_SGBRG10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SGRBG10_1X10, 10, V4L2_PIX_FMT_SGRBG10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SRGGB10_1X10, 10, V4L2_PIX_FMT_SRGGB10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SBGGR12_1X12, 12, V4L2_PIX_FMT_SBGGR12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_SGBRG12_1X12, 12, V4L2_PIX_FMT_SGBRG12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_SGRBG12_1X12, 12, V4L2_PIX_FMT_SGRBG12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_SRGGB12_1X12, 12, V4L2_PIX_FMT_SRGGB12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_Y10_1X10, 10, V4L2_PIX_FMT_Y10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, }; -static const struct vfe_format formats_rdi_8x16[] = { - { MEDIA_BUS_FMT_UYVY8_1X16, 8 }, - { MEDIA_BUS_FMT_VYUY8_1X16, 8 }, - { MEDIA_BUS_FMT_YUYV8_1X16, 8 }, - { MEDIA_BUS_FMT_YVYU8_1X16, 8 }, - { MEDIA_BUS_FMT_SBGGR8_1X8, 8 }, - { MEDIA_BUS_FMT_SGBRG8_1X8, 8 }, - { MEDIA_BUS_FMT_SGRBG8_1X8, 8 }, - { MEDIA_BUS_FMT_SRGGB8_1X8, 8 }, - { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, - { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, - { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, - { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, - { MEDIA_BUS_FMT_SBGGR12_1X12, 12 }, - { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, - { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, - { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, - { MEDIA_BUS_FMT_Y10_1X10, 10 }, +static const struct camss_format_info formats_rdi_8x96[] = { + { MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_UYVY, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_VYUY, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_YUYV, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_YVYU, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_SBGGR8_1X8, 8, V4L2_PIX_FMT_SBGGR8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SGBRG8_1X8, 8, V4L2_PIX_FMT_SGBRG8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SGRBG8_1X8, 8, V4L2_PIX_FMT_SGRBG8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SRGGB8_1X8, 8, V4L2_PIX_FMT_SRGGB8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SBGGR10_1X10, 10, V4L2_PIX_FMT_SBGGR10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SGBRG10_1X10, 10, V4L2_PIX_FMT_SGBRG10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SGRBG10_1X10, 10, V4L2_PIX_FMT_SGRBG10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SRGGB10_1X10, 10, V4L2_PIX_FMT_SRGGB10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, 16, V4L2_PIX_FMT_SBGGR10, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_SBGGR12_1X12, 12, V4L2_PIX_FMT_SBGGR12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_SGBRG12_1X12, 12, V4L2_PIX_FMT_SGBRG12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_SGRBG12_1X12, 12, V4L2_PIX_FMT_SGRBG12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_SRGGB12_1X12, 12, V4L2_PIX_FMT_SRGGB12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_SBGGR14_1X14, 14, V4L2_PIX_FMT_SBGGR14P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 14) }, + { MEDIA_BUS_FMT_SGBRG14_1X14, 14, V4L2_PIX_FMT_SGBRG14P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 14) }, + { MEDIA_BUS_FMT_SGRBG14_1X14, 14, V4L2_PIX_FMT_SGRBG14P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 14) }, + { MEDIA_BUS_FMT_SRGGB14_1X14, 14, V4L2_PIX_FMT_SRGGB14P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 14) }, + { MEDIA_BUS_FMT_Y10_1X10, 10, V4L2_PIX_FMT_Y10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 16, V4L2_PIX_FMT_Y10, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, }; -static const struct vfe_format formats_pix_8x16[] = { - { MEDIA_BUS_FMT_UYVY8_1X16, 8 }, - { MEDIA_BUS_FMT_VYUY8_1X16, 8 }, - { MEDIA_BUS_FMT_YUYV8_1X16, 8 }, - { MEDIA_BUS_FMT_YVYU8_1X16, 8 }, +static const struct camss_format_info formats_rdi_845[] = { + { MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_UYVY, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_VYUY, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_YUYV, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_YVYU, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_SBGGR8_1X8, 8, V4L2_PIX_FMT_SBGGR8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SGBRG8_1X8, 8, V4L2_PIX_FMT_SGBRG8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SGRBG8_1X8, 8, V4L2_PIX_FMT_SGRBG8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SRGGB8_1X8, 8, V4L2_PIX_FMT_SRGGB8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SBGGR10_1X10, 10, V4L2_PIX_FMT_SBGGR10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SGBRG10_1X10, 10, V4L2_PIX_FMT_SGBRG10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SGRBG10_1X10, 10, V4L2_PIX_FMT_SGRBG10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SRGGB10_1X10, 10, V4L2_PIX_FMT_SRGGB10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, 16, V4L2_PIX_FMT_SBGGR10, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_SBGGR12_1X12, 12, V4L2_PIX_FMT_SBGGR12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_SGBRG12_1X12, 12, V4L2_PIX_FMT_SGBRG12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_SGRBG12_1X12, 12, V4L2_PIX_FMT_SGRBG12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_SRGGB12_1X12, 12, V4L2_PIX_FMT_SRGGB12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_SBGGR14_1X14, 14, V4L2_PIX_FMT_SBGGR14P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 14) }, + { MEDIA_BUS_FMT_SGBRG14_1X14, 14, V4L2_PIX_FMT_SGBRG14P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 14) }, + { MEDIA_BUS_FMT_SGRBG14_1X14, 14, V4L2_PIX_FMT_SGRBG14P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 14) }, + { MEDIA_BUS_FMT_SRGGB14_1X14, 14, V4L2_PIX_FMT_SRGGB14P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 14) }, + { MEDIA_BUS_FMT_Y8_1X8, 8, V4L2_PIX_FMT_GREY, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_Y10_1X10, 10, V4L2_PIX_FMT_Y10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 16, V4L2_PIX_FMT_Y10, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, }; -static const struct vfe_format formats_rdi_8x96[] = { - { MEDIA_BUS_FMT_UYVY8_1X16, 8 }, - { MEDIA_BUS_FMT_VYUY8_1X16, 8 }, - { MEDIA_BUS_FMT_YUYV8_1X16, 8 }, - { MEDIA_BUS_FMT_YVYU8_1X16, 8 }, - { MEDIA_BUS_FMT_SBGGR8_1X8, 8 }, - { MEDIA_BUS_FMT_SGBRG8_1X8, 8 }, - { MEDIA_BUS_FMT_SGRBG8_1X8, 8 }, - { MEDIA_BUS_FMT_SRGGB8_1X8, 8 }, - { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, - { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, - { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, - { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, - { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, 16 }, - { MEDIA_BUS_FMT_SBGGR12_1X12, 12 }, - { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, - { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, - { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, - { MEDIA_BUS_FMT_SBGGR14_1X14, 14 }, - { MEDIA_BUS_FMT_SGBRG14_1X14, 14 }, - { MEDIA_BUS_FMT_SGRBG14_1X14, 14 }, - { MEDIA_BUS_FMT_SRGGB14_1X14, 14 }, - { MEDIA_BUS_FMT_Y10_1X10, 10 }, - { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 16 }, +static const struct camss_format_info formats_pix_8x16[] = { + { MEDIA_BUS_FMT_YUYV8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_YVYU8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_UYVY8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_VYUY8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_YUYV8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_YVYU8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_UYVY8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_VYUY8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_NV16, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_NV16, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_NV16, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_NV16, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_NV61, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_NV61, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_NV61, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_NV61, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, }; -static const struct vfe_format formats_pix_8x96[] = { - { MEDIA_BUS_FMT_UYVY8_1X16, 8 }, - { MEDIA_BUS_FMT_VYUY8_1X16, 8 }, - { MEDIA_BUS_FMT_YUYV8_1X16, 8 }, - { MEDIA_BUS_FMT_YVYU8_1X16, 8 }, +static const struct camss_format_info formats_pix_8x96[] = { + { MEDIA_BUS_FMT_YUYV8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_YVYU8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_UYVY8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_VYUY8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_YUYV8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_YVYU8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_UYVY8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_VYUY8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_NV16, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_NV16, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_NV16, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_NV16, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_NV61, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_NV61, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_NV61, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_NV61, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_UYVY, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_VYUY, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_YUYV, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_YVYU, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, }; -static const struct vfe_format formats_rdi_845[] = { - { MEDIA_BUS_FMT_UYVY8_1X16, 8 }, - { MEDIA_BUS_FMT_VYUY8_1X16, 8 }, - { MEDIA_BUS_FMT_YUYV8_1X16, 8 }, - { MEDIA_BUS_FMT_YVYU8_1X16, 8 }, - { MEDIA_BUS_FMT_SBGGR8_1X8, 8 }, - { MEDIA_BUS_FMT_SGBRG8_1X8, 8 }, - { MEDIA_BUS_FMT_SGRBG8_1X8, 8 }, - { MEDIA_BUS_FMT_SRGGB8_1X8, 8 }, - { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, - { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, - { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, - { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, - { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, 16 }, - { MEDIA_BUS_FMT_SBGGR12_1X12, 12 }, - { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, - { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, - { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, - { MEDIA_BUS_FMT_SBGGR14_1X14, 14 }, - { MEDIA_BUS_FMT_SGBRG14_1X14, 14 }, - { MEDIA_BUS_FMT_SGRBG14_1X14, 14 }, - { MEDIA_BUS_FMT_SRGGB14_1X14, 14 }, - { MEDIA_BUS_FMT_Y8_1X8, 8 }, - { MEDIA_BUS_FMT_Y10_1X10, 10 }, - { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 16 }, +const struct camss_formats vfe_formats_rdi_8x16 = { + .nformats = ARRAY_SIZE(formats_rdi_8x16), + .formats = formats_rdi_8x16 }; -/* - * vfe_get_bpp - map media bus format to bits per pixel - * @formats: supported media bus formats array - * @nformats: size of @formats array - * @code: media bus format code - * - * Return number of bits per pixel - */ -static u8 vfe_get_bpp(const struct vfe_format *formats, - unsigned int nformats, u32 code) -{ - unsigned int i; - - for (i = 0; i < nformats; i++) - if (code == formats[i].code) - return formats[i].bpp; - - WARN(1, "Unknown format\n"); - - return formats[0].bpp; -} +const struct camss_formats vfe_formats_pix_8x16 = { + .nformats = ARRAY_SIZE(formats_pix_8x16), + .formats = formats_pix_8x16 +}; -static u32 vfe_find_code(u32 *code, unsigned int n_code, - unsigned int index, u32 req_code) -{ - int i; +const struct camss_formats vfe_formats_rdi_8x96 = { + .nformats = ARRAY_SIZE(formats_rdi_8x96), + .formats = formats_rdi_8x96 +}; - if (!req_code && (index >= n_code)) - return 0; +const struct camss_formats vfe_formats_pix_8x96 = { + .nformats = ARRAY_SIZE(formats_pix_8x96), + .formats = formats_pix_8x96 +}; - for (i = 0; i < n_code; i++) - if (req_code) { - if (req_code == code[i]) - return req_code; - } else { - if (i == index) - return code[i]; - } +const struct camss_formats vfe_formats_rdi_845 = { + .nformats = ARRAY_SIZE(formats_rdi_845), + .formats = formats_rdi_845 +}; - return code[0]; -} +/* TODO: Replace with pix formats */ +const struct camss_formats vfe_formats_pix_845 = { + .nformats = ARRAY_SIZE(formats_rdi_845), + .formats = formats_rdi_845 +}; static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, unsigned int index, u32 src_req_code) @@ -181,8 +293,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, MEDIA_BUS_FMT_YUYV8_1_5X8, }; - return vfe_find_code(src_code, ARRAY_SIZE(src_code), - index, src_req_code); + return camss_format_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); } case MEDIA_BUS_FMT_YVYU8_1X16: { @@ -191,8 +303,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, MEDIA_BUS_FMT_YVYU8_1_5X8, }; - return vfe_find_code(src_code, ARRAY_SIZE(src_code), - index, src_req_code); + return camss_format_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); } case MEDIA_BUS_FMT_UYVY8_1X16: { @@ -201,8 +313,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, MEDIA_BUS_FMT_UYVY8_1_5X8, }; - return vfe_find_code(src_code, ARRAY_SIZE(src_code), - index, src_req_code); + return camss_format_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); } case MEDIA_BUS_FMT_VYUY8_1X16: { @@ -211,8 +323,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, MEDIA_BUS_FMT_VYUY8_1_5X8, }; - return vfe_find_code(src_code, ARRAY_SIZE(src_code), - index, src_req_code); + return camss_format_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); } default: if (index > 0) @@ -237,8 +349,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, MEDIA_BUS_FMT_YUYV8_1_5X8, }; - return vfe_find_code(src_code, ARRAY_SIZE(src_code), - index, src_req_code); + return camss_format_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); } case MEDIA_BUS_FMT_YVYU8_1X16: { @@ -250,8 +362,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, MEDIA_BUS_FMT_YVYU8_1_5X8, }; - return vfe_find_code(src_code, ARRAY_SIZE(src_code), - index, src_req_code); + return camss_format_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); } case MEDIA_BUS_FMT_UYVY8_1X16: { @@ -263,8 +375,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, MEDIA_BUS_FMT_UYVY8_1_5X8, }; - return vfe_find_code(src_code, ARRAY_SIZE(src_code), - index, src_req_code); + return camss_format_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); } case MEDIA_BUS_FMT_VYUY8_1X16: { @@ -276,8 +388,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, MEDIA_BUS_FMT_VYUY8_1_5X8, }; - return vfe_find_code(src_code, ARRAY_SIZE(src_code), - index, src_req_code); + return camss_format_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); } default: if (index > 0) @@ -296,7 +408,7 @@ int vfe_reset(struct vfe_device *vfe) reinit_completion(&vfe->reset_complete); - vfe->ops->global_reset(vfe); + vfe->res->hw_ops->global_reset(vfe); time = wait_for_completion_timeout(&vfe->reset_complete, msecs_to_jiffies(VFE_RESET_TIMEOUT_MS)); @@ -312,7 +424,7 @@ static void vfe_init_outputs(struct vfe_device *vfe) { int i; - for (i = 0; i < vfe->line_num; i++) { + for (i = 0; i < vfe->res->line_num; i++) { struct vfe_output *output = &vfe->line[i].output; output->state = VFE_OUTPUT_OFF; @@ -421,7 +533,7 @@ static int vfe_disable_output(struct vfe_line *line) spin_lock_irqsave(&vfe->output_lock, flags); for (i = 0; i < output->wm_num; i++) - vfe->ops->vfe_wm_stop(vfe, output->wm_idx[i]); + vfe->res->hw_ops->vfe_wm_stop(vfe, output->wm_idx[i]); output->gen2.active_num = 0; spin_unlock_irqrestore(&vfe->output_lock, flags); @@ -537,7 +649,7 @@ static int vfe_set_clock_rates(struct vfe_device *vfe) int i, j; int ret; - for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) { + for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++) { ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity, &pixel_clock[i]); if (ret) @@ -551,7 +663,7 @@ static int vfe_set_clock_rates(struct vfe_device *vfe) u64 min_rate = 0; long rate; - for (j = VFE_LINE_RDI0; j < vfe->line_num; j++) { + for (j = VFE_LINE_RDI0; j < vfe->res->line_num; j++) { u32 tmp; u8 bpp; @@ -560,9 +672,9 @@ static int vfe_set_clock_rates(struct vfe_device *vfe) } else { struct vfe_line *l = &vfe->line[j]; - bpp = vfe_get_bpp(l->formats, - l->nformats, - l->fmt[MSM_VFE_PAD_SINK].code); + bpp = camss_format_get_bpp(l->formats, + l->nformats, + l->fmt[MSM_VFE_PAD_SINK].code); tmp = pixel_clock[j] * bpp / 64; } @@ -618,7 +730,7 @@ static int vfe_check_clock_rates(struct vfe_device *vfe) int i, j; int ret; - for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) { + for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++) { ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity, &pixel_clock[i]); if (ret) @@ -632,7 +744,7 @@ static int vfe_check_clock_rates(struct vfe_device *vfe) u64 min_rate = 0; unsigned long rate; - for (j = VFE_LINE_RDI0; j < vfe->line_num; j++) { + for (j = VFE_LINE_RDI0; j < vfe->res->line_num; j++) { u32 tmp; u8 bpp; @@ -641,9 +753,9 @@ static int vfe_check_clock_rates(struct vfe_device *vfe) } else { struct vfe_line *l = &vfe->line[j]; - bpp = vfe_get_bpp(l->formats, - l->nformats, - l->fmt[MSM_VFE_PAD_SINK].code); + bpp = camss_format_get_bpp(l->formats, + l->nformats, + l->fmt[MSM_VFE_PAD_SINK].code); tmp = pixel_clock[j] * bpp / 64; } @@ -675,7 +787,7 @@ int vfe_get(struct vfe_device *vfe) mutex_lock(&vfe->power_lock); if (vfe->power_count == 0) { - ret = vfe->ops->pm_domain_on(vfe); + ret = vfe->res->hw_ops->pm_domain_on(vfe); if (ret < 0) goto error_pm_domain; @@ -700,7 +812,7 @@ int vfe_get(struct vfe_device *vfe) vfe_init_outputs(vfe); - vfe->ops->hw_version(vfe); + vfe->res->hw_ops->hw_version(vfe); } else { ret = vfe_check_clock_rates(vfe); if (ret < 0) @@ -718,7 +830,7 @@ error_reset: error_pm_runtime_get: pm_runtime_put_sync(vfe->camss->dev); error_domain_off: - vfe->ops->pm_domain_off(vfe); + vfe->res->hw_ops->pm_domain_off(vfe); error_pm_domain: mutex_unlock(&vfe->power_lock); @@ -740,11 +852,11 @@ void vfe_put(struct vfe_device *vfe) } else if (vfe->power_count == 1) { if (vfe->was_streaming) { vfe->was_streaming = 0; - vfe->ops->vfe_halt(vfe); + vfe->res->hw_ops->vfe_halt(vfe); } camss_disable_clocks(vfe->nclocks, vfe->clock); pm_runtime_put_sync(vfe->camss->dev); - vfe->ops->pm_domain_off(vfe); + vfe->res->hw_ops->pm_domain_off(vfe); } vfe->power_count--; @@ -834,12 +946,12 @@ static int vfe_set_stream(struct v4l2_subdev *sd, int enable) if (enable) { line->output.state = VFE_OUTPUT_RESERVED; - ret = vfe->ops->vfe_enable(line); + ret = vfe->res->hw_ops->vfe_enable(line); if (ret < 0) dev_err(vfe->camss->dev, "Failed to enable vfe outputs\n"); } else { - ret = vfe->ops->vfe_disable(line); + ret = vfe->res->hw_ops->vfe_disable(line); if (ret < 0) dev_err(vfe->camss->dev, "Failed to disable vfe outputs\n"); @@ -1376,23 +1488,24 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, int i, j; int ret; - vfe->ops = res->ops; - - if (!res->line_num) + if (!res->vfe.line_num) return -EINVAL; + vfe->res = &res->vfe; + vfe->res->hw_ops->subdev_init(dev, vfe); + /* Power domain */ - if (res->pd_name) { + if (res->vfe.pd_name) { vfe->genpd = dev_pm_domain_attach_by_name(camss->dev, - res->pd_name); + res->vfe.pd_name); if (IS_ERR(vfe->genpd)) { ret = PTR_ERR(vfe->genpd); return ret; } } - if (!vfe->genpd && res->has_pd) { + if (!vfe->genpd && res->vfe.has_pd) { /* * Legacy magic index. * Requires @@ -1409,9 +1522,6 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, return PTR_ERR(vfe->genpd); } - vfe->line_num = res->line_num; - vfe->ops->subdev_init(dev, vfe); - /* Memory */ vfe->base = devm_platform_ioremap_resource_byname(pdev, res->reg[0]); @@ -1429,7 +1539,7 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, vfe->irq = ret; snprintf(vfe->irq_name, sizeof(vfe->irq_name), "%s_%s%d", dev_name(dev), MSM_VFE_NAME, id); - ret = devm_request_irq(dev, vfe->irq, vfe->ops->isr, + ret = devm_request_irq(dev, vfe->irq, vfe->res->hw_ops->isr, IRQF_TRIGGER_RISING, vfe->irq_name, vfe); if (ret < 0) { dev_err(dev, "request_irq failed: %d\n", ret); @@ -1488,7 +1598,7 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, vfe->id = id; vfe->reg_update = 0; - for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) { + for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++) { struct vfe_line *l = &vfe->line[i]; l->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; @@ -1497,32 +1607,12 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, init_completion(&l->output.sof); init_completion(&l->output.reg_update); - switch (camss->res->version) { - case CAMSS_8x16: - if (i == VFE_LINE_PIX) { - l->formats = formats_pix_8x16; - l->nformats = ARRAY_SIZE(formats_pix_8x16); - } else { - l->formats = formats_rdi_8x16; - l->nformats = ARRAY_SIZE(formats_rdi_8x16); - } - break; - case CAMSS_8x96: - case CAMSS_660: - if (i == VFE_LINE_PIX) { - l->formats = formats_pix_8x96; - l->nformats = ARRAY_SIZE(formats_pix_8x96); - } else { - l->formats = formats_rdi_8x96; - l->nformats = ARRAY_SIZE(formats_rdi_8x96); - } - break; - case CAMSS_845: - case CAMSS_8250: - case CAMSS_8280XP: - l->formats = formats_rdi_845; - l->nformats = ARRAY_SIZE(formats_rdi_845); - break; + if (i == VFE_LINE_PIX) { + l->nformats = res->vfe.formats_pix->nformats; + l->formats = res->vfe.formats_pix->formats; + } else { + l->nformats = res->vfe.formats_rdi->nformats; + l->formats = res->vfe.formats_rdi->formats; } } @@ -1636,7 +1726,7 @@ int msm_vfe_register_entities(struct vfe_device *vfe, int ret; int i; - for (i = 0; i < vfe->line_num; i++) { + for (i = 0; i < vfe->res->line_num; i++) { char name[32]; sd = &vfe->line[i].subdev; @@ -1686,10 +1776,13 @@ int msm_vfe_register_entities(struct vfe_device *vfe, video_out->bpl_alignment = 16; video_out->line_based = 1; } + + video_out->nformats = vfe->line[i].nformats; + video_out->formats = vfe->line[i].formats; + snprintf(name, ARRAY_SIZE(name), "%s%d_%s%d", MSM_VFE_NAME, vfe->id, "video", i); - ret = msm_video_register(video_out, v4l2_dev, name, - i == VFE_LINE_PIX ? 1 : 0); + ret = msm_video_register(video_out, v4l2_dev, name); if (ret < 0) { dev_err(dev, "Failed to register video node: %d\n", ret); @@ -1743,7 +1836,7 @@ void msm_vfe_unregister_entities(struct vfe_device *vfe) mutex_destroy(&vfe->power_lock); mutex_destroy(&vfe->stream_lock); - for (i = 0; i < vfe->line_num; i++) { + for (i = 0; i < vfe->res->line_num; i++) { struct v4l2_subdev *sd = &vfe->line[i].subdev; struct camss_video *video_out = &vfe->line[i].video_out; @@ -1755,5 +1848,5 @@ void msm_vfe_unregister_entities(struct vfe_device *vfe) bool vfe_is_lite(struct vfe_device *vfe) { - return vfe->camss->res->vfe_res[vfe->id].is_lite; + return vfe->camss->res->vfe_res[vfe->id].vfe.is_lite; } diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h index 0572c9b08e11..10e2cc3c0b83 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.h +++ b/drivers/media/platform/qcom/camss/camss-vfe.h @@ -92,7 +92,7 @@ struct vfe_line { struct v4l2_rect crop; struct camss_video video_out; struct vfe_output output; - const struct vfe_format *formats; + const struct camss_format_info *formats; unsigned int nformats; }; @@ -126,6 +126,16 @@ struct vfe_isr_ops { void (*wm_done)(struct vfe_device *vfe, u8 wm); }; +struct vfe_subdev_resources { + bool is_lite; + u8 line_num; + bool has_pd; + char *pd_name; + const struct vfe_hw_ops *hw_ops; + const struct camss_formats *formats_rdi; + const struct camss_formats *formats_pix; +}; + struct vfe_device { struct camss *camss; u8 id; @@ -143,10 +153,9 @@ struct vfe_device { spinlock_t output_lock; enum vfe_line_id wm_output_map[MSM_VFE_IMAGE_MASTERS_NUM]; struct vfe_line line[VFE_LINE_NUM_MAX]; - u8 line_num; u32 reg_update; u8 was_streaming; - const struct vfe_hw_ops *ops; + const struct vfe_subdev_resources *res; const struct vfe_hw_ops_gen1 *ops_gen1; struct vfe_isr_ops isr_ops; struct camss_video_ops video_ops; @@ -217,6 +226,13 @@ void vfe_pm_domain_off(struct vfe_device *vfe); */ int vfe_pm_domain_on(struct vfe_device *vfe); +extern const struct camss_formats vfe_formats_rdi_8x16; +extern const struct camss_formats vfe_formats_pix_8x16; +extern const struct camss_formats vfe_formats_rdi_8x96; +extern const struct camss_formats vfe_formats_pix_8x96; +extern const struct camss_formats vfe_formats_rdi_845; +extern const struct camss_formats vfe_formats_pix_845; + extern const struct vfe_hw_ops vfe_ops_4_1; extern const struct vfe_hw_ops vfe_ops_4_7; extern const struct vfe_hw_ops vfe_ops_4_8; diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index 54cd82f74115..cd72feca618c 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -24,269 +24,10 @@ #define CAMSS_FRAME_MAX_HEIGHT_RDI 8191 #define CAMSS_FRAME_MAX_HEIGHT_PIX 4096 -struct fract { - u8 numerator; - u8 denominator; -}; - -/* - * struct camss_format_info - ISP media bus format information - * @code: V4L2 media bus format code - * @pixelformat: V4L2 pixel format FCC identifier - * @planes: Number of planes - * @hsub: Horizontal subsampling (for each plane) - * @vsub: Vertical subsampling (for each plane) - * @bpp: Bits per pixel when stored in memory (for each plane) - */ -struct camss_format_info { - u32 code; - u32 pixelformat; - u8 planes; - struct fract hsub[3]; - struct fract vsub[3]; - unsigned int bpp[3]; -}; - -static const struct camss_format_info formats_rdi_8x16[] = { - { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_UYVY, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_VYUY8_1X16, V4L2_PIX_FMT_VYUY, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_YUYV, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_YVYU8_1X16, V4L2_PIX_FMT_YVYU, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_Y10_1X10, V4L2_PIX_FMT_Y10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, -}; - -static const struct camss_format_info formats_rdi_8x96[] = { - { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_UYVY, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_VYUY8_1X16, V4L2_PIX_FMT_VYUY, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_YUYV, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_YVYU8_1X16, V4L2_PIX_FMT_YVYU, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_PIX_FMT_SBGGR10, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SBGGR14_1X14, V4L2_PIX_FMT_SBGGR14P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 14 } }, - { MEDIA_BUS_FMT_SGBRG14_1X14, V4L2_PIX_FMT_SGBRG14P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 14 } }, - { MEDIA_BUS_FMT_SGRBG14_1X14, V4L2_PIX_FMT_SGRBG14P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 14 } }, - { MEDIA_BUS_FMT_SRGGB14_1X14, V4L2_PIX_FMT_SRGGB14P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 14 } }, - { MEDIA_BUS_FMT_Y10_1X10, V4L2_PIX_FMT_Y10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, V4L2_PIX_FMT_Y10, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, -}; - -static const struct camss_format_info formats_rdi_845[] = { - { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_UYVY, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_VYUY8_1X16, V4L2_PIX_FMT_VYUY, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_YUYV, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_YVYU8_1X16, V4L2_PIX_FMT_YVYU, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_PIX_FMT_SBGGR10, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SBGGR14_1X14, V4L2_PIX_FMT_SBGGR14P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 14 } }, - { MEDIA_BUS_FMT_SGBRG14_1X14, V4L2_PIX_FMT_SGBRG14P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 14 } }, - { MEDIA_BUS_FMT_SGRBG14_1X14, V4L2_PIX_FMT_SGRBG14P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 14 } }, - { MEDIA_BUS_FMT_SRGGB14_1X14, V4L2_PIX_FMT_SRGGB14P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 14 } }, - { MEDIA_BUS_FMT_Y8_1X8, V4L2_PIX_FMT_GREY, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_Y10_1X10, V4L2_PIX_FMT_Y10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, V4L2_PIX_FMT_Y10, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, -}; - -static const struct camss_format_info formats_pix_8x16[] = { - { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV12, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV12, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV12, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV12, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV21, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV21, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV21, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV21, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_NV16, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_YVYU8_1X16, V4L2_PIX_FMT_NV16, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_NV16, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_VYUY8_1X16, V4L2_PIX_FMT_NV16, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_NV61, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_YVYU8_1X16, V4L2_PIX_FMT_NV61, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_NV61, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_VYUY8_1X16, V4L2_PIX_FMT_NV61, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, -}; - -static const struct camss_format_info formats_pix_8x96[] = { - { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV12, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV12, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV12, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV12, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV21, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV21, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV21, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV21, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_NV16, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_YVYU8_1X16, V4L2_PIX_FMT_NV16, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_NV16, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_VYUY8_1X16, V4L2_PIX_FMT_NV16, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_NV61, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_YVYU8_1X16, V4L2_PIX_FMT_NV61, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_NV61, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_VYUY8_1X16, V4L2_PIX_FMT_NV61, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_UYVY, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_VYUY8_1X16, V4L2_PIX_FMT_VYUY, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_YUYV, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_YVYU8_1X16, V4L2_PIX_FMT_YVYU, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, -}; - /* ----------------------------------------------------------------------------- * Helper functions */ -static int video_find_format(u32 code, u32 pixelformat, - const struct camss_format_info *formats, - unsigned int nformats) -{ - int i; - - for (i = 0; i < nformats; i++) { - if (formats[i].code == code && - formats[i].pixelformat == pixelformat) - return i; - } - - for (i = 0; i < nformats; i++) - if (formats[i].code == code) - return i; - - WARN_ON(1); - - return -EINVAL; -} - /* * video_mbus_to_pix_mp - Convert v4l2_mbus_framefmt to v4l2_pix_format_mplane * @mbus: v4l2_mbus_framefmt format (input) @@ -359,9 +100,8 @@ static int video_get_subdev_format(struct camss_video *video, if (ret) return ret; - ret = video_find_format(fmt.format.code, - format->fmt.pix_mp.pixelformat, - video->formats, video->nformats); + ret = camss_format_find_format(fmt.format.code, format->fmt.pix_mp.pixelformat, + video->formats, video->nformats); if (ret < 0) return ret; @@ -969,7 +709,7 @@ static int msm_video_init_format(struct camss_video *video) */ int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev, - const char *name, int is_pix) + const char *name) { struct media_pad *pad = &video->pad; struct video_device *vdev; @@ -1006,34 +746,6 @@ int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev, mutex_init(&video->lock); - switch (video->camss->res->version) { - case CAMSS_8x16: - if (is_pix) { - video->formats = formats_pix_8x16; - video->nformats = ARRAY_SIZE(formats_pix_8x16); - } else { - video->formats = formats_rdi_8x16; - video->nformats = ARRAY_SIZE(formats_rdi_8x16); - } - break; - case CAMSS_8x96: - case CAMSS_660: - if (is_pix) { - video->formats = formats_pix_8x96; - video->nformats = ARRAY_SIZE(formats_pix_8x96); - } else { - video->formats = formats_rdi_8x96; - video->nformats = ARRAY_SIZE(formats_rdi_8x96); - } - break; - case CAMSS_845: - case CAMSS_8250: - case CAMSS_8280XP: - video->formats = formats_rdi_845; - video->nformats = ARRAY_SIZE(formats_rdi_845); - break; - } - ret = msm_video_init_format(video); if (ret < 0) { dev_err(v4l2_dev->dev, "Failed to init format: %d\n", ret); diff --git a/drivers/media/platform/qcom/camss/camss-video.h b/drivers/media/platform/qcom/camss/camss-video.h index bdbae8424140..d3e56e240a88 100644 --- a/drivers/media/platform/qcom/camss/camss-video.h +++ b/drivers/media/platform/qcom/camss/camss-video.h @@ -33,8 +33,6 @@ struct camss_video_ops { enum vb2_buffer_state state); }; -struct camss_format_info; - struct camss_video { struct camss *camss; struct vb2_queue vb2_q; @@ -53,7 +51,7 @@ struct camss_video { }; int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev, - const char *name, int is_pix); + const char *name); void msm_video_unregister(struct camss_video *video); diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 1923615f0eea..51b1d3550421 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -32,6 +32,8 @@ #define CAMSS_CLOCK_MARGIN_NUMERATOR 105 #define CAMSS_CLOCK_MARGIN_DENOMINATOR 100 +static const struct parent_dev_ops vfe_parent_dev_ops; + static const struct camss_subdev_resources csiphy_res_8x16[] = { /* CSIPHY0 */ { @@ -43,7 +45,10 @@ static const struct camss_subdev_resources csiphy_res_8x16[] = { { 100000000, 200000000 } }, .reg = { "csiphy0", "csiphy0_clk_mux" }, .interrupt = { "csiphy0" }, - .ops = &csiphy_ops_2ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_2ph_1_0, + .formats = &csiphy_formats_8x16 + } }, /* CSIPHY1 */ @@ -56,7 +61,10 @@ static const struct camss_subdev_resources csiphy_res_8x16[] = { { 100000000, 200000000 } }, .reg = { "csiphy1", "csiphy1_clk_mux" }, .interrupt = { "csiphy1" }, - .ops = &csiphy_ops_2ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_2ph_1_0, + .formats = &csiphy_formats_8x16 + } } }; @@ -76,7 +84,11 @@ static const struct camss_subdev_resources csid_res_8x16[] = { { 0 } }, .reg = { "csid0" }, .interrupt = { "csid0" }, - .ops = &csid_ops_4_1, + .csid = { + .hw_ops = &csid_ops_4_1, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_4_1 + } }, /* CSID1 */ @@ -94,7 +106,11 @@ static const struct camss_subdev_resources csid_res_8x16[] = { { 0 } }, .reg = { "csid1" }, .interrupt = { "csid1" }, - .ops = &csid_ops_4_1, + .csid = { + .hw_ops = &csid_ops_4_1, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_4_1 + } }, }; @@ -105,8 +121,7 @@ static const struct camss_subdev_resources ispif_res_8x16 = { "csi1", "csi1_pix", "csi1_rdi" }, .clock_for_reset = { "vfe0", "csi_vfe0" }, .reg = { "ispif", "csi_clk_mux" }, - .interrupt = { "ispif" } - + .interrupt = { "ispif" }, }; static const struct camss_subdev_resources vfe_res_8x16[] = { @@ -128,8 +143,12 @@ static const struct camss_subdev_resources vfe_res_8x16[] = { { 0 } }, .reg = { "vfe0" }, .interrupt = { "vfe0" }, - .line_num = 3, - .ops = &vfe_ops_4_1 + .vfe = { + .line_num = 3, + .hw_ops = &vfe_ops_4_1, + .formats_rdi = &vfe_formats_rdi_8x16, + .formats_pix = &vfe_formats_pix_8x16 + } } }; @@ -144,7 +163,10 @@ static const struct camss_subdev_resources csiphy_res_8x96[] = { { 100000000, 200000000, 266666667 } }, .reg = { "csiphy0", "csiphy0_clk_mux" }, .interrupt = { "csiphy0" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_8x96 + } }, /* CSIPHY1 */ @@ -157,7 +179,10 @@ static const struct camss_subdev_resources csiphy_res_8x96[] = { { 100000000, 200000000, 266666667 } }, .reg = { "csiphy1", "csiphy1_clk_mux" }, .interrupt = { "csiphy1" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_8x96 + } }, /* CSIPHY2 */ @@ -170,7 +195,10 @@ static const struct camss_subdev_resources csiphy_res_8x96[] = { { 100000000, 200000000, 266666667 } }, .reg = { "csiphy2", "csiphy2_clk_mux" }, .interrupt = { "csiphy2" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_8x96 + } } }; @@ -190,7 +218,11 @@ static const struct camss_subdev_resources csid_res_8x96[] = { { 0 } }, .reg = { "csid0" }, .interrupt = { "csid0" }, - .ops = &csid_ops_4_7, + .csid = { + .hw_ops = &csid_ops_4_7, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_4_7 + } }, /* CSID1 */ @@ -208,7 +240,11 @@ static const struct camss_subdev_resources csid_res_8x96[] = { { 0 } }, .reg = { "csid1" }, .interrupt = { "csid1" }, - .ops = &csid_ops_4_7, + .csid = { + .hw_ops = &csid_ops_4_7, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_4_7 + } }, /* CSID2 */ @@ -226,7 +262,11 @@ static const struct camss_subdev_resources csid_res_8x96[] = { { 0 } }, .reg = { "csid2" }, .interrupt = { "csid2" }, - .ops = &csid_ops_4_7, + .csid = { + .hw_ops = &csid_ops_4_7, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_4_7 + } }, /* CSID3 */ @@ -244,7 +284,11 @@ static const struct camss_subdev_resources csid_res_8x96[] = { { 0 } }, .reg = { "csid3" }, .interrupt = { "csid3" }, - .ops = &csid_ops_4_7, + .csid = { + .hw_ops = &csid_ops_4_7, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_4_7 + } } }; @@ -257,7 +301,7 @@ static const struct camss_subdev_resources ispif_res_8x96 = { "csi3", "csi3_pix", "csi3_rdi" }, .clock_for_reset = { "vfe0", "csi_vfe0", "vfe1", "csi_vfe1" }, .reg = { "ispif", "csi_clk_mux" }, - .interrupt = { "ispif" } + .interrupt = { "ispif" }, }; static const struct camss_subdev_resources vfe_res_8x96[] = { @@ -277,9 +321,13 @@ static const struct camss_subdev_resources vfe_res_8x96[] = { { 0 } }, .reg = { "vfe0" }, .interrupt = { "vfe0" }, - .line_num = 3, - .has_pd = true, - .ops = &vfe_ops_4_7 + .vfe = { + .line_num = 3, + .has_pd = true, + .hw_ops = &vfe_ops_4_7, + .formats_rdi = &vfe_formats_rdi_8x96, + .formats_pix = &vfe_formats_pix_8x96 + } }, /* VFE1 */ @@ -298,9 +346,13 @@ static const struct camss_subdev_resources vfe_res_8x96[] = { { 0 } }, .reg = { "vfe1" }, .interrupt = { "vfe1" }, - .line_num = 3, - .has_pd = true, - .ops = &vfe_ops_4_7 + .vfe = { + .line_num = 3, + .has_pd = true, + .hw_ops = &vfe_ops_4_7, + .formats_rdi = &vfe_formats_rdi_8x96, + .formats_pix = &vfe_formats_pix_8x96 + } } }; @@ -317,7 +369,10 @@ static const struct camss_subdev_resources csiphy_res_660[] = { { 0 } }, .reg = { "csiphy0", "csiphy0_clk_mux" }, .interrupt = { "csiphy0" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_8x96 + } }, /* CSIPHY1 */ @@ -332,7 +387,10 @@ static const struct camss_subdev_resources csiphy_res_660[] = { { 0 } }, .reg = { "csiphy1", "csiphy1_clk_mux" }, .interrupt = { "csiphy1" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_8x96 + } }, /* CSIPHY2 */ @@ -347,7 +405,10 @@ static const struct camss_subdev_resources csiphy_res_660[] = { { 0 } }, .reg = { "csiphy2", "csiphy2_clk_mux" }, .interrupt = { "csiphy2" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_8x96 + } } }; @@ -370,7 +431,11 @@ static const struct camss_subdev_resources csid_res_660[] = { { 0 } }, .reg = { "csid0" }, .interrupt = { "csid0" }, - .ops = &csid_ops_4_7, + .csid = { + .hw_ops = &csid_ops_4_7, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_4_7 + } }, /* CSID1 */ @@ -391,7 +456,11 @@ static const struct camss_subdev_resources csid_res_660[] = { { 0 } }, .reg = { "csid1" }, .interrupt = { "csid1" }, - .ops = &csid_ops_4_7, + .csid = { + .hw_ops = &csid_ops_4_7, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_4_7 + } }, /* CSID2 */ @@ -412,7 +481,11 @@ static const struct camss_subdev_resources csid_res_660[] = { { 0 } }, .reg = { "csid2" }, .interrupt = { "csid2" }, - .ops = &csid_ops_4_7, + .csid = { + .hw_ops = &csid_ops_4_7, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_4_7 + } }, /* CSID3 */ @@ -433,7 +506,11 @@ static const struct camss_subdev_resources csid_res_660[] = { { 0 } }, .reg = { "csid3" }, .interrupt = { "csid3" }, - .ops = &csid_ops_4_7, + .csid = { + .hw_ops = &csid_ops_4_7, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_4_7 + } } }; @@ -446,7 +523,7 @@ static const struct camss_subdev_resources ispif_res_660 = { "csi3", "csi3_pix", "csi3_rdi" }, .clock_for_reset = { "vfe0", "csi_vfe0", "vfe1", "csi_vfe1" }, .reg = { "ispif", "csi_clk_mux" }, - .interrupt = { "ispif" } + .interrupt = { "ispif" }, }; static const struct camss_subdev_resources vfe_res_660[] = { @@ -469,9 +546,13 @@ static const struct camss_subdev_resources vfe_res_660[] = { { 0 } }, .reg = { "vfe0" }, .interrupt = { "vfe0" }, - .line_num = 3, - .has_pd = true, - .ops = &vfe_ops_4_8 + .vfe = { + .line_num = 3, + .has_pd = true, + .hw_ops = &vfe_ops_4_8, + .formats_rdi = &vfe_formats_rdi_8x96, + .formats_pix = &vfe_formats_pix_8x96 + } }, /* VFE1 */ @@ -493,9 +574,13 @@ static const struct camss_subdev_resources vfe_res_660[] = { { 0 } }, .reg = { "vfe1" }, .interrupt = { "vfe1" }, - .line_num = 3, - .has_pd = true, - .ops = &vfe_ops_4_8 + .vfe = { + .line_num = 3, + .has_pd = true, + .hw_ops = &vfe_ops_4_8, + .formats_rdi = &vfe_formats_rdi_8x96, + .formats_pix = &vfe_formats_pix_8x96 + } } }; @@ -516,7 +601,10 @@ static const struct camss_subdev_resources csiphy_res_845[] = { { 19200000, 240000000, 269333333 } }, .reg = { "csiphy0" }, .interrupt = { "csiphy0" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } }, /* CSIPHY1 */ @@ -535,7 +623,10 @@ static const struct camss_subdev_resources csiphy_res_845[] = { { 19200000, 240000000, 269333333 } }, .reg = { "csiphy1" }, .interrupt = { "csiphy1" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } }, /* CSIPHY2 */ @@ -554,7 +645,10 @@ static const struct camss_subdev_resources csiphy_res_845[] = { { 19200000, 240000000, 269333333 } }, .reg = { "csiphy2" }, .interrupt = { "csiphy2" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } }, /* CSIPHY3 */ @@ -573,7 +667,10 @@ static const struct camss_subdev_resources csiphy_res_845[] = { { 19200000, 240000000, 269333333 } }, .reg = { "csiphy3" }, .interrupt = { "csiphy3" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } } }; @@ -596,7 +693,11 @@ static const struct camss_subdev_resources csid_res_845[] = { { 384000000 } }, .reg = { "csid0" }, .interrupt = { "csid0" }, - .ops = &csid_ops_gen2 + .csid = { + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } }, /* CSID1 */ @@ -617,7 +718,11 @@ static const struct camss_subdev_resources csid_res_845[] = { { 384000000 } }, .reg = { "csid1" }, .interrupt = { "csid1" }, - .ops = &csid_ops_gen2 + .csid = { + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } }, /* CSID2 */ @@ -638,8 +743,12 @@ static const struct camss_subdev_resources csid_res_845[] = { { 384000000 } }, .reg = { "csid2" }, .interrupt = { "csid2" }, - .is_lite = true, - .ops = &csid_ops_gen2 + .csid = { + .is_lite = true, + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } } }; @@ -662,9 +771,13 @@ static const struct camss_subdev_resources vfe_res_845[] = { { 384000000 } }, .reg = { "vfe0" }, .interrupt = { "vfe0" }, - .line_num = 4, - .has_pd = true, - .ops = &vfe_ops_170 + .vfe = { + .line_num = 4, + .has_pd = true, + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } }, /* VFE1 */ @@ -685,9 +798,13 @@ static const struct camss_subdev_resources vfe_res_845[] = { { 384000000 } }, .reg = { "vfe1" }, .interrupt = { "vfe1" }, - .line_num = 4, - .has_pd = true, - .ops = &vfe_ops_170 + .vfe = { + .line_num = 4, + .has_pd = true, + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } }, /* VFE-lite */ @@ -707,9 +824,13 @@ static const struct camss_subdev_resources vfe_res_845[] = { { 384000000 } }, .reg = { "vfe_lite" }, .interrupt = { "vfe_lite" }, - .is_lite = true, - .line_num = 4, - .ops = &vfe_ops_170 + .vfe = { + .is_lite = true, + .line_num = 4, + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } } }; @@ -722,7 +843,10 @@ static const struct camss_subdev_resources csiphy_res_8250[] = { { 300000000 } }, .reg = { "csiphy0" }, .interrupt = { "csiphy0" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } }, /* CSIPHY1 */ { @@ -732,7 +856,10 @@ static const struct camss_subdev_resources csiphy_res_8250[] = { { 300000000 } }, .reg = { "csiphy1" }, .interrupt = { "csiphy1" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } }, /* CSIPHY2 */ { @@ -742,7 +869,10 @@ static const struct camss_subdev_resources csiphy_res_8250[] = { { 300000000 } }, .reg = { "csiphy2" }, .interrupt = { "csiphy2" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } }, /* CSIPHY3 */ { @@ -752,7 +882,10 @@ static const struct camss_subdev_resources csiphy_res_8250[] = { { 300000000 } }, .reg = { "csiphy3" }, .interrupt = { "csiphy3" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } }, /* CSIPHY4 */ { @@ -762,7 +895,10 @@ static const struct camss_subdev_resources csiphy_res_8250[] = { { 300000000 } }, .reg = { "csiphy4" }, .interrupt = { "csiphy4" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } }, /* CSIPHY5 */ { @@ -772,7 +908,10 @@ static const struct camss_subdev_resources csiphy_res_8250[] = { { 300000000 } }, .reg = { "csiphy5" }, .interrupt = { "csiphy5" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } } }; @@ -788,7 +927,11 @@ static const struct camss_subdev_resources csid_res_8250[] = { { 0 } }, .reg = { "csid0" }, .interrupt = { "csid0" }, - .ops = &csid_ops_gen2 + .csid = { + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } }, /* CSID1 */ { @@ -801,7 +944,11 @@ static const struct camss_subdev_resources csid_res_8250[] = { { 0 } }, .reg = { "csid1" }, .interrupt = { "csid1" }, - .ops = &csid_ops_gen2 + .csid = { + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } }, /* CSID2 */ { @@ -813,8 +960,12 @@ static const struct camss_subdev_resources csid_res_8250[] = { { 0 } }, .reg = { "csid2" }, .interrupt = { "csid2" }, - .is_lite = true, - .ops = &csid_ops_gen2 + .csid = { + .is_lite = true, + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } }, /* CSID3 */ { @@ -826,8 +977,12 @@ static const struct camss_subdev_resources csid_res_8250[] = { { 0 } }, .reg = { "csid3" }, .interrupt = { "csid3" }, - .is_lite = true, - .ops = &csid_ops_gen2 + .csid = { + .is_lite = true, + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } } }; @@ -849,10 +1004,14 @@ static const struct camss_subdev_resources vfe_res_8250[] = { { 0 } }, .reg = { "vfe0" }, .interrupt = { "vfe0" }, - .pd_name = "ife0", - .line_num = 3, - .has_pd = true, - .ops = &vfe_ops_480 + .vfe = { + .line_num = 3, + .has_pd = true, + .pd_name = "ife0", + .hw_ops = &vfe_ops_480, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } }, /* VFE1 */ { @@ -871,10 +1030,14 @@ static const struct camss_subdev_resources vfe_res_8250[] = { { 0 } }, .reg = { "vfe1" }, .interrupt = { "vfe1" }, - .pd_name = "ife1", - .line_num = 3, - .has_pd = true, - .ops = &vfe_ops_480 + .vfe = { + .line_num = 3, + .has_pd = true, + .pd_name = "ife1", + .hw_ops = &vfe_ops_480, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } }, /* VFE2 (lite) */ { @@ -892,9 +1055,13 @@ static const struct camss_subdev_resources vfe_res_8250[] = { { 0 } }, .reg = { "vfe_lite0" }, .interrupt = { "vfe_lite0" }, - .is_lite = true, - .line_num = 4, - .ops = &vfe_ops_480 + .vfe = { + .is_lite = true, + .line_num = 4, + .hw_ops = &vfe_ops_480, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } }, /* VFE3 (lite) */ { @@ -912,9 +1079,13 @@ static const struct camss_subdev_resources vfe_res_8250[] = { { 0 } }, .reg = { "vfe_lite1" }, .interrupt = { "vfe_lite1" }, - .is_lite = true, - .line_num = 4, - .ops = &vfe_ops_480 + .vfe = { + .is_lite = true, + .line_num = 4, + .hw_ops = &vfe_ops_480, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } }, }; @@ -950,7 +1121,10 @@ static const struct camss_subdev_resources csiphy_res_sc8280xp[] = { { 300000000 } }, .reg = { "csiphy0" }, .interrupt = { "csiphy0" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } }, /* CSIPHY1 */ { @@ -960,7 +1134,10 @@ static const struct camss_subdev_resources csiphy_res_sc8280xp[] = { { 300000000 } }, .reg = { "csiphy1" }, .interrupt = { "csiphy1" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } }, /* CSIPHY2 */ { @@ -970,7 +1147,10 @@ static const struct camss_subdev_resources csiphy_res_sc8280xp[] = { { 300000000 } }, .reg = { "csiphy2" }, .interrupt = { "csiphy2" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } }, /* CSIPHY3 */ { @@ -980,7 +1160,10 @@ static const struct camss_subdev_resources csiphy_res_sc8280xp[] = { { 300000000 } }, .reg = { "csiphy3" }, .interrupt = { "csiphy3" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } }, }; @@ -995,7 +1178,11 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { { 0 } }, .reg = { "csid0" }, .interrupt = { "csid0" }, - .ops = &csid_ops_gen2 + .csid = { + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } }, /* CSID1 */ { @@ -1007,7 +1194,11 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { { 0 } }, .reg = { "csid1" }, .interrupt = { "csid1" }, - .ops = &csid_ops_gen2 + .csid = { + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } }, /* CSID2 */ { @@ -1019,7 +1210,11 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { { 0 } }, .reg = { "csid2" }, .interrupt = { "csid2" }, - .ops = &csid_ops_gen2 + .csid = { + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } }, /* CSID3 */ { @@ -1031,7 +1226,11 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { { 0 } }, .reg = { "csid3" }, .interrupt = { "csid3" }, - .ops = &csid_ops_gen2 + .csid = { + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } }, /* CSID_LITE0 */ { @@ -1042,8 +1241,12 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { { 0 }, }, .reg = { "csid0_lite" }, .interrupt = { "csid0_lite" }, - .is_lite = true, - .ops = &csid_ops_gen2 + .csid = { + .is_lite = true, + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } }, /* CSID_LITE1 */ { @@ -1054,8 +1257,12 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { { 0 }, }, .reg = { "csid1_lite" }, .interrupt = { "csid1_lite" }, - .is_lite = true, - .ops = &csid_ops_gen2 + .csid = { + .is_lite = true, + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } }, /* CSID_LITE2 */ { @@ -1066,8 +1273,12 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { { 0 }, }, .reg = { "csid2_lite" }, .interrupt = { "csid2_lite" }, - .is_lite = true, - .ops = &csid_ops_gen2 + .csid = { + .is_lite = true, + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } }, /* CSID_LITE3 */ { @@ -1078,8 +1289,12 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { { 0 }, }, .reg = { "csid3_lite" }, .interrupt = { "csid3_lite" }, - .is_lite = true, - .ops = &csid_ops_gen2 + .csid = { + .is_lite = true, + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } } }; @@ -1096,9 +1311,13 @@ static const struct camss_subdev_resources vfe_res_sc8280xp[] = { { 0 }, }, .reg = { "vfe0" }, .interrupt = { "vfe0" }, - .pd_name = "ife0", - .line_num = 4, - .ops = &vfe_ops_170 + .vfe = { + .line_num = 4, + .pd_name = "ife0", + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } }, /* VFE1 */ { @@ -1112,9 +1331,13 @@ static const struct camss_subdev_resources vfe_res_sc8280xp[] = { { 0 }, }, .reg = { "vfe1" }, .interrupt = { "vfe1" }, - .pd_name = "ife1", - .line_num = 4, - .ops = &vfe_ops_170 + .vfe = { + .line_num = 4, + .pd_name = "ife1", + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } }, /* VFE2 */ { @@ -1128,9 +1351,13 @@ static const struct camss_subdev_resources vfe_res_sc8280xp[] = { { 0 }, }, .reg = { "vfe2" }, .interrupt = { "vfe2" }, - .pd_name = "ife2", - .line_num = 4, - .ops = &vfe_ops_170 + .vfe = { + .line_num = 4, + .pd_name = "ife2", + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } }, /* VFE3 */ { @@ -1144,9 +1371,13 @@ static const struct camss_subdev_resources vfe_res_sc8280xp[] = { { 0 }, }, .reg = { "vfe3" }, .interrupt = { "vfe3" }, - .pd_name = "ife3", - .line_num = 4, - .ops = &vfe_ops_170 + .vfe = { + .line_num = 4, + .pd_name = "ife3", + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } }, /* VFE_LITE_0 */ { @@ -1159,9 +1390,13 @@ static const struct camss_subdev_resources vfe_res_sc8280xp[] = { { 320000000, 400000000, 480000000, 600000000 }, }, .reg = { "vfe_lite0" }, .interrupt = { "vfe_lite0" }, - .is_lite = true, - .line_num = 4, - .ops = &vfe_ops_170 + .vfe = { + .is_lite = true, + .line_num = 4, + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } }, /* VFE_LITE_1 */ { @@ -1174,9 +1409,13 @@ static const struct camss_subdev_resources vfe_res_sc8280xp[] = { { 320000000, 400000000, 480000000, 600000000 }, }, .reg = { "vfe_lite1" }, .interrupt = { "vfe_lite1" }, - .is_lite = true, - .line_num = 4, - .ops = &vfe_ops_170 + .vfe = { + .is_lite = true, + .line_num = 4, + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } }, /* VFE_LITE_2 */ { @@ -1189,9 +1428,13 @@ static const struct camss_subdev_resources vfe_res_sc8280xp[] = { { 320000000, 400000000, 480000000, 600000000, }, }, .reg = { "vfe_lite2" }, .interrupt = { "vfe_lite2" }, - .is_lite = true, - .line_num = 4, - .ops = &vfe_ops_170 + .vfe = { + .is_lite = true, + .line_num = 4, + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } }, /* VFE_LITE_3 */ { @@ -1204,9 +1447,13 @@ static const struct camss_subdev_resources vfe_res_sc8280xp[] = { { 320000000, 400000000, 480000000, 600000000 }, }, .reg = { "vfe_lite3" }, .interrupt = { "vfe_lite3" }, - .is_lite = true, - .line_num = 4, - .ops = &vfe_ops_170 + .vfe = { + .is_lite = true, + .line_num = 4, + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } }, }; @@ -1375,7 +1622,7 @@ int camss_pm_domain_on(struct camss *camss, int id) if (id < camss->res->vfe_num) { struct vfe_device *vfe = &camss->vfe[id]; - ret = vfe->ops->pm_domain_on(vfe); + ret = vfe->res->hw_ops->pm_domain_on(vfe); } return ret; @@ -1386,10 +1633,52 @@ void camss_pm_domain_off(struct camss *camss, int id) if (id < camss->res->vfe_num) { struct vfe_device *vfe = &camss->vfe[id]; - vfe->ops->pm_domain_off(vfe); + vfe->res->hw_ops->pm_domain_off(vfe); + } +} + +static int vfe_parent_dev_ops_get(struct camss *camss, int id) +{ + int ret = -EINVAL; + + if (id < camss->res->vfe_num) { + struct vfe_device *vfe = &camss->vfe[id]; + + ret = vfe_get(vfe); } + + return ret; +} + +static int vfe_parent_dev_ops_put(struct camss *camss, int id) +{ + if (id < camss->res->vfe_num) { + struct vfe_device *vfe = &camss->vfe[id]; + + vfe_put(vfe); + } + + return 0; } +static void __iomem +*vfe_parent_dev_ops_get_base_address(struct camss *camss, int id) +{ + if (id < camss->res->vfe_num) { + struct vfe_device *vfe = &camss->vfe[id]; + + return vfe->base; + } + + return NULL; +} + +static const struct parent_dev_ops vfe_parent_dev_ops = { + .get = vfe_parent_dev_ops_get, + .put = vfe_parent_dev_ops_put, + .get_base_address = vfe_parent_dev_ops_get_base_address +}; + /* * camss_of_parse_endpoint_node - Parse port endpoint node * @dev: Device @@ -1406,8 +1695,11 @@ static int camss_of_parse_endpoint_node(struct device *dev, struct v4l2_mbus_config_mipi_csi2 *mipi_csi2; struct v4l2_fwnode_endpoint vep = { { 0 } }; unsigned int i; + int ret; - v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &vep); + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &vep); + if (ret) + return ret; csd->interface.csiphy_id = vep.base.port; @@ -1536,72 +1828,30 @@ static int camss_init_subdevices(struct camss *camss) } /* - * camss_register_entities - Register subdev nodes and create links + * camss_link_entities - Register subdev nodes and create links * @camss: CAMSS device * * Return 0 on success or a negative error code on failure */ -static int camss_register_entities(struct camss *camss) +static int camss_link_entities(struct camss *camss) { int i, j, k; int ret; for (i = 0; i < camss->res->csiphy_num; i++) { - ret = msm_csiphy_register_entity(&camss->csiphy[i], - &camss->v4l2_dev); - if (ret < 0) { - dev_err(camss->dev, - "Failed to register csiphy%d entity: %d\n", - i, ret); - goto err_reg_csiphy; - } - } - - for (i = 0; i < camss->res->csid_num; i++) { - ret = msm_csid_register_entity(&camss->csid[i], - &camss->v4l2_dev); - if (ret < 0) { - dev_err(camss->dev, - "Failed to register csid%d entity: %d\n", - i, ret); - goto err_reg_csid; - } - } - - ret = msm_ispif_register_entities(camss->ispif, - &camss->v4l2_dev); - if (ret < 0) { - dev_err(camss->dev, "Failed to register ispif entities: %d\n", - ret); - goto err_reg_ispif; - } - - for (i = 0; i < camss->res->vfe_num; i++) { - ret = msm_vfe_register_entities(&camss->vfe[i], - &camss->v4l2_dev); - if (ret < 0) { - dev_err(camss->dev, - "Failed to register vfe%d entities: %d\n", - i, ret); - goto err_reg_vfe; - } - } - - for (i = 0; i < camss->res->csiphy_num; i++) { for (j = 0; j < camss->res->csid_num; j++) { - ret = media_create_pad_link( - &camss->csiphy[i].subdev.entity, - MSM_CSIPHY_PAD_SRC, - &camss->csid[j].subdev.entity, - MSM_CSID_PAD_SINK, - 0); + ret = media_create_pad_link(&camss->csiphy[i].subdev.entity, + MSM_CSIPHY_PAD_SRC, + &camss->csid[j].subdev.entity, + MSM_CSID_PAD_SINK, + 0); if (ret < 0) { dev_err(camss->dev, "Failed to link %s->%s entities: %d\n", camss->csiphy[i].subdev.entity.name, camss->csid[j].subdev.entity.name, ret); - goto err_link; + return ret; } } } @@ -1609,26 +1859,25 @@ static int camss_register_entities(struct camss *camss) if (camss->ispif) { for (i = 0; i < camss->res->csid_num; i++) { for (j = 0; j < camss->ispif->line_num; j++) { - ret = media_create_pad_link( - &camss->csid[i].subdev.entity, - MSM_CSID_PAD_SRC, - &camss->ispif->line[j].subdev.entity, - MSM_ISPIF_PAD_SINK, - 0); + ret = media_create_pad_link(&camss->csid[i].subdev.entity, + MSM_CSID_PAD_SRC, + &camss->ispif->line[j].subdev.entity, + MSM_ISPIF_PAD_SINK, + 0); if (ret < 0) { dev_err(camss->dev, "Failed to link %s->%s entities: %d\n", camss->csid[i].subdev.entity.name, camss->ispif->line[j].subdev.entity.name, ret); - goto err_link; + return ret; } } } for (i = 0; i < camss->ispif->line_num; i++) for (k = 0; k < camss->res->vfe_num; k++) - for (j = 0; j < camss->vfe[k].line_num; j++) { + for (j = 0; j < camss->vfe[k].res->line_num; j++) { struct v4l2_subdev *ispif = &camss->ispif->line[i].subdev; struct v4l2_subdev *vfe = &camss->vfe[k].line[j].subdev; @@ -1643,13 +1892,13 @@ static int camss_register_entities(struct camss *camss) ispif->entity.name, vfe->entity.name, ret); - goto err_link; + return ret; } } } else { for (i = 0; i < camss->res->csid_num; i++) for (k = 0; k < camss->res->vfe_num; k++) - for (j = 0; j < camss->vfe[k].line_num; j++) { + for (j = 0; j < camss->vfe[k].res->line_num; j++) { struct v4l2_subdev *csid = &camss->csid[i].subdev; struct v4l2_subdev *vfe = &camss->vfe[k].line[j].subdev; @@ -1664,15 +1913,67 @@ static int camss_register_entities(struct camss *camss) csid->entity.name, vfe->entity.name, ret); - goto err_link; + return ret; } } } return 0; +} + +/* + * camss_register_entities - Register subdev nodes and create links + * @camss: CAMSS device + * + * Return 0 on success or a negative error code on failure + */ +static int camss_register_entities(struct camss *camss) +{ + int i; + int ret; + + for (i = 0; i < camss->res->csiphy_num; i++) { + ret = msm_csiphy_register_entity(&camss->csiphy[i], + &camss->v4l2_dev); + if (ret < 0) { + dev_err(camss->dev, + "Failed to register csiphy%d entity: %d\n", + i, ret); + goto err_reg_csiphy; + } + } + + for (i = 0; i < camss->res->csid_num; i++) { + ret = msm_csid_register_entity(&camss->csid[i], + &camss->v4l2_dev); + if (ret < 0) { + dev_err(camss->dev, + "Failed to register csid%d entity: %d\n", + i, ret); + goto err_reg_csid; + } + } + + ret = msm_ispif_register_entities(camss->ispif, + &camss->v4l2_dev); + if (ret < 0) { + dev_err(camss->dev, "Failed to register ispif entities: %d\n", ret); + goto err_reg_ispif; + } + + for (i = 0; i < camss->res->vfe_num; i++) { + ret = msm_vfe_register_entities(&camss->vfe[i], + &camss->v4l2_dev); + if (ret < 0) { + dev_err(camss->dev, + "Failed to register vfe%d entities: %d\n", + i, ret); + goto err_reg_vfe; + } + } + + return 0; -err_link: - i = camss->res->vfe_num; err_reg_vfe: for (i--; i >= 0; i--) msm_vfe_unregister_entities(&camss->vfe[i]); @@ -1810,7 +2111,7 @@ static int camss_configure_pd(struct camss *camss) /* count the # of VFEs which have flagged power-domain */ for (vfepd_num = i = 0; i < camss->res->vfe_num; i++) { - if (res->vfe_res[i].has_pd) + if (res->vfe_res[i].vfe.has_pd) vfepd_num++; } @@ -1992,6 +2293,10 @@ static int camss_probe(struct platform_device *pdev) if (ret < 0) goto err_v4l2_device_unregister; + ret = camss->res->link_entities(camss); + if (ret < 0) + goto err_register_subdevs; + if (num_subdevs) { camss->notifier.ops = &camss_subdev_notifier_ops; @@ -2071,6 +2376,7 @@ static const struct camss_resources msm8916_resources = { .csiphy_num = ARRAY_SIZE(csiphy_res_8x16), .csid_num = ARRAY_SIZE(csid_res_8x16), .vfe_num = ARRAY_SIZE(vfe_res_8x16), + .link_entities = camss_link_entities }; static const struct camss_resources msm8996_resources = { @@ -2082,6 +2388,7 @@ static const struct camss_resources msm8996_resources = { .csiphy_num = ARRAY_SIZE(csiphy_res_8x96), .csid_num = ARRAY_SIZE(csid_res_8x96), .vfe_num = ARRAY_SIZE(vfe_res_8x96), + .link_entities = camss_link_entities }; static const struct camss_resources sdm660_resources = { @@ -2093,6 +2400,7 @@ static const struct camss_resources sdm660_resources = { .csiphy_num = ARRAY_SIZE(csiphy_res_660), .csid_num = ARRAY_SIZE(csid_res_660), .vfe_num = ARRAY_SIZE(vfe_res_660), + .link_entities = camss_link_entities }; static const struct camss_resources sdm845_resources = { @@ -2103,6 +2411,7 @@ static const struct camss_resources sdm845_resources = { .csiphy_num = ARRAY_SIZE(csiphy_res_845), .csid_num = ARRAY_SIZE(csid_res_845), .vfe_num = ARRAY_SIZE(vfe_res_845), + .link_entities = camss_link_entities }; static const struct camss_resources sm8250_resources = { @@ -2116,6 +2425,7 @@ static const struct camss_resources sm8250_resources = { .csiphy_num = ARRAY_SIZE(csiphy_res_8250), .csid_num = ARRAY_SIZE(csid_res_8250), .vfe_num = ARRAY_SIZE(vfe_res_8250), + .link_entities = camss_link_entities }; static const struct camss_resources sc8280xp_resources = { @@ -2130,6 +2440,7 @@ static const struct camss_resources sc8280xp_resources = { .csiphy_num = ARRAY_SIZE(csiphy_res_sc8280xp), .csid_num = ARRAY_SIZE(csid_res_sc8280xp), .vfe_num = ARRAY_SIZE(vfe_res_sc8280xp), + .link_entities = camss_link_entities }; static const struct of_device_id camss_dt_match[] = { diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h index ac15fe23a702..73c47c07fc30 100644 --- a/drivers/media/platform/qcom/camss/camss.h +++ b/drivers/media/platform/qcom/camss/camss.h @@ -22,6 +22,7 @@ #include "camss-csiphy.h" #include "camss-ispif.h" #include "camss-vfe.h" +#include "camss-format.h" #define to_camss(ptr_module) \ container_of(ptr_module, struct camss, ptr_module) @@ -48,11 +49,11 @@ struct camss_subdev_resources { u32 clock_rate[CAMSS_RES_MAX][CAMSS_RES_MAX]; char *reg[CAMSS_RES_MAX]; char *interrupt[CAMSS_RES_MAX]; - char *pd_name; - u8 line_num; - bool has_pd; - bool is_lite; - const void *ops; + union { + struct csiphy_subdev_resources csiphy; + struct csid_subdev_resources csid; + struct vfe_subdev_resources vfe; + }; }; struct icc_bw_tbl { @@ -97,6 +98,7 @@ struct camss_resources { const unsigned int csiphy_num; const unsigned int csid_num; const unsigned int vfe_num; + int (*link_entities)(struct camss *camss); }; struct camss { @@ -133,6 +135,12 @@ struct camss_clock { u32 nfreqs; }; +struct parent_dev_ops { + int (*get)(struct camss *camss, int id); + int (*put)(struct camss *camss, int id); + void __iomem *(*get_base_address)(struct camss *camss, int id); +}; + void camss_add_clock_margin(u64 *rate); int camss_enable_clocks(int nclocks, struct camss_clock *clock, struct device *dev); @@ -143,6 +151,8 @@ s64 camss_get_link_freq(struct media_entity *entity, unsigned int bpp, int camss_get_pixel_clock(struct media_entity *entity, u64 *pixel_clock); int camss_pm_domain_on(struct camss *camss, int id); void camss_pm_domain_off(struct camss *camss, int id); +int camss_vfe_get(struct camss *camss, int id); +void camss_vfe_put(struct camss *camss, int id); void camss_delete(struct camss *camss); #endif /* QC_MSM_CAMSS_H */ diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index ce206b709754..165c947a6703 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -68,6 +68,7 @@ static void venus_event_notify(struct venus_core *core, u32 event) mutex_lock(&core->lock); set_bit(0, &core->sys_error); + set_bit(0, &core->dump_core); list_for_each_entry(inst, &core->instances, list) inst->ops->event_notify(inst, EVT_SESSION_ERROR, NULL); mutex_unlock(&core->lock); @@ -110,7 +111,10 @@ static void venus_sys_error_handler(struct work_struct *work) venus_shutdown(core); - venus_coredump(core); + if (test_bit(0, &core->dump_core)) { + venus_coredump(core); + clear_bit(0, &core->dump_core); + } pm_runtime_put_sync(core->dev); @@ -587,6 +591,44 @@ static const struct venus_resources msm8996_res = { .fwname = "qcom/venus-4.2/venus.mbn", }; +static const struct freq_tbl msm8998_freq_table[] = { + { 1944000, 465000000 }, /* 4k UHD @ 60 (decode only) */ + { 972000, 465000000 }, /* 4k UHD @ 30 */ + { 489600, 360000000 }, /* 1080p @ 60 */ + { 244800, 186000000 }, /* 1080p @ 30 */ + { 108000, 100000000 }, /* 720p @ 30 */ +}; + +static const struct reg_val msm8998_reg_preset[] = { + { 0x80124, 0x00000003 }, + { 0x80550, 0x01111111 }, + { 0x80560, 0x01111111 }, + { 0x80568, 0x01111111 }, + { 0x80570, 0x01111111 }, + { 0x80580, 0x01111111 }, + { 0x80588, 0x01111111 }, + { 0xe2010, 0x00000000 }, +}; + +static const struct venus_resources msm8998_res = { + .freq_tbl = msm8998_freq_table, + .freq_tbl_size = ARRAY_SIZE(msm8998_freq_table), + .reg_tbl = msm8998_reg_preset, + .reg_tbl_size = ARRAY_SIZE(msm8998_reg_preset), + .clks = { "core", "iface", "bus", "mbus" }, + .clks_num = 4, + .vcodec0_clks = { "core" }, + .vcodec1_clks = { "core" }, + .vcodec_clks_num = 1, + .max_load = 2563200, + .hfi_version = HFI_VERSION_3XX, + .vmem_id = VIDC_RESOURCE_NONE, + .vmem_size = 0, + .vmem_addr = 0, + .dma_mask = 0xddc00000 - 1, + .fwname = "qcom/venus-4.4/venus.mbn", +}; + static const struct freq_tbl sdm660_freq_table[] = { { 979200, 518400000 }, { 489600, 441600000 }, @@ -893,6 +935,7 @@ static const struct venus_resources sc7280_res = { static const struct of_device_id venus_dt_match[] = { { .compatible = "qcom,msm8916-venus", .data = &msm8916_res, }, { .compatible = "qcom,msm8996-venus", .data = &msm8996_res, }, + { .compatible = "qcom,msm8998-venus", .data = &msm8998_res, }, { .compatible = "qcom,sdm660-venus", .data = &sdm660_res, }, { .compatible = "qcom,sdm845-venus", .data = &sdm845_res, }, { .compatible = "qcom,sdm845-venus-v2", .data = &sdm845_res_v2, }, diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 6a77de374454..55202b89e1b9 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -169,6 +169,7 @@ struct venus_format { * @core1_usage_count: usage counter for core1 * @root: debugfs root directory * @venus_ver: the venus firmware version + * @dump_core: a flag indicating that a core dump is required */ struct venus_core { void __iomem *base; @@ -232,6 +233,7 @@ struct venus_core { u32 minor; u32 rev; } venus_ver; + unsigned long dump_core; }; struct vdec_controls { diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 29130a9441e7..d12089370d91 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -1255,7 +1255,7 @@ static int vdec_stop_output(struct venus_inst *inst) break; case VENUS_DEC_STATE_INIT: case VENUS_DEC_STATE_CAPTURE_SETUP: - ret = hfi_session_flush(inst, HFI_FLUSH_INPUT, true); + ret = hfi_session_flush(inst, HFI_FLUSH_ALL, true); break; default: break; @@ -1747,6 +1747,7 @@ static int vdec_close(struct file *file) vdec_pm_get(inst); + cancel_work_sync(&inst->delayed_process_work); v4l2_m2m_ctx_release(inst->m2m_ctx); v4l2_m2m_release(inst->m2m_dev); vdec_ctrl_deinit(inst); diff --git a/drivers/media/platform/raspberrypi/Kconfig b/drivers/media/platform/raspberrypi/Kconfig new file mode 100644 index 000000000000..e928f979019e --- /dev/null +++ b/drivers/media/platform/raspberrypi/Kconfig @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only + +comment "Raspberry Pi media platform drivers" + +source "drivers/media/platform/raspberrypi/pisp_be/Kconfig" diff --git a/drivers/media/platform/raspberrypi/Makefile b/drivers/media/platform/raspberrypi/Makefile new file mode 100644 index 000000000000..c0d1a2dab486 --- /dev/null +++ b/drivers/media/platform/raspberrypi/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-y += pisp_be/ diff --git a/drivers/media/platform/raspberrypi/pisp_be/Kconfig b/drivers/media/platform/raspberrypi/pisp_be/Kconfig new file mode 100644 index 000000000000..38c0f8305d62 --- /dev/null +++ b/drivers/media/platform/raspberrypi/pisp_be/Kconfig @@ -0,0 +1,12 @@ +config VIDEO_RASPBERRYPI_PISP_BE + tristate "Raspberry Pi PiSP Backend (BE) ISP driver" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV + select VIDEO_V4L2_SUBDEV_API + select MEDIA_CONTROLLER + select VIDEOBUF2_DMA_CONTIG + help + Say Y here to enable support for the PiSP Backend (BE) ISP driver. + + To compile this driver as a module, choose M here. The module will be + called pisp-be. diff --git a/drivers/media/platform/raspberrypi/pisp_be/Makefile b/drivers/media/platform/raspberrypi/pisp_be/Makefile new file mode 100644 index 000000000000..a70bf5716824 --- /dev/null +++ b/drivers/media/platform/raspberrypi/pisp_be/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for Raspberry Pi PiSP Backend driver +# +pisp-be-objs := pisp_be.o +obj-$(CONFIG_VIDEO_RASPBERRYPI_PISP_BE) += pisp-be.o diff --git a/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c b/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c new file mode 100644 index 000000000000..65ff2382cffe --- /dev/null +++ b/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c @@ -0,0 +1,1797 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PiSP Back End driver. + * Copyright (c) 2021-2024 Raspberry Pi Limited. + * + */ +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/lockdep.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf2-dma-contig.h> +#include <media/videobuf2-vmalloc.h> + +#include <uapi/linux/media/raspberrypi/pisp_be_config.h> + +#include "pisp_be_formats.h" + +/* Maximum number of config buffers possible */ +#define PISP_BE_NUM_CONFIG_BUFFERS VB2_MAX_FRAME + +#define PISPBE_NAME "pispbe" + +/* Some ISP-BE registers */ +#define PISP_BE_VERSION_REG 0x0 +#define PISP_BE_CONTROL_REG 0x4 +#define PISP_BE_CONTROL_COPY_CONFIG BIT(1) +#define PISP_BE_CONTROL_QUEUE_JOB BIT(0) +#define PISP_BE_CONTROL_NUM_TILES(n) ((n) << 16) +#define PISP_BE_TILE_ADDR_LO_REG 0x8 +#define PISP_BE_TILE_ADDR_HI_REG 0xc +#define PISP_BE_STATUS_REG 0x10 +#define PISP_BE_STATUS_QUEUED BIT(0) +#define PISP_BE_BATCH_STATUS_REG 0x14 +#define PISP_BE_INTERRUPT_EN_REG 0x18 +#define PISP_BE_INTERRUPT_STATUS_REG 0x1c +#define PISP_BE_AXI_REG 0x20 +#define PISP_BE_CONFIG_BASE_REG 0x40 +#define PISP_BE_IO_ADDR_LOW(n) (PISP_BE_CONFIG_BASE_REG + 8 * (n)) +#define PISP_BE_IO_ADDR_HIGH(n) (PISP_BE_IO_ADDR_LOW((n)) + 4) +#define PISP_BE_GLOBAL_BAYER_ENABLE 0xb0 +#define PISP_BE_GLOBAL_RGB_ENABLE 0xb4 +#define N_HW_ADDRESSES 13 +#define N_HW_ENABLES 2 + +#define PISP_BE_VERSION_2712 0x02252700 +#define PISP_BE_VERSION_MINOR_BITS 0xf + +/* + * This maps our nodes onto the inputs/outputs of the actual PiSP Back End. + * Be wary of the word "OUTPUT" which is used ambiguously here. In a V4L2 + * context it means an input to the hardware (source image or metadata). + * Elsewhere it means an output from the hardware. + */ +enum pispbe_node_ids { + MAIN_INPUT_NODE, + TDN_INPUT_NODE, + STITCH_INPUT_NODE, + OUTPUT0_NODE, + OUTPUT1_NODE, + TDN_OUTPUT_NODE, + STITCH_OUTPUT_NODE, + CONFIG_NODE, + PISPBE_NUM_NODES +}; + +struct pispbe_node_description { + const char *ent_name; + enum v4l2_buf_type buf_type; + unsigned int caps; +}; + +static const struct pispbe_node_description node_desc[PISPBE_NUM_NODES] = { + /* MAIN_INPUT_NODE */ + { + .ent_name = PISPBE_NAME "-input", + .buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE, + }, + /* TDN_INPUT_NODE */ + { + .ent_name = PISPBE_NAME "-tdn_input", + .buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE, + }, + /* STITCH_INPUT_NODE */ + { + .ent_name = PISPBE_NAME "-stitch_input", + .buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE, + }, + /* OUTPUT0_NODE */ + { + .ent_name = PISPBE_NAME "-output0", + .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + .caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE, + }, + /* OUTPUT1_NODE */ + { + .ent_name = PISPBE_NAME "-output1", + .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + .caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE, + }, + /* TDN_OUTPUT_NODE */ + { + .ent_name = PISPBE_NAME "-tdn_output", + .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + .caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE, + }, + /* STITCH_OUTPUT_NODE */ + { + .ent_name = PISPBE_NAME "-stitch_output", + .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + .caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE, + }, + /* CONFIG_NODE */ + { + .ent_name = PISPBE_NAME "-config", + .buf_type = V4L2_BUF_TYPE_META_OUTPUT, + .caps = V4L2_CAP_META_OUTPUT, + } +}; + +#define NODE_DESC_IS_OUTPUT(desc) ( \ + ((desc)->buf_type == V4L2_BUF_TYPE_META_OUTPUT) || \ + ((desc)->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT) || \ + ((desc)->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)) + +#define NODE_IS_META(node) ( \ + ((node)->buf_type == V4L2_BUF_TYPE_META_OUTPUT)) +#define NODE_IS_OUTPUT(node) ( \ + ((node)->buf_type == V4L2_BUF_TYPE_META_OUTPUT) || \ + ((node)->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT) || \ + ((node)->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)) +#define NODE_IS_CAPTURE(node) ( \ + ((node)->buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE) || \ + ((node)->buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)) +#define NODE_IS_MPLANE(node) ( \ + ((node)->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) || \ + ((node)->buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)) + +/* + * Structure to describe a single node /dev/video<N> which represents a single + * input or output queue to the PiSP Back End device. + */ +struct pispbe_node { + unsigned int id; + int vfl_dir; + enum v4l2_buf_type buf_type; + struct video_device vfd; + struct media_pad pad; + struct media_intf_devnode *intf_devnode; + struct media_link *intf_link; + struct pispbe_dev *pispbe; + /* Video device lock */ + struct mutex node_lock; + /* vb2_queue lock */ + struct mutex queue_lock; + /* Protect pispbe_node->ready_queue and pispbe_buffer->ready_list */ + spinlock_t ready_lock; + struct list_head ready_queue; + struct vb2_queue queue; + struct v4l2_format format; + const struct pisp_be_format *pisp_format; +}; + +/* For logging only, use the entity name with "pispbe" and separator removed */ +#define NODE_NAME(node) \ + (node_desc[(node)->id].ent_name + sizeof(PISPBE_NAME)) + +/* Records details of the jobs currently running or queued on the h/w. */ +struct pispbe_job { + bool valid; + /* + * An array of buffer pointers - remember it's source buffers first, + * then captures, then metadata last. + */ + struct pispbe_buffer *buf[PISPBE_NUM_NODES]; +}; + +struct pispbe_hw_enables { + u32 bayer_enables; + u32 rgb_enables; +}; + +/* Records a job configuration and memory addresses. */ +struct pispbe_job_descriptor { + dma_addr_t hw_dma_addrs[N_HW_ADDRESSES]; + struct pisp_be_tiles_config *config; + struct pispbe_hw_enables hw_enables; + dma_addr_t tiles; +}; + +/* + * Structure representing the entire PiSP Back End device, comprising several + * nodes which share platform resources and a mutex for the actual HW. + */ +struct pispbe_dev { + struct device *dev; + struct pispbe_dev *pispbe; + struct pisp_be_tiles_config *config; + void __iomem *be_reg_base; + struct clk *clk; + struct v4l2_device v4l2_dev; + struct v4l2_subdev sd; + struct media_device mdev; + struct media_pad pad[PISPBE_NUM_NODES]; /* output pads first */ + struct pispbe_node node[PISPBE_NUM_NODES]; + dma_addr_t config_dma_addr; + unsigned int sequence; + u32 streaming_map; + struct pispbe_job queued_job, running_job; + spinlock_t hw_lock; /* protects "hw_busy" flag and streaming_map */ + bool hw_busy; /* non-zero if a job is queued or is being started */ + int irq; + u32 hw_version; + u8 done, started; +}; + +static u32 pispbe_rd(struct pispbe_dev *pispbe, unsigned int offset) +{ + return readl(pispbe->be_reg_base + offset); +} + +static void pispbe_wr(struct pispbe_dev *pispbe, unsigned int offset, u32 val) +{ + writel(val, pispbe->be_reg_base + offset); +} + +/* + * Queue a job to the h/w. If the h/w is idle it will begin immediately. + * Caller must ensure it is "safe to queue", i.e. we don't already have a + * queued, unstarted job. + */ +static void pispbe_queue_job(struct pispbe_dev *pispbe, + struct pispbe_job_descriptor *job) +{ + unsigned int begin, end; + + if (pispbe_rd(pispbe, PISP_BE_STATUS_REG) & PISP_BE_STATUS_QUEUED) + dev_err(pispbe->dev, "ERROR: not safe to queue new job!\n"); + + /* + * Write configuration to hardware. DMA addresses and enable flags + * are passed separately, because the driver needs to sanitize them, + * and we don't want to modify (or be vulnerable to modifications of) + * the mmap'd buffer. + */ + for (unsigned int u = 0; u < N_HW_ADDRESSES; ++u) { + pispbe_wr(pispbe, PISP_BE_IO_ADDR_LOW(u), + lower_32_bits(job->hw_dma_addrs[u])); + pispbe_wr(pispbe, PISP_BE_IO_ADDR_HIGH(u), + upper_32_bits(job->hw_dma_addrs[u])); + } + pispbe_wr(pispbe, PISP_BE_GLOBAL_BAYER_ENABLE, + job->hw_enables.bayer_enables); + pispbe_wr(pispbe, PISP_BE_GLOBAL_RGB_ENABLE, + job->hw_enables.rgb_enables); + + /* Everything else is as supplied by the user. */ + begin = offsetof(struct pisp_be_config, global.bayer_order) / + sizeof(u32); + end = sizeof(struct pisp_be_config) / sizeof(u32); + for (unsigned int u = begin; u < end; u++) + pispbe_wr(pispbe, PISP_BE_CONFIG_BASE_REG + sizeof(u32) * u, + ((u32 *)job->config)[u]); + + /* Read back the addresses -- an error here could be fatal */ + for (unsigned int u = 0; u < N_HW_ADDRESSES; ++u) { + unsigned int offset = PISP_BE_IO_ADDR_LOW(u); + u64 along = pispbe_rd(pispbe, offset); + + along += ((u64)pispbe_rd(pispbe, offset + 4)) << 32; + if (along != (u64)(job->hw_dma_addrs[u])) { + dev_dbg(pispbe->dev, + "ISP BE config error: check if ISP RAMs enabled?\n"); + return; + } + } + + /* + * Write tile pointer to hardware. The IOMMU should prevent + * out-of-bounds offsets reaching non-ISP buffers. + */ + pispbe_wr(pispbe, PISP_BE_TILE_ADDR_LO_REG, lower_32_bits(job->tiles)); + pispbe_wr(pispbe, PISP_BE_TILE_ADDR_HI_REG, upper_32_bits(job->tiles)); + + /* Enqueue the job */ + pispbe_wr(pispbe, PISP_BE_CONTROL_REG, + PISP_BE_CONTROL_COPY_CONFIG | PISP_BE_CONTROL_QUEUE_JOB | + PISP_BE_CONTROL_NUM_TILES(job->config->num_tiles)); +} + +struct pispbe_buffer { + struct vb2_v4l2_buffer vb; + struct list_head ready_list; + unsigned int config_index; +}; + +static int pispbe_get_planes_addr(dma_addr_t addr[3], struct pispbe_buffer *buf, + struct pispbe_node *node) +{ + unsigned int num_planes = node->format.fmt.pix_mp.num_planes; + unsigned int plane_factor = 0; + unsigned int size; + unsigned int p; + + if (!buf || !node->pisp_format) + return 0; + + /* + * Determine the base plane size. This will not be the same + * as node->format.fmt.pix_mp.plane_fmt[0].sizeimage for a single + * plane buffer in an mplane format. + */ + size = node->format.fmt.pix_mp.plane_fmt[0].bytesperline * + node->format.fmt.pix_mp.height; + + for (p = 0; p < num_planes && p < PISPBE_MAX_PLANES; p++) { + addr[p] = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, p); + plane_factor += node->pisp_format->plane_factor[p]; + } + + for (; p < PISPBE_MAX_PLANES && node->pisp_format->plane_factor[p]; p++) { + /* + * Calculate the address offset of this plane as needed + * by the hardware. This is specifically for non-mplane + * buffer formats, where there are 3 image planes, e.g. + * for the V4L2_PIX_FMT_YUV420 format. + */ + addr[p] = addr[0] + ((size * plane_factor) >> 3); + plane_factor += node->pisp_format->plane_factor[p]; + } + + return num_planes; +} + +static dma_addr_t pispbe_get_addr(struct pispbe_buffer *buf) +{ + if (buf) + return vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + + return 0; +} + +static void pispbe_xlate_addrs(struct pispbe_dev *pispbe, + struct pispbe_job_descriptor *job, + struct pispbe_buffer *buf[PISPBE_NUM_NODES]) +{ + struct pispbe_hw_enables *hw_en = &job->hw_enables; + struct pisp_be_tiles_config *config = job->config; + dma_addr_t *addrs = job->hw_dma_addrs; + int ret; + + /* Take a copy of the "enable" bitmaps so we can modify them. */ + hw_en->bayer_enables = config->config.global.bayer_enables; + hw_en->rgb_enables = config->config.global.rgb_enables; + + /* + * Main input first. There are 3 address pointers, corresponding to up + * to 3 planes. + */ + ret = pispbe_get_planes_addr(addrs, buf[MAIN_INPUT_NODE], + &pispbe->node[MAIN_INPUT_NODE]); + if (ret <= 0) { + /* + * This shouldn't happen; pispbe_schedule_internal should insist + * on an input. + */ + dev_warn(pispbe->dev, "ISP-BE missing input\n"); + hw_en->bayer_enables = 0; + hw_en->rgb_enables = 0; + return; + } + + /* + * Now TDN/Stitch inputs and outputs. These are single-plane and only + * used with Bayer input. Input enables must match the requirements + * of the processing stages, otherwise the hardware can lock up! + */ + if (hw_en->bayer_enables & PISP_BE_BAYER_ENABLE_INPUT) { + addrs[3] = pispbe_get_addr(buf[TDN_INPUT_NODE]); + if (addrs[3] == 0 || + !(hw_en->bayer_enables & PISP_BE_BAYER_ENABLE_TDN_INPUT) || + !(hw_en->bayer_enables & PISP_BE_BAYER_ENABLE_TDN) || + (config->config.tdn.reset & 1)) { + hw_en->bayer_enables &= + ~(PISP_BE_BAYER_ENABLE_TDN_INPUT | + PISP_BE_BAYER_ENABLE_TDN_DECOMPRESS); + if (!(config->config.tdn.reset & 1)) + hw_en->bayer_enables &= + ~PISP_BE_BAYER_ENABLE_TDN; + } + + addrs[4] = pispbe_get_addr(buf[STITCH_INPUT_NODE]); + if (addrs[4] == 0 || + !(hw_en->bayer_enables & PISP_BE_BAYER_ENABLE_STITCH_INPUT) || + !(hw_en->bayer_enables & PISP_BE_BAYER_ENABLE_STITCH)) { + hw_en->bayer_enables &= + ~(PISP_BE_BAYER_ENABLE_STITCH_INPUT | + PISP_BE_BAYER_ENABLE_STITCH_DECOMPRESS | + PISP_BE_BAYER_ENABLE_STITCH); + } + + addrs[5] = pispbe_get_addr(buf[TDN_OUTPUT_NODE]); + if (addrs[5] == 0) + hw_en->bayer_enables &= + ~(PISP_BE_BAYER_ENABLE_TDN_COMPRESS | + PISP_BE_BAYER_ENABLE_TDN_OUTPUT); + + addrs[6] = pispbe_get_addr(buf[STITCH_OUTPUT_NODE]); + if (addrs[6] == 0) + hw_en->bayer_enables &= + ~(PISP_BE_BAYER_ENABLE_STITCH_COMPRESS | + PISP_BE_BAYER_ENABLE_STITCH_OUTPUT); + } else { + /* No Bayer input? Disable entire Bayer pipe (else lockup) */ + hw_en->bayer_enables = 0; + } + + /* Main image output channels. */ + for (unsigned int i = 0; i < PISP_BACK_END_NUM_OUTPUTS; i++) { + ret = pispbe_get_planes_addr(addrs + 7 + 3 * i, + buf[OUTPUT0_NODE + i], + &pispbe->node[OUTPUT0_NODE + i]); + if (ret <= 0) + hw_en->rgb_enables &= ~(PISP_BE_RGB_ENABLE_OUTPUT0 << i); + } +} + +/* + * Prepare a job description to be submitted to the HW. + * + * To schedule a job, we need all streaming nodes (apart from Output0, + * Output1, Tdn and Stitch) to have a buffer ready, which must + * include at least a config buffer and a main input image. + * + * For Output0, Output1, Tdn and Stitch, a buffer only needs to be + * available if the blocks are enabled in the config. + * + * Needs to be called with hw_lock held. + * + * Returns 0 if a job has been successfully prepared, < 0 otherwise. + */ +static int pispbe_prepare_job(struct pispbe_dev *pispbe, + struct pispbe_job_descriptor *job) +{ + struct pispbe_buffer *buf[PISPBE_NUM_NODES] = {}; + unsigned int config_index; + struct pispbe_node *node; + unsigned long flags; + + lockdep_assert_held(&pispbe->hw_lock); + + memset(job, 0, sizeof(struct pispbe_job_descriptor)); + + if (((BIT(CONFIG_NODE) | BIT(MAIN_INPUT_NODE)) & + pispbe->streaming_map) != + (BIT(CONFIG_NODE) | BIT(MAIN_INPUT_NODE))) + return -ENODEV; + + node = &pispbe->node[CONFIG_NODE]; + spin_lock_irqsave(&node->ready_lock, flags); + buf[CONFIG_NODE] = list_first_entry_or_null(&node->ready_queue, + struct pispbe_buffer, + ready_list); + if (buf[CONFIG_NODE]) { + list_del(&buf[CONFIG_NODE]->ready_list); + pispbe->queued_job.buf[CONFIG_NODE] = buf[CONFIG_NODE]; + } + spin_unlock_irqrestore(&node->ready_lock, flags); + + /* Exit early if no config buffer has been queued. */ + if (!buf[CONFIG_NODE]) + return -ENODEV; + + config_index = buf[CONFIG_NODE]->vb.vb2_buf.index; + job->config = &pispbe->config[config_index]; + job->tiles = pispbe->config_dma_addr + + config_index * sizeof(struct pisp_be_tiles_config) + + offsetof(struct pisp_be_tiles_config, tiles); + + /* remember: srcimages, captures then metadata */ + for (unsigned int i = 0; i < PISPBE_NUM_NODES; i++) { + unsigned int bayer_en = + job->config->config.global.bayer_enables; + unsigned int rgb_en = + job->config->config.global.rgb_enables; + bool ignore_buffers = false; + + /* Config node is handled outside the loop above. */ + if (i == CONFIG_NODE) + continue; + + buf[i] = NULL; + if (!(pispbe->streaming_map & BIT(i))) + continue; + + if ((!(rgb_en & PISP_BE_RGB_ENABLE_OUTPUT0) && + i == OUTPUT0_NODE) || + (!(rgb_en & PISP_BE_RGB_ENABLE_OUTPUT1) && + i == OUTPUT1_NODE) || + (!(bayer_en & PISP_BE_BAYER_ENABLE_TDN_INPUT) && + i == TDN_INPUT_NODE) || + (!(bayer_en & PISP_BE_BAYER_ENABLE_TDN_OUTPUT) && + i == TDN_OUTPUT_NODE) || + (!(bayer_en & PISP_BE_BAYER_ENABLE_STITCH_INPUT) && + i == STITCH_INPUT_NODE) || + (!(bayer_en & PISP_BE_BAYER_ENABLE_STITCH_OUTPUT) && + i == STITCH_OUTPUT_NODE)) { + /* + * Ignore Output0/Output1/Tdn/Stitch buffer check if the + * global enables aren't set for these blocks. If a + * buffer has been provided, we dequeue it back to the + * user with the other in-use buffers. + */ + ignore_buffers = true; + } + + node = &pispbe->node[i]; + + /* Pull a buffer from each V4L2 queue to form the queued job */ + spin_lock_irqsave(&node->ready_lock, flags); + buf[i] = list_first_entry_or_null(&node->ready_queue, + struct pispbe_buffer, + ready_list); + if (buf[i]) { + list_del(&buf[i]->ready_list); + pispbe->queued_job.buf[i] = buf[i]; + } + spin_unlock_irqrestore(&node->ready_lock, flags); + + if (!buf[i] && !ignore_buffers) + goto err_return_buffers; + } + + pispbe->queued_job.valid = true; + + /* Convert buffers to DMA addresses for the hardware */ + pispbe_xlate_addrs(pispbe, job, buf); + + return 0; + +err_return_buffers: + for (unsigned int i = 0; i < PISPBE_NUM_NODES; i++) { + struct pispbe_node *n = &pispbe->node[i]; + + if (!buf[i]) + continue; + + /* Return the buffer to the ready_list queue */ + spin_lock_irqsave(&n->ready_lock, flags); + list_add(&buf[i]->ready_list, &n->ready_queue); + spin_unlock_irqrestore(&n->ready_lock, flags); + } + + memset(&pispbe->queued_job, 0, sizeof(pispbe->queued_job)); + + return -ENODEV; +} + +static void pispbe_schedule(struct pispbe_dev *pispbe, bool clear_hw_busy) +{ + struct pispbe_job_descriptor job; + unsigned long flags; + int ret; + + spin_lock_irqsave(&pispbe->hw_lock, flags); + + if (clear_hw_busy) + pispbe->hw_busy = false; + + if (pispbe->hw_busy) + goto unlock_and_return; + + ret = pispbe_prepare_job(pispbe, &job); + if (ret) + goto unlock_and_return; + + /* + * We can kick the job off without the hw_lock, as this can + * never run again until hw_busy is cleared, which will happen + * only when the following job has been queued and an interrupt + * is rised. + */ + pispbe->hw_busy = true; + spin_unlock_irqrestore(&pispbe->hw_lock, flags); + + if (job.config->num_tiles <= 0 || + job.config->num_tiles > PISP_BACK_END_NUM_TILES || + !((job.hw_enables.bayer_enables | job.hw_enables.rgb_enables) & + PISP_BE_BAYER_ENABLE_INPUT)) { + /* + * Bad job. We can't let it proceed as it could lock up + * the hardware, or worse! + * + * For now, just force num_tiles to 0, which causes the + * H/W to do something bizarre but survivable. It + * increments (started,done) counters by more than 1, + * but we seem to survive... + */ + dev_dbg(pispbe->dev, "Bad job: invalid number of tiles: %u\n", + job.config->num_tiles); + job.config->num_tiles = 0; + } + + pispbe_queue_job(pispbe, &job); + + return; + +unlock_and_return: + /* No job has been queued, just release the lock and return. */ + spin_unlock_irqrestore(&pispbe->hw_lock, flags); +} + +static void pispbe_isr_jobdone(struct pispbe_dev *pispbe, + struct pispbe_job *job) +{ + struct pispbe_buffer **buf = job->buf; + u64 ts = ktime_get_ns(); + + for (unsigned int i = 0; i < PISPBE_NUM_NODES; i++) { + if (buf[i]) { + buf[i]->vb.vb2_buf.timestamp = ts; + buf[i]->vb.sequence = pispbe->sequence; + vb2_buffer_done(&buf[i]->vb.vb2_buf, + VB2_BUF_STATE_DONE); + } + } + + pispbe->sequence++; +} + +static irqreturn_t pispbe_isr(int irq, void *dev) +{ + struct pispbe_dev *pispbe = (struct pispbe_dev *)dev; + bool can_queue_another = false; + u8 started, done; + u32 u; + + u = pispbe_rd(pispbe, PISP_BE_INTERRUPT_STATUS_REG); + if (u == 0) + return IRQ_NONE; + + pispbe_wr(pispbe, PISP_BE_INTERRUPT_STATUS_REG, u); + u = pispbe_rd(pispbe, PISP_BE_BATCH_STATUS_REG); + done = (uint8_t)u; + started = (uint8_t)(u >> 8); + + /* + * Be aware that done can go up by 2 and started by 1 when: a job that + * we previously saw "start" now finishes, and we then queued a new job + * which we see both start and finish "simultaneously". + */ + if (pispbe->running_job.valid && pispbe->done != done) { + pispbe_isr_jobdone(pispbe, &pispbe->running_job); + memset(&pispbe->running_job, 0, sizeof(pispbe->running_job)); + pispbe->done++; + } + + if (pispbe->started != started) { + pispbe->started++; + can_queue_another = 1; + + if (pispbe->done != done && pispbe->queued_job.valid) { + pispbe_isr_jobdone(pispbe, &pispbe->queued_job); + pispbe->done++; + } else { + pispbe->running_job = pispbe->queued_job; + } + + memset(&pispbe->queued_job, 0, sizeof(pispbe->queued_job)); + } + + if (pispbe->done != done || pispbe->started != started) { + dev_dbg(pispbe->dev, + "Job counters not matching: done = %u, expected %u - started = %u, expected %u\n", + pispbe->done, done, pispbe->started, started); + pispbe->started = started; + pispbe->done = done; + } + + /* check if there's more to do before going to sleep */ + pispbe_schedule(pispbe, can_queue_another); + + return IRQ_HANDLED; +} + +static int pisp_be_validate_config(struct pispbe_dev *pispbe, + struct pisp_be_tiles_config *config) +{ + u32 bayer_enables = config->config.global.bayer_enables; + u32 rgb_enables = config->config.global.rgb_enables; + struct device *dev = pispbe->dev; + struct v4l2_format *fmt; + unsigned int bpl, size; + + if (!(bayer_enables & PISP_BE_BAYER_ENABLE_INPUT) == + !(rgb_enables & PISP_BE_RGB_ENABLE_INPUT)) { + dev_dbg(dev, "%s: Not one input enabled\n", __func__); + return -EIO; + } + + /* Ensure output config strides and buffer sizes match the V4L2 formats. */ + fmt = &pispbe->node[TDN_OUTPUT_NODE].format; + if (bayer_enables & PISP_BE_BAYER_ENABLE_TDN_OUTPUT) { + bpl = config->config.tdn_output_format.stride; + size = bpl * config->config.tdn_output_format.height; + + if (fmt->fmt.pix_mp.plane_fmt[0].bytesperline < bpl) { + dev_dbg(dev, "%s: bpl mismatch on tdn_output\n", + __func__); + return -EINVAL; + } + + if (fmt->fmt.pix_mp.plane_fmt[0].sizeimage < size) { + dev_dbg(dev, "%s: size mismatch on tdn_output\n", + __func__); + return -EINVAL; + } + } + + fmt = &pispbe->node[STITCH_OUTPUT_NODE].format; + if (bayer_enables & PISP_BE_BAYER_ENABLE_STITCH_OUTPUT) { + bpl = config->config.stitch_output_format.stride; + size = bpl * config->config.stitch_output_format.height; + + if (fmt->fmt.pix_mp.plane_fmt[0].bytesperline < bpl) { + dev_dbg(dev, "%s: bpl mismatch on stitch_output\n", + __func__); + return -EINVAL; + } + + if (fmt->fmt.pix_mp.plane_fmt[0].sizeimage < size) { + dev_dbg(dev, "%s: size mismatch on stitch_output\n", + __func__); + return -EINVAL; + } + } + + for (unsigned int j = 0; j < PISP_BACK_END_NUM_OUTPUTS; j++) { + if (!(rgb_enables & PISP_BE_RGB_ENABLE_OUTPUT(j))) + continue; + + if (config->config.output_format[j].image.format & + PISP_IMAGE_FORMAT_WALLPAPER_ROLL) + continue; /* TODO: Size checks for wallpaper formats */ + + fmt = &pispbe->node[OUTPUT0_NODE + j].format; + for (unsigned int i = 0; i < fmt->fmt.pix_mp.num_planes; i++) { + bpl = !i ? config->config.output_format[j].image.stride + : config->config.output_format[j].image.stride2; + size = bpl * config->config.output_format[j].image.height; + + if (config->config.output_format[j].image.format & + PISP_IMAGE_FORMAT_SAMPLING_420) + size >>= 1; + + if (fmt->fmt.pix_mp.plane_fmt[i].bytesperline < bpl) { + dev_dbg(dev, "%s: bpl mismatch on output %d\n", + __func__, j); + return -EINVAL; + } + + if (fmt->fmt.pix_mp.plane_fmt[i].sizeimage < size) { + dev_dbg(dev, "%s: size mismatch on output\n", + __func__); + return -EINVAL; + } + } + } + + return 0; +} + +static int pispbe_node_queue_setup(struct vb2_queue *q, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct pispbe_node *node = vb2_get_drv_priv(q); + struct pispbe_dev *pispbe = node->pispbe; + unsigned int num_planes = NODE_IS_MPLANE(node) ? + node->format.fmt.pix_mp.num_planes : 1; + + if (*nplanes) { + if (*nplanes != num_planes) + return -EINVAL; + + for (unsigned int i = 0; i < *nplanes; i++) { + unsigned int size = NODE_IS_MPLANE(node) ? + node->format.fmt.pix_mp.plane_fmt[i].sizeimage : + node->format.fmt.meta.buffersize; + + if (sizes[i] < size) + return -EINVAL; + } + + return 0; + } + + *nplanes = num_planes; + for (unsigned int i = 0; i < *nplanes; i++) { + unsigned int size = NODE_IS_MPLANE(node) ? + node->format.fmt.pix_mp.plane_fmt[i].sizeimage : + node->format.fmt.meta.buffersize; + sizes[i] = size; + } + + dev_dbg(pispbe->dev, + "Image (or metadata) size %u, nbuffers %u for node %s\n", + sizes[0], *nbuffers, NODE_NAME(node)); + + return 0; +} + +static int pispbe_node_buffer_prepare(struct vb2_buffer *vb) +{ + struct pispbe_node *node = vb2_get_drv_priv(vb->vb2_queue); + struct pispbe_dev *pispbe = node->pispbe; + unsigned int num_planes = NODE_IS_MPLANE(node) ? + node->format.fmt.pix_mp.num_planes : 1; + + for (unsigned int i = 0; i < num_planes; i++) { + unsigned long size = NODE_IS_MPLANE(node) ? + node->format.fmt.pix_mp.plane_fmt[i].sizeimage : + node->format.fmt.meta.buffersize; + + if (vb2_plane_size(vb, i) < size) { + dev_dbg(pispbe->dev, + "data will not fit into plane %d (%lu < %lu)\n", + i, vb2_plane_size(vb, i), size); + return -EINVAL; + } + + vb2_set_plane_payload(vb, i, size); + } + + if (node->id == CONFIG_NODE) { + void *dst = &node->pispbe->config[vb->index]; + void *src = vb2_plane_vaddr(vb, 0); + + memcpy(dst, src, sizeof(struct pisp_be_tiles_config)); + + return pisp_be_validate_config(pispbe, dst); + } + + return 0; +} + +static void pispbe_node_buffer_queue(struct vb2_buffer *buf) +{ + struct vb2_v4l2_buffer *vbuf = + container_of(buf, struct vb2_v4l2_buffer, vb2_buf); + struct pispbe_buffer *buffer = + container_of(vbuf, struct pispbe_buffer, vb); + struct pispbe_node *node = vb2_get_drv_priv(buf->vb2_queue); + struct pispbe_dev *pispbe = node->pispbe; + unsigned long flags; + + dev_dbg(pispbe->dev, "%s: for node %s\n", __func__, NODE_NAME(node)); + spin_lock_irqsave(&node->ready_lock, flags); + list_add_tail(&buffer->ready_list, &node->ready_queue); + spin_unlock_irqrestore(&node->ready_lock, flags); + + /* + * Every time we add a buffer, check if there's now some work for the hw + * to do. + */ + pispbe_schedule(pispbe, false); +} + +static int pispbe_node_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct pispbe_node *node = vb2_get_drv_priv(q); + struct pispbe_dev *pispbe = node->pispbe; + struct pispbe_buffer *buf, *tmp; + unsigned long flags; + int ret; + + ret = pm_runtime_resume_and_get(pispbe->dev); + if (ret < 0) + goto err_return_buffers; + + spin_lock_irqsave(&pispbe->hw_lock, flags); + node->pispbe->streaming_map |= BIT(node->id); + node->pispbe->sequence = 0; + spin_unlock_irqrestore(&pispbe->hw_lock, flags); + + dev_dbg(pispbe->dev, "%s: for node %s (count %u)\n", + __func__, NODE_NAME(node), count); + dev_dbg(pispbe->dev, "Nodes streaming now 0x%x\n", + node->pispbe->streaming_map); + + /* Maybe we're ready to run. */ + pispbe_schedule(pispbe, false); + + return 0; + +err_return_buffers: + spin_lock_irqsave(&pispbe->hw_lock, flags); + list_for_each_entry_safe(buf, tmp, &node->ready_queue, ready_list) { + list_del(&buf->ready_list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); + } + spin_unlock_irqrestore(&pispbe->hw_lock, flags); + + return ret; +} + +static void pispbe_node_stop_streaming(struct vb2_queue *q) +{ + struct pispbe_node *node = vb2_get_drv_priv(q); + struct pispbe_dev *pispbe = node->pispbe; + struct pispbe_buffer *buf; + unsigned long flags; + + /* + * Now this is a bit awkward. In a simple M2M device we could just wait + * for all queued jobs to complete, but here there's a risk that a + * partial set of buffers was queued and cannot be run. For now, just + * cancel all buffers stuck in the "ready queue", then wait for any + * running job. + * + * This may return buffers out of order. + */ + dev_dbg(pispbe->dev, "%s: for node %s\n", __func__, NODE_NAME(node)); + spin_lock_irqsave(&pispbe->hw_lock, flags); + do { + unsigned long flags1; + + spin_lock_irqsave(&node->ready_lock, flags1); + buf = list_first_entry_or_null(&node->ready_queue, + struct pispbe_buffer, + ready_list); + if (buf) { + list_del(&buf->ready_list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } + spin_unlock_irqrestore(&node->ready_lock, flags1); + } while (buf); + spin_unlock_irqrestore(&pispbe->hw_lock, flags); + + vb2_wait_for_all_buffers(&node->queue); + + spin_lock_irqsave(&pispbe->hw_lock, flags); + pispbe->streaming_map &= ~BIT(node->id); + spin_unlock_irqrestore(&pispbe->hw_lock, flags); + + pm_runtime_mark_last_busy(pispbe->dev); + pm_runtime_put_autosuspend(pispbe->dev); + + dev_dbg(pispbe->dev, "Nodes streaming now 0x%x\n", + pispbe->streaming_map); +} + +static const struct vb2_ops pispbe_node_queue_ops = { + .queue_setup = pispbe_node_queue_setup, + .buf_prepare = pispbe_node_buffer_prepare, + .buf_queue = pispbe_node_buffer_queue, + .start_streaming = pispbe_node_start_streaming, + .stop_streaming = pispbe_node_stop_streaming, +}; + +static const struct v4l2_file_operations pispbe_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap +}; + +static int pispbe_node_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct pispbe_node *node = video_drvdata(file); + struct pispbe_dev *pispbe = node->pispbe; + + strscpy(cap->driver, PISPBE_NAME, sizeof(cap->driver)); + strscpy(cap->card, PISPBE_NAME, sizeof(cap->card)); + + dev_dbg(pispbe->dev, "Caps for node %s: %x and %x (dev %x)\n", + NODE_NAME(node), cap->capabilities, cap->device_caps, + node->vfd.device_caps); + + return 0; +} + +static int pispbe_node_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct pispbe_node *node = video_drvdata(file); + struct pispbe_dev *pispbe = node->pispbe; + + if (!NODE_IS_CAPTURE(node) || NODE_IS_META(node)) { + dev_dbg(pispbe->dev, + "Cannot get capture fmt for output node %s\n", + NODE_NAME(node)); + return -EINVAL; + } + + *f = node->format; + dev_dbg(pispbe->dev, "Get capture format for node %s\n", + NODE_NAME(node)); + + return 0; +} + +static int pispbe_node_g_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct pispbe_node *node = video_drvdata(file); + struct pispbe_dev *pispbe = node->pispbe; + + if (NODE_IS_CAPTURE(node) || NODE_IS_META(node)) { + dev_dbg(pispbe->dev, + "Cannot get capture fmt for output node %s\n", + NODE_NAME(node)); + return -EINVAL; + } + + *f = node->format; + dev_dbg(pispbe->dev, "Get output format for node %s\n", + NODE_NAME(node)); + + return 0; +} + +static int pispbe_node_g_fmt_meta_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct pispbe_node *node = video_drvdata(file); + struct pispbe_dev *pispbe = node->pispbe; + + if (!NODE_IS_META(node) || NODE_IS_CAPTURE(node)) { + dev_dbg(pispbe->dev, + "Cannot get capture fmt for meta output node %s\n", + NODE_NAME(node)); + return -EINVAL; + } + + *f = node->format; + dev_dbg(pispbe->dev, "Get output format for meta node %s\n", + NODE_NAME(node)); + + return 0; +} + +static const struct pisp_be_format *pispbe_find_fmt(unsigned int fourcc) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(supported_formats); i++) { + if (supported_formats[i].fourcc == fourcc) + return &supported_formats[i]; + } + + return NULL; +} + +static void pispbe_set_plane_params(struct v4l2_format *f, + const struct pisp_be_format *fmt) +{ + unsigned int nplanes = f->fmt.pix_mp.num_planes; + unsigned int total_plane_factor = 0; + + for (unsigned int i = 0; i < PISPBE_MAX_PLANES; i++) + total_plane_factor += fmt->plane_factor[i]; + + for (unsigned int i = 0; i < nplanes; i++) { + struct v4l2_plane_pix_format *p = &f->fmt.pix_mp.plane_fmt[i]; + unsigned int bpl, plane_size; + + bpl = (f->fmt.pix_mp.width * fmt->bit_depth) >> 3; + bpl = ALIGN(max(p->bytesperline, bpl), fmt->align); + + plane_size = bpl * f->fmt.pix_mp.height * + (nplanes > 1 ? fmt->plane_factor[i] : total_plane_factor); + /* + * The shift is to divide out the plane_factor fixed point + * scaling of 8. + */ + plane_size = max(p->sizeimage, plane_size >> 3); + + p->bytesperline = bpl; + p->sizeimage = plane_size; + } +} + +static void pispbe_try_format(struct v4l2_format *f, struct pispbe_node *node) +{ + struct pispbe_dev *pispbe = node->pispbe; + u32 pixfmt = f->fmt.pix_mp.pixelformat; + const struct pisp_be_format *fmt; + bool is_rgb; + + dev_dbg(pispbe->dev, + "%s: [%s] req %ux%u %p4cc, planes %d\n", + __func__, NODE_NAME(node), f->fmt.pix_mp.width, + f->fmt.pix_mp.height, &pixfmt, + f->fmt.pix_mp.num_planes); + + fmt = pispbe_find_fmt(pixfmt); + if (!fmt) { + dev_dbg(pispbe->dev, + "%s: [%s] Format not found, defaulting to YUV420\n", + __func__, NODE_NAME(node)); + fmt = pispbe_find_fmt(V4L2_PIX_FMT_YUV420); + } + + f->fmt.pix_mp.pixelformat = fmt->fourcc; + f->fmt.pix_mp.num_planes = fmt->num_planes; + f->fmt.pix_mp.field = V4L2_FIELD_NONE; + f->fmt.pix_mp.width = max(min(f->fmt.pix_mp.width, 65536u), + PISP_BACK_END_MIN_TILE_WIDTH); + f->fmt.pix_mp.height = max(min(f->fmt.pix_mp.height, 65536u), + PISP_BACK_END_MIN_TILE_HEIGHT); + + /* + * Fill in the actual colour space when the requested one was + * not supported. This also catches the case when the "default" + * colour space was requested (as that's never in the mask). + */ + if (!(V4L2_COLORSPACE_MASK(f->fmt.pix_mp.colorspace) & + fmt->colorspace_mask)) + f->fmt.pix_mp.colorspace = fmt->colorspace_default; + + /* In all cases, we only support the defaults for these: */ + f->fmt.pix_mp.ycbcr_enc = + V4L2_MAP_YCBCR_ENC_DEFAULT(f->fmt.pix_mp.colorspace); + f->fmt.pix_mp.xfer_func = + V4L2_MAP_XFER_FUNC_DEFAULT(f->fmt.pix_mp.colorspace); + + is_rgb = f->fmt.pix_mp.colorspace == V4L2_COLORSPACE_SRGB; + f->fmt.pix_mp.quantization = + V4L2_MAP_QUANTIZATION_DEFAULT(is_rgb, f->fmt.pix_mp.colorspace, + f->fmt.pix_mp.ycbcr_enc); + + /* Set plane size and bytes/line for each plane. */ + pispbe_set_plane_params(f, fmt); + + for (unsigned int i = 0; i < f->fmt.pix_mp.num_planes; i++) { + dev_dbg(pispbe->dev, + "%s: [%s] calc plane %d, %ux%u, depth %u, bpl %u size %u\n", + __func__, NODE_NAME(node), i, f->fmt.pix_mp.width, + f->fmt.pix_mp.height, fmt->bit_depth, + f->fmt.pix_mp.plane_fmt[i].bytesperline, + f->fmt.pix_mp.plane_fmt[i].sizeimage); + } +} + +static int pispbe_node_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct pispbe_node *node = video_drvdata(file); + struct pispbe_dev *pispbe = node->pispbe; + + if (!NODE_IS_CAPTURE(node) || NODE_IS_META(node)) { + dev_dbg(pispbe->dev, + "Cannot set capture fmt for output node %s\n", + NODE_NAME(node)); + return -EINVAL; + } + + pispbe_try_format(f, node); + + return 0; +} + +static int pispbe_node_try_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct pispbe_node *node = video_drvdata(file); + struct pispbe_dev *pispbe = node->pispbe; + + if (!NODE_IS_OUTPUT(node) || NODE_IS_META(node)) { + dev_dbg(pispbe->dev, + "Cannot set capture fmt for output node %s\n", + NODE_NAME(node)); + return -EINVAL; + } + + pispbe_try_format(f, node); + + return 0; +} + +static int pispbe_node_try_fmt_meta_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct pispbe_node *node = video_drvdata(file); + struct pispbe_dev *pispbe = node->pispbe; + + if (!NODE_IS_META(node) || NODE_IS_CAPTURE(node)) { + dev_dbg(pispbe->dev, + "Cannot set capture fmt for meta output node %s\n", + NODE_NAME(node)); + return -EINVAL; + } + + f->fmt.meta.dataformat = V4L2_META_FMT_RPI_BE_CFG; + f->fmt.meta.buffersize = sizeof(struct pisp_be_tiles_config); + + return 0; +} + +static int pispbe_node_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct pispbe_node *node = video_drvdata(file); + struct pispbe_dev *pispbe = node->pispbe; + int ret; + + ret = pispbe_node_try_fmt_vid_cap(file, priv, f); + if (ret < 0) + return ret; + + if (vb2_is_busy(&node->queue)) + return -EBUSY; + + node->format = *f; + node->pisp_format = pispbe_find_fmt(f->fmt.pix_mp.pixelformat); + + dev_dbg(pispbe->dev, "Set capture format for node %s to %p4cc\n", + NODE_NAME(node), &f->fmt.pix_mp.pixelformat); + + return 0; +} + +static int pispbe_node_s_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct pispbe_node *node = video_drvdata(file); + struct pispbe_dev *pispbe = node->pispbe; + int ret; + + ret = pispbe_node_try_fmt_vid_out(file, priv, f); + if (ret < 0) + return ret; + + if (vb2_is_busy(&node->queue)) + return -EBUSY; + + node->format = *f; + node->pisp_format = pispbe_find_fmt(f->fmt.pix_mp.pixelformat); + + dev_dbg(pispbe->dev, "Set output format for node %s to %p4cc\n", + NODE_NAME(node), &f->fmt.pix_mp.pixelformat); + + return 0; +} + +static int pispbe_node_s_fmt_meta_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct pispbe_node *node = video_drvdata(file); + struct pispbe_dev *pispbe = node->pispbe; + int ret; + + ret = pispbe_node_try_fmt_meta_out(file, priv, f); + if (ret < 0) + return ret; + + if (vb2_is_busy(&node->queue)) + return -EBUSY; + + node->format = *f; + node->pisp_format = &meta_out_supported_formats[0]; + + dev_dbg(pispbe->dev, "Set output format for meta node %s to %p4cc\n", + NODE_NAME(node), &f->fmt.meta.dataformat); + + return 0; +} + +static int pispbe_node_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct pispbe_node *node = video_drvdata(file); + + if (f->type != node->queue.type) + return -EINVAL; + + if (NODE_IS_META(node)) { + if (f->index) + return -EINVAL; + + f->pixelformat = V4L2_META_FMT_RPI_BE_CFG; + f->flags = 0; + return 0; + } + + if (f->index >= ARRAY_SIZE(supported_formats)) + return -EINVAL; + + f->pixelformat = supported_formats[f->index].fourcc; + f->flags = 0; + + return 0; +} + +static int pispbe_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + struct pispbe_node *node = video_drvdata(file); + struct pispbe_dev *pispbe = node->pispbe; + + if (NODE_IS_META(node) || fsize->index) + return -EINVAL; + + if (!pispbe_find_fmt(fsize->pixel_format)) { + dev_dbg(pispbe->dev, "Invalid pixel code: %x\n", + fsize->pixel_format); + return -EINVAL; + } + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.min_width = 32; + fsize->stepwise.max_width = 65535; + fsize->stepwise.step_width = 2; + + fsize->stepwise.min_height = 32; + fsize->stepwise.max_height = 65535; + fsize->stepwise.step_height = 2; + + return 0; +} + +static const struct v4l2_ioctl_ops pispbe_node_ioctl_ops = { + .vidioc_querycap = pispbe_node_querycap, + .vidioc_g_fmt_vid_cap_mplane = pispbe_node_g_fmt_vid_cap, + .vidioc_g_fmt_vid_out_mplane = pispbe_node_g_fmt_vid_out, + .vidioc_g_fmt_meta_out = pispbe_node_g_fmt_meta_out, + .vidioc_try_fmt_vid_cap_mplane = pispbe_node_try_fmt_vid_cap, + .vidioc_try_fmt_vid_out_mplane = pispbe_node_try_fmt_vid_out, + .vidioc_try_fmt_meta_out = pispbe_node_try_fmt_meta_out, + .vidioc_s_fmt_vid_cap_mplane = pispbe_node_s_fmt_vid_cap, + .vidioc_s_fmt_vid_out_mplane = pispbe_node_s_fmt_vid_out, + .vidioc_s_fmt_meta_out = pispbe_node_s_fmt_meta_out, + .vidioc_enum_fmt_vid_cap = pispbe_node_enum_fmt, + .vidioc_enum_fmt_vid_out = pispbe_node_enum_fmt, + .vidioc_enum_fmt_meta_out = pispbe_node_enum_fmt, + .vidioc_enum_framesizes = pispbe_enum_framesizes, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +static const struct video_device pispbe_videodev = { + .name = PISPBE_NAME, + .vfl_dir = VFL_DIR_M2M, /* gets overwritten */ + .fops = &pispbe_fops, + .ioctl_ops = &pispbe_node_ioctl_ops, + .minor = -1, + .release = video_device_release_empty, +}; + +static void pispbe_node_def_fmt(struct pispbe_node *node) +{ + if (NODE_IS_META(node) && NODE_IS_OUTPUT(node)) { + /* Config node */ + struct v4l2_format *f = &node->format; + + f->fmt.meta.dataformat = V4L2_META_FMT_RPI_BE_CFG; + f->fmt.meta.buffersize = sizeof(struct pisp_be_tiles_config); + f->type = node->buf_type; + } else { + struct v4l2_format f = { + .fmt.pix_mp.pixelformat = V4L2_PIX_FMT_YUV420, + .fmt.pix_mp.width = 1920, + .fmt.pix_mp.height = 1080, + .type = node->buf_type, + }; + pispbe_try_format(&f, node); + node->format = f; + } + + node->pisp_format = pispbe_find_fmt(node->format.fmt.pix_mp.pixelformat); +} + +/* + * Initialise a struct pispbe_node and register it as /dev/video<N> + * to represent one of the PiSP Back End's input or output streams. + */ +static int pispbe_init_node(struct pispbe_dev *pispbe, unsigned int id) +{ + bool output = NODE_DESC_IS_OUTPUT(&node_desc[id]); + struct pispbe_node *node = &pispbe->node[id]; + struct media_entity *entity = &node->vfd.entity; + struct video_device *vdev = &node->vfd; + struct vb2_queue *q = &node->queue; + int ret; + + node->id = id; + node->pispbe = pispbe; + node->buf_type = node_desc[id].buf_type; + + mutex_init(&node->node_lock); + mutex_init(&node->queue_lock); + INIT_LIST_HEAD(&node->ready_queue); + spin_lock_init(&node->ready_lock); + + node->format.type = node->buf_type; + pispbe_node_def_fmt(node); + + q->type = node->buf_type; + q->io_modes = VB2_MMAP | VB2_DMABUF; + q->mem_ops = &vb2_dma_contig_memops; + q->drv_priv = node; + q->ops = &pispbe_node_queue_ops; + q->buf_struct_size = sizeof(struct pispbe_buffer); + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->dev = pispbe->dev; + /* get V4L2 to handle node->queue locking */ + q->lock = &node->queue_lock; + + ret = vb2_queue_init(q); + if (ret < 0) { + dev_err(pispbe->dev, "vb2_queue_init failed\n"); + goto err_mutex_destroy; + } + + *vdev = pispbe_videodev; /* default initialization */ + strscpy(vdev->name, node_desc[id].ent_name, sizeof(vdev->name)); + vdev->v4l2_dev = &pispbe->v4l2_dev; + vdev->vfl_dir = output ? VFL_DIR_TX : VFL_DIR_RX; + /* get V4L2 to serialise our ioctls */ + vdev->lock = &node->node_lock; + vdev->queue = &node->queue; + vdev->device_caps = V4L2_CAP_STREAMING | node_desc[id].caps; + + node->pad.flags = output ? MEDIA_PAD_FL_SOURCE : MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(entity, 1, &node->pad); + if (ret) { + dev_err(pispbe->dev, + "Failed to register media pads for %s device node\n", + NODE_NAME(node)); + goto err_unregister_queue; + } + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) { + dev_err(pispbe->dev, + "Failed to register video %s device node\n", + NODE_NAME(node)); + goto err_unregister_queue; + } + video_set_drvdata(vdev, node); + + if (output) + ret = media_create_pad_link(entity, 0, &pispbe->sd.entity, + id, MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + else + ret = media_create_pad_link(&pispbe->sd.entity, id, entity, + 0, MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + if (ret) + goto err_unregister_video_dev; + + dev_dbg(pispbe->dev, "%s device node registered as /dev/video%d\n", + NODE_NAME(node), node->vfd.num); + + return 0; + +err_unregister_video_dev: + video_unregister_device(&node->vfd); +err_unregister_queue: + vb2_queue_release(&node->queue); +err_mutex_destroy: + mutex_destroy(&node->node_lock); + mutex_destroy(&node->queue_lock); + return ret; +} + +static const struct v4l2_subdev_pad_ops pispbe_pad_ops = { + .link_validate = v4l2_subdev_link_validate_default, +}; + +static const struct v4l2_subdev_ops pispbe_sd_ops = { + .pad = &pispbe_pad_ops, +}; + +static int pispbe_init_subdev(struct pispbe_dev *pispbe) +{ + struct v4l2_subdev *sd = &pispbe->sd; + int ret; + + v4l2_subdev_init(sd, &pispbe_sd_ops); + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; + sd->owner = THIS_MODULE; + sd->dev = pispbe->dev; + strscpy(sd->name, PISPBE_NAME, sizeof(sd->name)); + + for (unsigned int i = 0; i < PISPBE_NUM_NODES; i++) + pispbe->pad[i].flags = + NODE_DESC_IS_OUTPUT(&node_desc[i]) ? + MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&sd->entity, PISPBE_NUM_NODES, + pispbe->pad); + if (ret) + goto error; + + ret = v4l2_device_register_subdev(&pispbe->v4l2_dev, sd); + if (ret) + goto error; + + return 0; + +error: + media_entity_cleanup(&sd->entity); + return ret; +} + +static int pispbe_init_devices(struct pispbe_dev *pispbe) +{ + struct v4l2_device *v4l2_dev; + struct media_device *mdev; + unsigned int num_regist; + int ret; + + /* Register v4l2_device and media_device */ + mdev = &pispbe->mdev; + mdev->hw_revision = pispbe->hw_version; + mdev->dev = pispbe->dev; + strscpy(mdev->model, PISPBE_NAME, sizeof(mdev->model)); + media_device_init(mdev); + + v4l2_dev = &pispbe->v4l2_dev; + v4l2_dev->mdev = &pispbe->mdev; + strscpy(v4l2_dev->name, PISPBE_NAME, sizeof(v4l2_dev->name)); + + ret = v4l2_device_register(pispbe->dev, v4l2_dev); + if (ret) + goto err_media_dev_cleanup; + + /* Register the PISPBE subdevice. */ + ret = pispbe_init_subdev(pispbe); + if (ret) + goto err_unregister_v4l2; + + /* Create device video nodes */ + for (num_regist = 0; num_regist < PISPBE_NUM_NODES; num_regist++) { + ret = pispbe_init_node(pispbe, num_regist); + if (ret) + goto err_unregister_nodes; + } + + ret = media_device_register(mdev); + if (ret) + goto err_unregister_nodes; + + pispbe->config = + dma_alloc_coherent(pispbe->dev, + sizeof(struct pisp_be_tiles_config) * + PISP_BE_NUM_CONFIG_BUFFERS, + &pispbe->config_dma_addr, GFP_KERNEL); + if (!pispbe->config) { + dev_err(pispbe->dev, "Unable to allocate cached config buffers.\n"); + ret = -ENOMEM; + goto err_unregister_mdev; + } + + return 0; + +err_unregister_mdev: + media_device_unregister(mdev); +err_unregister_nodes: + while (num_regist-- > 0) { + video_unregister_device(&pispbe->node[num_regist].vfd); + vb2_queue_release(&pispbe->node[num_regist].queue); + } + v4l2_device_unregister_subdev(&pispbe->sd); + media_entity_cleanup(&pispbe->sd.entity); +err_unregister_v4l2: + v4l2_device_unregister(v4l2_dev); +err_media_dev_cleanup: + media_device_cleanup(mdev); + return ret; +} + +static void pispbe_destroy_devices(struct pispbe_dev *pispbe) +{ + if (pispbe->config) { + dma_free_coherent(pispbe->dev, + sizeof(struct pisp_be_tiles_config) * + PISP_BE_NUM_CONFIG_BUFFERS, + pispbe->config, + pispbe->config_dma_addr); + } + + dev_dbg(pispbe->dev, "Unregister from media controller\n"); + + v4l2_device_unregister_subdev(&pispbe->sd); + media_entity_cleanup(&pispbe->sd.entity); + media_device_unregister(&pispbe->mdev); + + for (int i = PISPBE_NUM_NODES - 1; i >= 0; i--) { + video_unregister_device(&pispbe->node[i].vfd); + vb2_queue_release(&pispbe->node[i].queue); + mutex_destroy(&pispbe->node[i].node_lock); + mutex_destroy(&pispbe->node[i].queue_lock); + } + + media_device_cleanup(&pispbe->mdev); + v4l2_device_unregister(&pispbe->v4l2_dev); +} + +static int pispbe_runtime_suspend(struct device *dev) +{ + struct pispbe_dev *pispbe = dev_get_drvdata(dev); + + clk_disable_unprepare(pispbe->clk); + + return 0; +} + +static int pispbe_runtime_resume(struct device *dev) +{ + struct pispbe_dev *pispbe = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(pispbe->clk); + if (ret) { + dev_err(dev, "Unable to enable clock\n"); + return ret; + } + + dev_dbg(dev, "%s: Enabled clock, rate=%lu\n", + __func__, clk_get_rate(pispbe->clk)); + + return 0; +} + +static int pispbe_hw_init(struct pispbe_dev *pispbe) +{ + u32 u; + + /* Check the HW is present and has a known version */ + u = pispbe_rd(pispbe, PISP_BE_VERSION_REG); + dev_dbg(pispbe->dev, "pispbe_probe: HW version: 0x%08x", u); + pispbe->hw_version = u; + if ((u & ~PISP_BE_VERSION_MINOR_BITS) != PISP_BE_VERSION_2712) + return -ENODEV; + + /* Clear leftover interrupts */ + pispbe_wr(pispbe, PISP_BE_INTERRUPT_STATUS_REG, 0xFFFFFFFFu); + u = pispbe_rd(pispbe, PISP_BE_BATCH_STATUS_REG); + dev_dbg(pispbe->dev, "pispbe_probe: BatchStatus: 0x%08x", u); + + pispbe->done = (uint8_t)u; + pispbe->started = (uint8_t)(u >> 8); + u = pispbe_rd(pispbe, PISP_BE_STATUS_REG); + dev_dbg(pispbe->dev, "pispbe_probe: Status: 0x%08x", u); + + if (u != 0 || pispbe->done != pispbe->started) { + dev_err(pispbe->dev, "pispbe_probe: HW is stuck or busy\n"); + return -EBUSY; + } + + /* + * AXI QOS=0, CACHE=4'b0010, PROT=3'b011 + * Also set "chicken bits" 22:20 which enable sub-64-byte bursts + * and AXI AWID/BID variability (on versions which support this). + */ + pispbe_wr(pispbe, PISP_BE_AXI_REG, 0x32703200u); + + /* Enable both interrupt flags */ + pispbe_wr(pispbe, PISP_BE_INTERRUPT_EN_REG, 0x00000003u); + + return 0; +} + +/* Probe the ISP-BE hardware block, as a single platform device. */ +static int pispbe_probe(struct platform_device *pdev) +{ + struct pispbe_dev *pispbe; + int ret; + + pispbe = devm_kzalloc(&pdev->dev, sizeof(*pispbe), GFP_KERNEL); + if (!pispbe) + return -ENOMEM; + + dev_set_drvdata(&pdev->dev, pispbe); + pispbe->dev = &pdev->dev; + platform_set_drvdata(pdev, pispbe); + + pispbe->be_reg_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pispbe->be_reg_base)) { + dev_err(&pdev->dev, "Failed to get ISP-BE registers address\n"); + return PTR_ERR(pispbe->be_reg_base); + } + + pispbe->irq = platform_get_irq(pdev, 0); + if (pispbe->irq <= 0) + return -EINVAL; + + ret = devm_request_irq(&pdev->dev, pispbe->irq, pispbe_isr, 0, + PISPBE_NAME, pispbe); + if (ret) { + dev_err(&pdev->dev, "Unable to request interrupt\n"); + return ret; + } + + ret = dma_set_mask_and_coherent(pispbe->dev, DMA_BIT_MASK(36)); + if (ret) + return ret; + + pispbe->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pispbe->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(pispbe->clk), + "Failed to get clock"); + + /* Hardware initialisation */ + pm_runtime_set_autosuspend_delay(pispbe->dev, 200); + pm_runtime_use_autosuspend(pispbe->dev); + pm_runtime_enable(pispbe->dev); + + ret = pispbe_runtime_resume(pispbe->dev); + if (ret) + goto pm_runtime_disable_err; + + pispbe->hw_busy = false; + spin_lock_init(&pispbe->hw_lock); + ret = pispbe_hw_init(pispbe); + if (ret) + goto pm_runtime_suspend_err; + + ret = pispbe_init_devices(pispbe); + if (ret) + goto disable_devs_err; + + pm_runtime_mark_last_busy(pispbe->dev); + pm_runtime_put_autosuspend(pispbe->dev); + + return 0; + +disable_devs_err: + pispbe_destroy_devices(pispbe); +pm_runtime_suspend_err: + pispbe_runtime_suspend(pispbe->dev); +pm_runtime_disable_err: + pm_runtime_dont_use_autosuspend(pispbe->dev); + pm_runtime_disable(pispbe->dev); + + return ret; +} + +static void pispbe_remove(struct platform_device *pdev) +{ + struct pispbe_dev *pispbe = platform_get_drvdata(pdev); + + pispbe_destroy_devices(pispbe); + + pispbe_runtime_suspend(pispbe->dev); + pm_runtime_dont_use_autosuspend(pispbe->dev); + pm_runtime_disable(pispbe->dev); +} + +static const struct dev_pm_ops pispbe_pm_ops = { + SET_RUNTIME_PM_OPS(pispbe_runtime_suspend, pispbe_runtime_resume, NULL) +}; + +static const struct of_device_id pispbe_of_match[] = { + { + .compatible = "raspberrypi,pispbe", + }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, pispbe_of_match); + +static struct platform_driver pispbe_pdrv = { + .probe = pispbe_probe, + .remove_new = pispbe_remove, + .driver = { + .name = PISPBE_NAME, + .of_match_table = pispbe_of_match, + .pm = &pispbe_pm_ops, + }, +}; + +module_platform_driver(pispbe_pdrv); + +MODULE_DESCRIPTION("PiSP Back End driver"); +MODULE_AUTHOR("David Plowman <david.plowman@raspberrypi.com>"); +MODULE_AUTHOR("Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/raspberrypi/pisp_be/pisp_be_formats.h b/drivers/media/platform/raspberrypi/pisp_be/pisp_be_formats.h new file mode 100644 index 000000000000..b5cb7b8c7531 --- /dev/null +++ b/drivers/media/platform/raspberrypi/pisp_be/pisp_be_formats.h @@ -0,0 +1,519 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * PiSP Back End driver image format definitions. + * + * Copyright (c) 2021-2024 Raspberry Pi Ltd + */ + +#ifndef _PISP_BE_FORMATS_ +#define _PISP_BE_FORMATS_ + +#include <linux/bits.h> +#include <linux/videodev2.h> + +#define PISPBE_MAX_PLANES 3 +#define P3(x) ((x) * 8) + +struct pisp_be_format { + unsigned int fourcc; + unsigned int align; + unsigned int bit_depth; + /* 0P3 factor for plane sizing */ + unsigned int plane_factor[PISPBE_MAX_PLANES]; + unsigned int num_planes; + unsigned int colorspace_mask; + enum v4l2_colorspace colorspace_default; +}; + +#define V4L2_COLORSPACE_MASK(colorspace) BIT(colorspace) + +#define V4L2_COLORSPACE_MASK_JPEG \ + V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_JPEG) +#define V4L2_COLORSPACE_MASK_SMPTE170M \ + V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_SMPTE170M) +#define V4L2_COLORSPACE_MASK_REC709 \ + V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_REC709) +#define V4L2_COLORSPACE_MASK_SRGB \ + V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_SRGB) +#define V4L2_COLORSPACE_MASK_RAW \ + V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_RAW) + +/* + * All three colour spaces SRGB, SMPTE170M and REC709 are fundamentally sRGB + * underneath (as near as makes no difference to us), just with different YCbCr + * encodings. Therefore the ISP can generate sRGB on its main output and any of + * the others on its low resolution output. Applications should, when using both + * outputs, program the colour spaces on them to be the same, matching whatever + * is requested for the low resolution output, even if the main output is + * producing an RGB format. In turn this requires us to allow all these colour + * spaces for every YUV/RGB output format. + */ +#define V4L2_COLORSPACE_MASK_ALL_SRGB (V4L2_COLORSPACE_MASK_JPEG | \ + V4L2_COLORSPACE_MASK_SRGB | \ + V4L2_COLORSPACE_MASK_SMPTE170M | \ + V4L2_COLORSPACE_MASK_REC709) + +static const struct pisp_be_format supported_formats[] = { + /* Single plane YUV formats */ + { + .fourcc = V4L2_PIX_FMT_YUV420, + /* 128 alignment to ensure U/V planes are 64 byte aligned. */ + .align = 128, + .bit_depth = 8, + .plane_factor = { P3(1), P3(0.25), P3(0.25) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_YVU420, + /* 128 alignment to ensure U/V planes are 64 byte aligned. */ + .align = 128, + .bit_depth = 8, + .plane_factor = { P3(1), P3(0.25), P3(0.25) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_NV12, + .align = 32, + .bit_depth = 8, + .plane_factor = { P3(1), P3(0.5) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_NV21, + .align = 32, + .bit_depth = 8, + .plane_factor = { P3(1), P3(0.5) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_YUYV, + .align = 64, + .bit_depth = 16, + .plane_factor = { P3(1) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_UYVY, + .align = 64, + .bit_depth = 16, + .plane_factor = { P3(1) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_YVYU, + .align = 64, + .bit_depth = 16, + .plane_factor = { P3(1) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_VYUY, + .align = 64, + .bit_depth = 16, + .plane_factor = { P3(1) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + /* Multiplane YUV formats */ + { + .fourcc = V4L2_PIX_FMT_YUV420M, + .align = 64, + .bit_depth = 8, + .plane_factor = { P3(1), P3(0.25), P3(0.25) }, + .num_planes = 3, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_NV12M, + .align = 32, + .bit_depth = 8, + .plane_factor = { P3(1), P3(0.5) }, + .num_planes = 2, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_NV21M, + .align = 32, + .bit_depth = 8, + .plane_factor = { P3(1), P3(0.5) }, + .num_planes = 2, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_YVU420M, + .align = 64, + .bit_depth = 8, + .plane_factor = { P3(1), P3(0.25), P3(0.25) }, + .num_planes = 3, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_YUV422M, + .align = 64, + .bit_depth = 8, + .plane_factor = { P3(1), P3(0.5), P3(0.5) }, + .num_planes = 3, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_YVU422M, + .align = 64, + .bit_depth = 8, + .plane_factor = { P3(1), P3(0.5), P3(0.5) }, + .num_planes = 3, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_YUV444M, + .align = 64, + .bit_depth = 8, + .plane_factor = { P3(1), P3(1), P3(1) }, + .num_planes = 3, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_YVU444M, + .align = 64, + .bit_depth = 8, + .plane_factor = { P3(1), P3(1), P3(1) }, + .num_planes = 3, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + /* RGB formats */ + { + .fourcc = V4L2_PIX_FMT_RGB24, + .align = 32, + .bit_depth = 24, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SRGB, + }, + { + .fourcc = V4L2_PIX_FMT_BGR24, + .align = 32, + .bit_depth = 24, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SRGB, + }, + { + .fourcc = V4L2_PIX_FMT_XBGR32, + .align = 64, + .bit_depth = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SRGB, + }, + { + .fourcc = V4L2_PIX_FMT_RGBX32, + .align = 64, + .bit_depth = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SRGB, + }, + { + .fourcc = V4L2_PIX_FMT_RGB48, + .align = 64, + .bit_depth = 48, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SRGB, + }, + { + .fourcc = V4L2_PIX_FMT_BGR48, + .align = 64, + .bit_depth = 48, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SRGB, + }, + /* Bayer formats - 8-bit */ + { + .fourcc = V4L2_PIX_FMT_SRGGB8, + .bit_depth = 8, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR8, + .bit_depth = 8, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .bit_depth = 8, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG8, + .bit_depth = 8, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + /* Bayer formats - 16-bit */ + { + .fourcc = V4L2_PIX_FMT_SRGGB16, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR16, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG16, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG16, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + /* Bayer formats unpacked to 16bpp */ + /* 10 bit */ + .fourcc = V4L2_PIX_FMT_SRGGB10, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG10, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG10, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + /* 12 bit */ + .fourcc = V4L2_PIX_FMT_SRGGB12, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR12, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG12, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG12, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + /* 14 bit */ + .fourcc = V4L2_PIX_FMT_SRGGB14, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR14, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG14, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG14, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + /* Bayer formats - 16-bit PiSP Compressed */ + { + .fourcc = V4L2_PIX_FMT_PISP_COMP1_BGGR, + .bit_depth = 8, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_PISP_COMP1_RGGB, + .bit_depth = 8, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_PISP_COMP1_GRBG, + .bit_depth = 8, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_PISP_COMP1_GBRG, + .bit_depth = 8, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + /* Greyscale Formats */ + { + .fourcc = V4L2_PIX_FMT_GREY, + .bit_depth = 8, + .align = 32, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_Y16, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_PISP_COMP1_MONO, + .bit_depth = 8, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, +}; + +static const struct pisp_be_format meta_out_supported_formats[] = { + /* Configuration buffer format. */ + { + .fourcc = V4L2_META_FMT_RPI_BE_CFG, + }, +}; + +#endif /* _PISP_BE_FORMATS_ */ diff --git a/drivers/media/platform/renesas/rcar-csi2.c b/drivers/media/platform/renesas/rcar-csi2.c index 582d5e35db0e..c419ddb4c5a2 100644 --- a/drivers/media/platform/renesas/rcar-csi2.c +++ b/drivers/media/platform/renesas/rcar-csi2.c @@ -587,7 +587,8 @@ enum rcar_csi2_pads { struct rcar_csi2_info { int (*init_phtw)(struct rcar_csi2 *priv, unsigned int mbps); int (*phy_post_init)(struct rcar_csi2 *priv); - int (*start_receiver)(struct rcar_csi2 *priv); + int (*start_receiver)(struct rcar_csi2 *priv, + struct v4l2_subdev_state *state); void (*enter_standby)(struct rcar_csi2 *priv); const struct rcsi2_mbps_reg *hsfreqrange; unsigned int csi0clkfreqrange; @@ -613,8 +614,6 @@ struct rcar_csi2 { int channel_vc[4]; - struct mutex lock; /* Protects mf and stream_count. */ - struct v4l2_mbus_framefmt mf; int stream_count; bool cphy; @@ -632,6 +631,16 @@ static inline struct rcar_csi2 *notifier_to_csi2(struct v4l2_async_notifier *n) return container_of(n, struct rcar_csi2, notifier); } +static unsigned int rcsi2_num_pads(const struct rcar_csi2 *priv) +{ + /* Used together with R-Car ISP: one sink and one source pad. */ + if (priv->info->use_isp) + return 2; + + /* Used together with R-Car VIN: one sink and four source pads. */ + return 5; +} + static u32 rcsi2_read(struct rcar_csi2 *priv, unsigned int reg) { return ioread32(priv->base + reg); @@ -808,20 +817,25 @@ static int rcsi2_get_active_lanes(struct rcar_csi2 *priv, return 0; } -static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv) +static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv, + struct v4l2_subdev_state *state) { const struct rcar_csi2_format *format; u32 phycnt, vcdt = 0, vcdt2 = 0, fld = 0; + const struct v4l2_mbus_framefmt *fmt; unsigned int lanes; unsigned int i; int mbps, ret; + /* Use the format on the sink pad to compute the receiver config. */ + fmt = v4l2_subdev_state_get_format(state, RCAR_CSI2_SINK); + dev_dbg(priv->dev, "Input size (%ux%u%c)\n", - priv->mf.width, priv->mf.height, - priv->mf.field == V4L2_FIELD_NONE ? 'p' : 'i'); + fmt->width, fmt->height, + fmt->field == V4L2_FIELD_NONE ? 'p' : 'i'); /* Code is validated in set_fmt. */ - format = rcsi2_code_to_fmt(priv->mf.code); + format = rcsi2_code_to_fmt(fmt->code); if (!format) return -EINVAL; @@ -849,11 +863,11 @@ static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv) vcdt2 |= vcdt_part << ((i % 2) * 16); } - if (priv->mf.field == V4L2_FIELD_ALTERNATE) { + if (fmt->field == V4L2_FIELD_ALTERNATE) { fld = FLD_DET_SEL(1) | FLD_FLD_EN4 | FLD_FLD_EN3 | FLD_FLD_EN2 | FLD_FLD_EN; - if (priv->mf.height == 240) + if (fmt->height == 240) fld |= FLD_FLD_NUM(0); else fld |= FLD_FLD_NUM(1); @@ -1049,15 +1063,18 @@ static int rcsi2_c_phy_setting_v4h(struct rcar_csi2 *priv, int msps) return 0; } -static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv) +static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv, + struct v4l2_subdev_state *state) { const struct rcar_csi2_format *format; + const struct v4l2_mbus_framefmt *fmt; unsigned int lanes; int msps; int ret; - /* Calculate parameters */ - format = rcsi2_code_to_fmt(priv->mf.code); + /* Use the format on the sink pad to compute the receiver config. */ + fmt = v4l2_subdev_state_get_format(state, RCAR_CSI2_SINK); + format = rcsi2_code_to_fmt(fmt->code); if (!format) return -EINVAL; @@ -1114,7 +1131,7 @@ static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv) return 0; } -static int rcsi2_start(struct rcar_csi2 *priv) +static int rcsi2_start(struct rcar_csi2 *priv, struct v4l2_subdev_state *state) { int ret; @@ -1122,7 +1139,7 @@ static int rcsi2_start(struct rcar_csi2 *priv) if (ret < 0) return ret; - ret = priv->info->start_receiver(priv); + ret = priv->info->start_receiver(priv, state); if (ret) { rcsi2_enter_standby(priv); return ret; @@ -1146,17 +1163,16 @@ static void rcsi2_stop(struct rcar_csi2 *priv) static int rcsi2_s_stream(struct v4l2_subdev *sd, int enable) { struct rcar_csi2 *priv = sd_to_csi2(sd); + struct v4l2_subdev_state *state; int ret = 0; - mutex_lock(&priv->lock); + if (!priv->remote) + return -ENODEV; - if (!priv->remote) { - ret = -ENODEV; - goto out; - } + state = v4l2_subdev_lock_and_get_active_state(&priv->subdev); if (enable && priv->stream_count == 0) { - ret = rcsi2_start(priv); + ret = rcsi2_start(priv, state); if (ret) goto out; } else if (!enable && priv->stream_count == 1) { @@ -1165,49 +1181,29 @@ static int rcsi2_s_stream(struct v4l2_subdev *sd, int enable) priv->stream_count += enable ? 1 : -1; out: - mutex_unlock(&priv->lock); + v4l2_subdev_unlock_state(state); return ret; } static int rcsi2_set_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_state *state, struct v4l2_subdev_format *format) { struct rcar_csi2 *priv = sd_to_csi2(sd); - struct v4l2_mbus_framefmt *framefmt; + unsigned int num_pads = rcsi2_num_pads(priv); - mutex_lock(&priv->lock); + if (format->pad > RCAR_CSI2_SINK) + return v4l2_subdev_get_fmt(sd, state, format); if (!rcsi2_code_to_fmt(format->format.code)) format->format.code = rcar_csi2_formats[0].code; - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - priv->mf = format->format; - } else { - framefmt = v4l2_subdev_state_get_format(sd_state, 0); - *framefmt = format->format; - } + *v4l2_subdev_state_get_format(state, format->pad) = format->format; - mutex_unlock(&priv->lock); - - return 0; -} - -static int rcsi2_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) -{ - struct rcar_csi2 *priv = sd_to_csi2(sd); - - mutex_lock(&priv->lock); - - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - format->format = priv->mf; - else - format->format = *v4l2_subdev_state_get_format(sd_state, 0); - - mutex_unlock(&priv->lock); + /* Propagate the format to the source pads. */ + for (unsigned int i = RCAR_CSI2_SOURCE_VC0; i < num_pads; i++) + *v4l2_subdev_state_get_format(state, i) = format->format; return 0; } @@ -1218,7 +1214,7 @@ static const struct v4l2_subdev_video_ops rcar_csi2_video_ops = { static const struct v4l2_subdev_pad_ops rcar_csi2_pad_ops = { .set_fmt = rcsi2_set_pad_format, - .get_fmt = rcsi2_get_pad_format, + .get_fmt = v4l2_subdev_get_fmt, }; static const struct v4l2_subdev_ops rcar_csi2_subdev_ops = { @@ -1226,6 +1222,33 @@ static const struct v4l2_subdev_ops rcar_csi2_subdev_ops = { .pad = &rcar_csi2_pad_ops, }; +static int rcsi2_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct rcar_csi2 *priv = sd_to_csi2(sd); + unsigned int num_pads = rcsi2_num_pads(priv); + + static const struct v4l2_mbus_framefmt rcar_csi2_default_fmt = { + .width = 1920, + .height = 1080, + .code = MEDIA_BUS_FMT_RGB888_1X24, + .colorspace = V4L2_COLORSPACE_SRGB, + .field = V4L2_FIELD_NONE, + .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT, + .quantization = V4L2_QUANTIZATION_DEFAULT, + .xfer_func = V4L2_XFER_FUNC_DEFAULT, + }; + + for (unsigned int i = RCAR_CSI2_SINK; i < num_pads; i++) + *v4l2_subdev_state_get_format(state, i) = rcar_csi2_default_fmt; + + return 0; +} + +static const struct v4l2_subdev_internal_ops rcar_csi2_internal_ops = { + .init_state = rcsi2_init_state, +}; + static irqreturn_t rcsi2_irq(int irq, void *data) { struct rcar_csi2 *priv = data; @@ -1251,14 +1274,17 @@ static irqreturn_t rcsi2_irq(int irq, void *data) static irqreturn_t rcsi2_irq_thread(int irq, void *data) { + struct v4l2_subdev_state *state; struct rcar_csi2 *priv = data; - mutex_lock(&priv->lock); + state = v4l2_subdev_lock_and_get_active_state(&priv->subdev); + rcsi2_stop(priv); usleep_range(1000, 2000); - if (rcsi2_start(priv)) + if (rcsi2_start(priv, state)) dev_warn(priv->dev, "Failed to restart CSI-2 receiver\n"); - mutex_unlock(&priv->lock); + + v4l2_subdev_unlock_state(state); return IRQ_HANDLED; } @@ -1870,23 +1896,23 @@ static int rcsi2_probe(struct platform_device *pdev) priv->dev = &pdev->dev; - mutex_init(&priv->lock); priv->stream_count = 0; ret = rcsi2_probe_resources(priv, pdev); if (ret) { dev_err(priv->dev, "Failed to get resources\n"); - goto error_mutex; + return ret; } platform_set_drvdata(pdev, priv); ret = rcsi2_parse_dt(priv); if (ret) - goto error_mutex; + return ret; priv->subdev.owner = THIS_MODULE; priv->subdev.dev = &pdev->dev; + priv->subdev.internal_ops = &rcar_csi2_internal_ops; v4l2_subdev_init(&priv->subdev, &rcar_csi2_subdev_ops); v4l2_set_subdevdata(&priv->subdev, &pdev->dev); snprintf(priv->subdev.name, sizeof(priv->subdev.name), "%s %s", @@ -1896,7 +1922,7 @@ static int rcsi2_probe(struct platform_device *pdev) priv->subdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; priv->subdev.entity.ops = &rcar_csi2_entity_ops; - num_pads = priv->info->use_isp ? 2 : NR_OF_RCAR_CSI2_PAD; + num_pads = rcsi2_num_pads(priv); priv->pads[RCAR_CSI2_SINK].flags = MEDIA_PAD_FL_SINK; for (i = RCAR_CSI2_SOURCE_VC0; i < num_pads; i++) @@ -1912,19 +1938,25 @@ static int rcsi2_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); + ret = v4l2_subdev_init_finalize(&priv->subdev); + if (ret) + goto error_pm_runtime; + ret = v4l2_async_register_subdev(&priv->subdev); if (ret < 0) - goto error_async; + goto error_subdev; dev_info(priv->dev, "%d lanes found\n", priv->lanes); return 0; +error_subdev: + v4l2_subdev_cleanup(&priv->subdev); +error_pm_runtime: + pm_runtime_disable(&pdev->dev); error_async: v4l2_async_nf_unregister(&priv->notifier); v4l2_async_nf_cleanup(&priv->notifier); -error_mutex: - mutex_destroy(&priv->lock); return ret; } @@ -1936,10 +1968,9 @@ static void rcsi2_remove(struct platform_device *pdev) v4l2_async_nf_unregister(&priv->notifier); v4l2_async_nf_cleanup(&priv->notifier); v4l2_async_unregister_subdev(&priv->subdev); + v4l2_subdev_cleanup(&priv->subdev); pm_runtime_disable(&pdev->dev); - - mutex_destroy(&priv->lock); } static struct platform_driver rcar_csi2_pdrv = { diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c index e2c40abc6d3d..21d5b2815e86 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c @@ -742,12 +742,22 @@ static int rvin_setup(struct rvin_dev *vin) */ switch (vin->mbus_code) { case MEDIA_BUS_FMT_YUYV8_1X16: - /* BT.601/BT.1358 16bit YCbCr422 */ - vnmc |= VNMC_INF_YUV16; + if (vin->is_csi) + /* YCbCr422 8-bit */ + vnmc |= VNMC_INF_YUV8_BT601; + else + /* BT.601/BT.1358 16bit YCbCr422 */ + vnmc |= VNMC_INF_YUV16; input_is_yuv = true; break; case MEDIA_BUS_FMT_UYVY8_1X16: - vnmc |= VNMC_INF_YUV16 | VNMC_YCAL; + if (vin->is_csi) + /* YCbCr422 8-bit */ + vnmc |= VNMC_INF_YUV8_BT601; + else + /* BT.601/BT.1358 16bit YCbCr422 */ + vnmc |= VNMC_INF_YUV16; + vnmc |= VNMC_YCAL; input_is_yuv = true; break; case MEDIA_BUS_FMT_UYVY8_2X8: diff --git a/drivers/media/platform/renesas/vsp1/vsp1_brx.c b/drivers/media/platform/renesas/vsp1/vsp1_brx.c index a8535c6e2c46..5dee0490c593 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_brx.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_brx.c @@ -96,13 +96,6 @@ static int brx_enum_frame_size(struct v4l2_subdev *subdev, return 0; } -static struct v4l2_rect *brx_get_compose(struct vsp1_brx *brx, - struct v4l2_subdev_state *sd_state, - unsigned int pad) -{ - return v4l2_subdev_state_get_compose(sd_state, pad); -} - static void brx_try_format(struct vsp1_brx *brx, struct v4l2_subdev_state *sd_state, unsigned int pad, struct v4l2_mbus_framefmt *fmt) @@ -119,8 +112,8 @@ static void brx_try_format(struct vsp1_brx *brx, default: /* The BRx can't perform format conversion. */ - format = vsp1_entity_get_pad_format(&brx->entity, sd_state, - BRX_PAD_SINK(0)); + format = v4l2_subdev_state_get_format(sd_state, + BRX_PAD_SINK(0)); fmt->code = format->code; break; } @@ -150,14 +143,14 @@ static int brx_set_format(struct v4l2_subdev *subdev, brx_try_format(brx, state, fmt->pad, &fmt->format); - format = vsp1_entity_get_pad_format(&brx->entity, state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); *format = fmt->format; /* Reset the compose rectangle. */ if (fmt->pad != brx->entity.source_pad) { struct v4l2_rect *compose; - compose = brx_get_compose(brx, state, fmt->pad); + compose = v4l2_subdev_state_get_compose(state, fmt->pad); compose->left = 0; compose->top = 0; compose->width = format->width; @@ -169,8 +162,7 @@ static int brx_set_format(struct v4l2_subdev *subdev, unsigned int i; for (i = 0; i <= brx->entity.source_pad; ++i) { - format = vsp1_entity_get_pad_format(&brx->entity, - state, i); + format = v4l2_subdev_state_get_format(state, i); format->code = fmt->format.code; } } @@ -205,7 +197,7 @@ static int brx_get_selection(struct v4l2_subdev *subdev, return -EINVAL; mutex_lock(&brx->entity.lock); - sel->r = *brx_get_compose(brx, state, sel->pad); + sel->r = *v4l2_subdev_state_get_compose(state, sel->pad); mutex_unlock(&brx->entity.lock); return 0; @@ -242,8 +234,7 @@ static int brx_set_selection(struct v4l2_subdev *subdev, * The compose rectangle top left corner must be inside the output * frame. */ - format = vsp1_entity_get_pad_format(&brx->entity, state, - brx->entity.source_pad); + format = v4l2_subdev_state_get_format(state, brx->entity.source_pad); sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1); sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1); @@ -251,11 +242,11 @@ static int brx_set_selection(struct v4l2_subdev *subdev, * Scaling isn't supported, the compose rectangle size must be identical * to the sink format size. */ - format = vsp1_entity_get_pad_format(&brx->entity, state, sel->pad); + format = v4l2_subdev_state_get_format(state, sel->pad); sel->r.width = format->width; sel->r.height = format->height; - compose = brx_get_compose(brx, state, sel->pad); + compose = v4l2_subdev_state_get_compose(state, sel->pad); *compose = sel->r; done: @@ -281,6 +272,7 @@ static const struct v4l2_subdev_ops brx_ops = { */ static void brx_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -290,8 +282,7 @@ static void brx_configure_stream(struct vsp1_entity *entity, unsigned int flags; unsigned int i; - format = vsp1_entity_get_pad_format(&brx->entity, brx->entity.state, - brx->entity.source_pad); + format = v4l2_subdev_state_get_format(state, brx->entity.source_pad); /* * The hardware is extremely flexible but we have no userspace API to diff --git a/drivers/media/platform/renesas/vsp1/vsp1_clu.c b/drivers/media/platform/renesas/vsp1/vsp1_clu.c index 625776a9bda4..98645bd2a983 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_clu.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_clu.c @@ -170,6 +170,7 @@ static const struct v4l2_subdev_ops clu_ops = { */ static void clu_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -181,8 +182,7 @@ static void clu_configure_stream(struct vsp1_entity *entity, * The yuv_mode can't be changed during streaming. Cache it internally * for future runtime configuration calls. */ - format = vsp1_entity_get_pad_format(&clu->entity, clu->entity.state, - CLU_PAD_SINK); + format = v4l2_subdev_state_get_format(state, CLU_PAD_SINK); clu->yuv_mode = format->code == MEDIA_BUS_FMT_AYUV8_1X32; } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c index 9b087bd8df7d..b5d1f238f7be 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c @@ -317,7 +317,10 @@ static int vsp1_du_pipeline_setup_brx(struct vsp1_device *vsp1, list_add_tail(&released_brx->list_pipe, &pipe->entities); - /* Add the BRx to the pipeline. */ + /* + * Add the BRx to the pipeline, inserting it just before the + * WPF. + */ dev_dbg(vsp1->dev, "%s: pipe %u: acquired %s\n", __func__, pipe->lif->index, BRX_NAME(brx)); @@ -326,7 +329,8 @@ static int vsp1_du_pipeline_setup_brx(struct vsp1_device *vsp1, pipe->brx->sink = &pipe->output->entity; pipe->brx->sink_pad = 0; - list_add_tail(&pipe->brx->list_pipe, &pipe->entities); + list_add_tail(&pipe->brx->list_pipe, + &pipe->output->entity.list_pipe); } /* @@ -420,7 +424,7 @@ static int vsp1_du_pipeline_setup_inputs(struct vsp1_device *vsp1, if (!rpf->entity.pipe) { rpf->entity.pipe = pipe; - list_add_tail(&rpf->entity.list_pipe, &pipe->entities); + list_add(&rpf->entity.list_pipe, &pipe->entities); } brx->inputs[i].rpf = rpf; @@ -546,6 +550,9 @@ static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe) struct vsp1_dl_body *dlb; unsigned int dl_flags = 0; + vsp1_pipeline_calculate_partition(pipe, &pipe->part_table[0], + drm_pipe->width, 0); + if (drm_pipe->force_brx_release) dl_flags |= VSP1_DL_FRAME_END_INTERNAL; if (pipe->output->writeback) @@ -567,9 +574,11 @@ static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe) } vsp1_entity_route_setup(entity, pipe, dlb); - vsp1_entity_configure_stream(entity, pipe, dl, dlb); + vsp1_entity_configure_stream(entity, entity->state, pipe, + dl, dlb); vsp1_entity_configure_frame(entity, pipe, dl, dlb); - vsp1_entity_configure_partition(entity, pipe, dl, dlb); + vsp1_entity_configure_partition(entity, pipe, + &pipe->part_table[0], dl, dlb); } vsp1_dl_list_commit(dl, dl_flags); @@ -733,6 +742,8 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, if (ret < 0) goto unlock; + vsp1_pipeline_dump(pipe, "LIF setup"); + /* Enable the VSP1. */ ret = vsp1_device_get(vsp1); if (ret < 0) @@ -906,6 +917,9 @@ void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index, } vsp1_du_pipeline_setup_inputs(vsp1, pipe); + + vsp1_pipeline_dump(pipe, "atomic update"); + vsp1_du_pipeline_configure(pipe); done: @@ -959,6 +973,9 @@ int vsp1_drm_init(struct vsp1_device *vsp1) vsp1_pipeline_init(pipe); + pipe->partitions = 1; + pipe->part_table = &drm_pipe->partition; + pipe->frame_end = vsp1_du_pipeline_frame_end; /* diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.h b/drivers/media/platform/renesas/vsp1/vsp1_drm.h index ab8b7e3161a2..3fd95b53f27e 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.h @@ -20,6 +20,7 @@ /** * struct vsp1_drm_pipeline - State for the API exposed to the DRM driver * @pipe: the VSP1 pipeline used for display + * @partition: the pre-calculated partition used by the pipeline * @width: output display width * @height: output display height * @force_brx_release: when set, release the BRx during the next reconfiguration @@ -31,6 +32,7 @@ */ struct vsp1_drm_pipeline { struct vsp1_pipeline pipe; + struct vsp1_partition partition; unsigned int width; unsigned int height; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c index 0a5a7f9cc870..8b8945bd8f10 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c @@ -70,12 +70,13 @@ void vsp1_entity_route_setup(struct vsp1_entity *entity, } void vsp1_entity_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { if (entity->ops->configure_stream) - entity->ops->configure_stream(entity, pipe, dl, dlb); + entity->ops->configure_stream(entity, state, pipe, dl, dlb); } void vsp1_entity_configure_frame(struct vsp1_entity *entity, @@ -89,11 +90,13 @@ void vsp1_entity_configure_frame(struct vsp1_entity *entity, void vsp1_entity_configure_partition(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + const struct vsp1_partition *partition, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { if (entity->ops->configure_partition) - entity->ops->configure_partition(entity, pipe, dl, dlb); + entity->ops->configure_partition(entity, pipe, partition, + dl, dlb); } /* ----------------------------------------------------------------------------- @@ -127,49 +130,6 @@ vsp1_entity_get_state(struct vsp1_entity *entity, } } -/** - * vsp1_entity_get_pad_format - Get a pad format from storage for an entity - * @entity: the entity - * @sd_state: the state storage - * @pad: the pad number - * - * Return the format stored in the given configuration for an entity's pad. The - * configuration can be an ACTIVE or TRY configuration. - */ -struct v4l2_mbus_framefmt * -vsp1_entity_get_pad_format(struct vsp1_entity *entity, - struct v4l2_subdev_state *sd_state, - unsigned int pad) -{ - return v4l2_subdev_state_get_format(sd_state, pad); -} - -/** - * vsp1_entity_get_pad_selection - Get a pad selection from storage for entity - * @entity: the entity - * @sd_state: the state storage - * @pad: the pad number - * @target: the selection target - * - * Return the selection rectangle stored in the given configuration for an - * entity's pad. The configuration can be an ACTIVE or TRY configuration. The - * selection target can be COMPOSE or CROP. - */ -struct v4l2_rect * -vsp1_entity_get_pad_selection(struct vsp1_entity *entity, - struct v4l2_subdev_state *sd_state, - unsigned int pad, unsigned int target) -{ - switch (target) { - case V4L2_SEL_TGT_COMPOSE: - return v4l2_subdev_state_get_compose(sd_state, pad); - case V4L2_SEL_TGT_CROP: - return v4l2_subdev_state_get_crop(sd_state, pad); - default: - return NULL; - } -} - /* * vsp1_subdev_get_pad_format - Subdev pad get_fmt handler * @subdev: V4L2 subdevice @@ -191,7 +151,7 @@ int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev, return -EINVAL; mutex_lock(&entity->lock); - fmt->format = *vsp1_entity_get_pad_format(entity, state, fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(state, fmt->pad); mutex_unlock(&entity->lock); return 0; @@ -238,7 +198,7 @@ int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev, return -EINVAL; mutex_lock(&entity->lock); - format = vsp1_entity_get_pad_format(entity, state, 0); + format = v4l2_subdev_state_get_format(state, 0); code->code = format->code; mutex_unlock(&entity->lock); } @@ -276,7 +236,7 @@ int vsp1_subdev_enum_frame_size(struct v4l2_subdev *subdev, if (!state) return -EINVAL; - format = vsp1_entity_get_pad_format(entity, state, fse->pad); + format = v4l2_subdev_state_get_format(state, fse->pad); mutex_lock(&entity->lock); @@ -346,7 +306,7 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, goto done; } - format = vsp1_entity_get_pad_format(entity, state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); if (fmt->pad == entity->source_pad) { /* The output format can't be modified. */ @@ -374,19 +334,17 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, fmt->format = *format; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(entity, state, entity->source_pad); + format = v4l2_subdev_state_get_format(state, entity->source_pad); *format = fmt->format; /* Reset the crop and compose rectangles. */ - selection = vsp1_entity_get_pad_selection(entity, state, fmt->pad, - V4L2_SEL_TGT_CROP); + selection = v4l2_subdev_state_get_crop(state, fmt->pad); selection->left = 0; selection->top = 0; selection->width = format->width; selection->height = format->height; - selection = vsp1_entity_get_pad_selection(entity, state, fmt->pad, - V4L2_SEL_TGT_COMPOSE); + selection = v4l2_subdev_state_get_compose(state, fmt->pad); selection->left = 0; selection->top = 0; selection->width = format->width; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.h b/drivers/media/platform/renesas/vsp1/vsp1_entity.h index 735f32dde4b5..1bcc9e27dfdc 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.h @@ -19,7 +19,6 @@ struct vsp1_dl_body; struct vsp1_dl_list; struct vsp1_pipeline; struct vsp1_partition; -struct vsp1_partition_window; enum vsp1_entity_type { VSP1_ENTITY_BRS, @@ -78,19 +77,30 @@ struct vsp1_route { * configuration. */ struct vsp1_entity_operations { - void (*destroy)(struct vsp1_entity *); - void (*configure_stream)(struct vsp1_entity *, struct vsp1_pipeline *, - struct vsp1_dl_list *, struct vsp1_dl_body *); - void (*configure_frame)(struct vsp1_entity *, struct vsp1_pipeline *, - struct vsp1_dl_list *, struct vsp1_dl_body *); - void (*configure_partition)(struct vsp1_entity *, - struct vsp1_pipeline *, - struct vsp1_dl_list *, - struct vsp1_dl_body *); - unsigned int (*max_width)(struct vsp1_entity *, struct vsp1_pipeline *); - void (*partition)(struct vsp1_entity *, struct vsp1_pipeline *, - struct vsp1_partition *, unsigned int, - struct vsp1_partition_window *); + void (*destroy)(struct vsp1_entity *entity); + void (*configure_stream)(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb); + void (*configure_frame)(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb); + void (*configure_partition)(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + const struct vsp1_partition *partition, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb); + unsigned int (*max_width)(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, + struct vsp1_pipeline *pipe); + void (*partition)(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, + struct vsp1_pipeline *pipe, + struct vsp1_partition *partition, + unsigned int index, + struct v4l2_rect *window); }; struct vsp1_entity { @@ -138,20 +148,13 @@ struct v4l2_subdev_state * vsp1_entity_get_state(struct vsp1_entity *entity, struct v4l2_subdev_state *sd_state, enum v4l2_subdev_format_whence which); -struct v4l2_mbus_framefmt * -vsp1_entity_get_pad_format(struct vsp1_entity *entity, - struct v4l2_subdev_state *sd_state, - unsigned int pad); -struct v4l2_rect * -vsp1_entity_get_pad_selection(struct vsp1_entity *entity, - struct v4l2_subdev_state *sd_state, - unsigned int pad, unsigned int target); void vsp1_entity_route_setup(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, struct vsp1_dl_body *dlb); void vsp1_entity_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb); @@ -163,6 +166,7 @@ void vsp1_entity_configure_frame(struct vsp1_entity *entity, void vsp1_entity_configure_partition(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + const struct vsp1_partition *partition, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hgo.c b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c index 40c571a987ef..2c8ce7175a4e 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hgo.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c @@ -130,6 +130,7 @@ static const struct v4l2_ctrl_config hgo_num_bins_control = { */ static void hgo_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -140,11 +141,8 @@ static void hgo_configure_stream(struct vsp1_entity *entity, unsigned int hratio; unsigned int vratio; - crop = vsp1_entity_get_pad_selection(entity, entity->state, - HISTO_PAD_SINK, V4L2_SEL_TGT_CROP); - compose = vsp1_entity_get_pad_selection(entity, entity->state, - HISTO_PAD_SINK, - V4L2_SEL_TGT_COMPOSE); + crop = v4l2_subdev_state_get_crop(state, HISTO_PAD_SINK); + compose = v4l2_subdev_state_get_compose(state, HISTO_PAD_SINK); vsp1_hgo_write(hgo, dlb, VI6_HGO_REGRST, VI6_HGO_REGRST_RCLEA); @@ -194,6 +192,16 @@ struct vsp1_hgo *vsp1_hgo_create(struct vsp1_device *vsp1) if (hgo == NULL) return ERR_PTR(-ENOMEM); + /* Initialize the video device and queue for statistics data. */ + ret = vsp1_histogram_init(vsp1, &hgo->histo, VSP1_ENTITY_HGO, "hgo", + &hgo_entity_ops, hgo_mbus_formats, + ARRAY_SIZE(hgo_mbus_formats), + HGO_DATA_SIZE, V4L2_META_FMT_VSP1_HGO); + if (ret < 0) { + vsp1_entity_destroy(&hgo->histo.entity); + return ERR_PTR(ret); + } + /* Initialize the control handler. */ v4l2_ctrl_handler_init(&hgo->ctrls.handler, vsp1->info->gen >= 3 ? 2 : 1); @@ -209,15 +217,5 @@ struct vsp1_hgo *vsp1_hgo_create(struct vsp1_device *vsp1) hgo->histo.entity.subdev.ctrl_handler = &hgo->ctrls.handler; - /* Initialize the video device and queue for statistics data. */ - ret = vsp1_histogram_init(vsp1, &hgo->histo, VSP1_ENTITY_HGO, "hgo", - &hgo_entity_ops, hgo_mbus_formats, - ARRAY_SIZE(hgo_mbus_formats), - HGO_DATA_SIZE, V4L2_META_FMT_VSP1_HGO); - if (ret < 0) { - vsp1_entity_destroy(&hgo->histo.entity); - return ERR_PTR(ret); - } - return hgo; } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hgt.c b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c index 8281b86874ab..858f330d44fa 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hgt.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c @@ -126,6 +126,7 @@ static const struct v4l2_ctrl_config hgt_hue_areas = { */ static void hgt_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -139,11 +140,8 @@ static void hgt_configure_stream(struct vsp1_entity *entity, u8 upper; unsigned int i; - crop = vsp1_entity_get_pad_selection(entity, entity->state, - HISTO_PAD_SINK, V4L2_SEL_TGT_CROP); - compose = vsp1_entity_get_pad_selection(entity, entity->state, - HISTO_PAD_SINK, - V4L2_SEL_TGT_COMPOSE); + crop = v4l2_subdev_state_get_crop(state, HISTO_PAD_SINK); + compose = v4l2_subdev_state_get_compose(state, HISTO_PAD_SINK); vsp1_hgt_write(hgt, dlb, VI6_HGT_REGRST, VI6_HGT_REGRST_RCLEA); @@ -193,12 +191,6 @@ struct vsp1_hgt *vsp1_hgt_create(struct vsp1_device *vsp1) if (hgt == NULL) return ERR_PTR(-ENOMEM); - /* Initialize the control handler. */ - v4l2_ctrl_handler_init(&hgt->ctrls, 1); - v4l2_ctrl_new_custom(&hgt->ctrls, &hgt_hue_areas, NULL); - - hgt->histo.entity.subdev.ctrl_handler = &hgt->ctrls; - /* Initialize the video device and queue for statistics data. */ ret = vsp1_histogram_init(vsp1, &hgt->histo, VSP1_ENTITY_HGT, "hgt", &hgt_entity_ops, hgt_mbus_formats, @@ -209,6 +201,12 @@ struct vsp1_hgt *vsp1_hgt_create(struct vsp1_device *vsp1) return ERR_PTR(ret); } + /* Initialize the control handler. */ + v4l2_ctrl_handler_init(&hgt->ctrls, 1); + v4l2_ctrl_new_custom(&hgt->ctrls, &hgt_hue_areas, NULL); + + hgt->histo.entity.subdev.ctrl_handler = &hgt->ctrls; + v4l2_ctrl_handler_setup(&hgt->ctrls); return hgt; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_histo.c b/drivers/media/platform/renesas/vsp1/vsp1_histo.c index 71155282ca11..9c2d4c91bfad 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_histo.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_histo.c @@ -36,9 +36,8 @@ struct vsp1_histogram_buffer * vsp1_histogram_buffer_get(struct vsp1_histogram *histo) { struct vsp1_histogram_buffer *buf = NULL; - unsigned long flags; - spin_lock_irqsave(&histo->irqlock, flags); + spin_lock(&histo->irqlock); if (list_empty(&histo->irqqueue)) goto done; @@ -49,7 +48,7 @@ vsp1_histogram_buffer_get(struct vsp1_histogram *histo) histo->readout = true; done: - spin_unlock_irqrestore(&histo->irqlock, flags); + spin_unlock(&histo->irqlock); return buf; } @@ -58,7 +57,6 @@ void vsp1_histogram_buffer_complete(struct vsp1_histogram *histo, size_t size) { struct vsp1_pipeline *pipe = histo->entity.pipe; - unsigned long flags; /* * The pipeline pointer is guaranteed to be valid as this function is @@ -70,10 +68,10 @@ void vsp1_histogram_buffer_complete(struct vsp1_histogram *histo, vb2_set_plane_payload(&buf->buf.vb2_buf, 0, size); vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE); - spin_lock_irqsave(&histo->irqlock, flags); + spin_lock(&histo->irqlock); histo->readout = false; wake_up(&histo->wait_queue); - spin_unlock_irqrestore(&histo->irqlock, flags); + spin_unlock(&histo->irqlock); } /* ----------------------------------------------------------------------------- @@ -124,11 +122,10 @@ static void histo_buffer_queue(struct vb2_buffer *vb) struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vsp1_histogram *histo = vb2_get_drv_priv(vb->vb2_queue); struct vsp1_histogram_buffer *buf = to_vsp1_histogram_buffer(vbuf); - unsigned long flags; - spin_lock_irqsave(&histo->irqlock, flags); + spin_lock_irq(&histo->irqlock); list_add_tail(&buf->queue, &histo->irqqueue); - spin_unlock_irqrestore(&histo->irqlock, flags); + spin_unlock_irq(&histo->irqlock); } static int histo_start_streaming(struct vb2_queue *vq, unsigned int count) @@ -140,9 +137,8 @@ static void histo_stop_streaming(struct vb2_queue *vq) { struct vsp1_histogram *histo = vb2_get_drv_priv(vq); struct vsp1_histogram_buffer *buffer; - unsigned long flags; - spin_lock_irqsave(&histo->irqlock, flags); + spin_lock_irq(&histo->irqlock); /* Remove all buffers from the IRQ queue. */ list_for_each_entry(buffer, &histo->irqqueue, queue) @@ -152,7 +148,7 @@ static void histo_stop_streaming(struct vb2_queue *vq) /* Wait for the buffer being read out (if any) to complete. */ wait_event_lock_irq(histo->wait_queue, !histo->readout, histo->irqlock); - spin_unlock_irqrestore(&histo->irqlock, flags); + spin_unlock_irq(&histo->irqlock); } static const struct vb2_ops histo_video_queue_qops = { @@ -222,9 +218,7 @@ static int histo_get_selection(struct v4l2_subdev *subdev, switch (sel->target) { case V4L2_SEL_TGT_COMPOSE_BOUNDS: case V4L2_SEL_TGT_COMPOSE_DEFAULT: - crop = vsp1_entity_get_pad_selection(&histo->entity, state, - HISTO_PAD_SINK, - V4L2_SEL_TGT_CROP); + crop = v4l2_subdev_state_get_crop(state, HISTO_PAD_SINK); sel->r.left = 0; sel->r.top = 0; sel->r.width = crop->width; @@ -233,8 +227,7 @@ static int histo_get_selection(struct v4l2_subdev *subdev, case V4L2_SEL_TGT_CROP_BOUNDS: case V4L2_SEL_TGT_CROP_DEFAULT: - format = vsp1_entity_get_pad_format(&histo->entity, state, - HISTO_PAD_SINK); + format = v4l2_subdev_state_get_format(state, HISTO_PAD_SINK); sel->r.left = 0; sel->r.top = 0; sel->r.width = format->width; @@ -242,9 +235,11 @@ static int histo_get_selection(struct v4l2_subdev *subdev, break; case V4L2_SEL_TGT_COMPOSE: + sel->r = *v4l2_subdev_state_get_compose(state, sel->pad); + break; + case V4L2_SEL_TGT_CROP: - sel->r = *vsp1_entity_get_pad_selection(&histo->entity, state, - sel->pad, sel->target); + sel->r = *v4l2_subdev_state_get_crop(state, sel->pad); break; default: @@ -261,13 +256,10 @@ static int histo_set_crop(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { - struct vsp1_histogram *histo = subdev_to_histo(subdev); struct v4l2_mbus_framefmt *format; - struct v4l2_rect *selection; /* The crop rectangle must be inside the input frame. */ - format = vsp1_entity_get_pad_format(&histo->entity, sd_state, - HISTO_PAD_SINK); + format = v4l2_subdev_state_get_format(sd_state, HISTO_PAD_SINK); sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1); sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1); sel->r.width = clamp_t(unsigned int, sel->r.width, HISTO_MIN_SIZE, @@ -276,14 +268,8 @@ static int histo_set_crop(struct v4l2_subdev *subdev, format->height - sel->r.top); /* Set the crop rectangle and reset the compose rectangle. */ - selection = vsp1_entity_get_pad_selection(&histo->entity, sd_state, - sel->pad, V4L2_SEL_TGT_CROP); - *selection = sel->r; - - selection = vsp1_entity_get_pad_selection(&histo->entity, sd_state, - sel->pad, - V4L2_SEL_TGT_COMPOSE); - *selection = sel->r; + *v4l2_subdev_state_get_crop(sd_state, sel->pad) = sel->r; + *v4l2_subdev_state_get_compose(sd_state, sel->pad) = sel->r; return 0; } @@ -292,7 +278,6 @@ static int histo_set_compose(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { - struct vsp1_histogram *histo = subdev_to_histo(subdev); struct v4l2_rect *compose; struct v4l2_rect *crop; unsigned int ratio; @@ -305,9 +290,7 @@ static int histo_set_compose(struct v4l2_subdev *subdev, sel->r.left = 0; sel->r.top = 0; - crop = vsp1_entity_get_pad_selection(&histo->entity, sd_state, - sel->pad, - V4L2_SEL_TGT_CROP); + crop = v4l2_subdev_state_get_crop(sd_state, sel->pad); /* * Clamp the width and height to acceptable values first and then @@ -332,9 +315,7 @@ static int histo_set_compose(struct v4l2_subdev *subdev, ratio = 1 << (crop->height * 2 / sel->r.height / 3); sel->r.height = crop->height / ratio; - compose = vsp1_entity_get_pad_selection(&histo->entity, sd_state, - sel->pad, - V4L2_SEL_TGT_COMPOSE); + compose = v4l2_subdev_state_get_compose(sd_state, sel->pad); *compose = sel->r; return 0; @@ -371,31 +352,22 @@ done: return ret; } -static int histo_get_format(struct v4l2_subdev *subdev, +static int histo_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { + struct vsp1_histogram *histo = subdev_to_histo(subdev); + if (fmt->pad == HISTO_PAD_SOURCE) { fmt->format.code = MEDIA_BUS_FMT_FIXED; fmt->format.width = 0; fmt->format.height = 0; fmt->format.field = V4L2_FIELD_NONE; fmt->format.colorspace = V4L2_COLORSPACE_RAW; + return 0; } - return vsp1_subdev_get_pad_format(subdev, sd_state, fmt); -} - -static int histo_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct vsp1_histogram *histo = subdev_to_histo(subdev); - - if (fmt->pad != HISTO_PAD_SINK) - return histo_get_format(subdev, sd_state, fmt); - return vsp1_subdev_set_pad_format(subdev, sd_state, fmt, histo->formats, histo->num_formats, HISTO_MIN_SIZE, HISTO_MIN_SIZE, @@ -405,7 +377,7 @@ static int histo_set_format(struct v4l2_subdev *subdev, static const struct v4l2_subdev_pad_ops histo_pad_ops = { .enum_mbus_code = histo_enum_mbus_code, .enum_frame_size = histo_enum_frame_size, - .get_fmt = histo_get_format, + .get_fmt = vsp1_subdev_get_pad_format, .set_fmt = histo_set_format, .get_selection = histo_get_selection, .set_selection = histo_set_selection, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c index bc1299c29ac9..8ba2a7c7305c 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c @@ -78,7 +78,7 @@ static int hsit_set_format(struct v4l2_subdev *subdev, goto done; } - format = vsp1_entity_get_pad_format(&hsit->entity, state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); if (fmt->pad == HSIT_PAD_SOURCE) { /* @@ -101,8 +101,7 @@ static int hsit_set_format(struct v4l2_subdev *subdev, fmt->format = *format; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&hsit->entity, state, - HSIT_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, HSIT_PAD_SOURCE); *format = fmt->format; format->code = hsit->inverse ? MEDIA_BUS_FMT_ARGB8888_1X32 : MEDIA_BUS_FMT_AHSV8888_1X32; @@ -128,6 +127,7 @@ static const struct v4l2_subdev_ops hsit_ops = { */ static void hsit_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_lif.c b/drivers/media/platform/renesas/vsp1/vsp1_lif.c index b1d21a54837b..b3d83d1c5306 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_lif.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_lif.c @@ -83,6 +83,7 @@ static const struct v4l2_subdev_ops lif_ops = { */ static void lif_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -93,8 +94,7 @@ static void lif_configure_stream(struct vsp1_entity *entity, unsigned int obth; unsigned int lbth; - format = vsp1_entity_get_pad_format(&lif->entity, lif->entity.state, - LIF_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, LIF_PAD_SOURCE); switch (entity->vsp1->version & VI6_IP_VERSION_MODEL_MASK) { case VI6_IP_VERSION_MODEL_VSPD_GEN2: diff --git a/drivers/media/platform/renesas/vsp1/vsp1_lut.c b/drivers/media/platform/renesas/vsp1/vsp1_lut.c index 451d24ab0b56..dd264e6532e0 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_lut.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_lut.c @@ -146,6 +146,7 @@ static const struct v4l2_subdev_ops lut_ops = { */ static void lut_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c index 68d05243c3ee..bb0739f684f3 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c @@ -301,6 +301,28 @@ void vsp1_pipeline_init(struct vsp1_pipeline *pipe) pipe->state = VSP1_PIPELINE_STOPPED; } +void __vsp1_pipeline_dump(struct _ddebug *dbg, struct vsp1_pipeline *pipe, + const char *msg) +{ + struct vsp1_device *vsp1 = pipe->output->entity.vsp1; + struct vsp1_entity *entity; + bool first = true; + + printk(KERN_DEBUG "%s: %s: pipe: ", dev_name(vsp1->dev), msg); + + list_for_each_entry(entity, &pipe->entities, list_pipe) { + const char *name; + + name = strchrnul(entity->subdev.name, ' '); + name = name ? name + 1 : entity->subdev.name; + + pr_cont("%s%s", first ? "" : ", ", name); + first = false; + } + + pr_cont("\n"); +} + /* Must be called with the pipe irqlock held. */ void vsp1_pipeline_run(struct vsp1_pipeline *pipe) { @@ -444,6 +466,10 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, vsp1_uds_set_alpha(pipe->uds, dlb, alpha); } +/* ----------------------------------------------------------------------------- + * VSP1 Partition Algorithm support + */ + /* * Propagate the partition calculations through the pipeline * @@ -452,17 +478,82 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, * source. Each entity must produce the partition required for the previous * entity in the pipeline. */ -void vsp1_pipeline_propagate_partition(struct vsp1_pipeline *pipe, - struct vsp1_partition *partition, - unsigned int index, - struct vsp1_partition_window *window) +static void vsp1_pipeline_propagate_partition(struct vsp1_pipeline *pipe, + struct vsp1_partition *partition, + unsigned int index, + struct v4l2_rect *window) { struct vsp1_entity *entity; list_for_each_entry_reverse(entity, &pipe->entities, list_pipe) { if (entity->ops->partition) - entity->ops->partition(entity, pipe, partition, index, - window); + entity->ops->partition(entity, entity->state, pipe, + partition, index, window); } } +/* + * vsp1_pipeline_calculate_partition - Calculate pipeline configuration for a + * partition + * + * @pipe: the pipeline + * @partition: partition that will hold the calculated values + * @div_size: pre-determined maximum partition division size + * @index: partition index + */ +void vsp1_pipeline_calculate_partition(struct vsp1_pipeline *pipe, + struct vsp1_partition *partition, + unsigned int div_size, + unsigned int index) +{ + const struct v4l2_mbus_framefmt *format; + struct v4l2_rect window; + unsigned int modulus; + + /* + * Partitions are computed on the size before rotation, use the format + * at the WPF sink. + */ + format = v4l2_subdev_state_get_format(pipe->output->entity.state, + RWPF_PAD_SINK); + + /* Initialise the partition with sane starting conditions. */ + window.left = index * div_size; + window.width = div_size; + window.top = 0; + window.height = format->height; + + modulus = format->width % div_size; + + /* + * We need to prevent the last partition from being smaller than the + * *minimum* width of the hardware capabilities. + * + * If the modulus is less than half of the partition size, + * the penultimate partition is reduced to half, which is added + * to the final partition: |1234|1234|1234|12|341| + * to prevent this: |1234|1234|1234|1234|1|. + */ + if (modulus) { + /* + * pipe->partitions is 1 based, whilst index is a 0 based index. + * Normalise this locally. + */ + unsigned int partitions = pipe->partitions - 1; + + if (modulus < div_size / 2) { + if (index == partitions - 1) { + /* Halve the penultimate partition. */ + window.width = div_size / 2; + } else if (index == partitions) { + /* Increase the final partition. */ + window.width = (div_size / 2) + modulus; + window.left -= div_size / 2; + } + } else if (index == partitions) { + window.width = modulus; + } + } + + vsp1_pipeline_propagate_partition(pipe, partition, index, &window); +} diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.h b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h index 674b5748d929..1ba7bdbad5a8 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h @@ -9,6 +9,7 @@ #ifndef __VSP1_PIPE_H__ #define __VSP1_PIPE_H__ +#include <linux/dynamic_debug.h> #include <linux/kref.h> #include <linux/list.h> #include <linux/spinlock.h> @@ -54,17 +55,6 @@ enum vsp1_pipeline_state { }; /* - * struct vsp1_partition_window - Partition window coordinates - * @left: horizontal coordinate of the partition start in pixels relative to the - * left edge of the image - * @width: partition width in pixels - */ -struct vsp1_partition_window { - unsigned int left; - unsigned int width; -}; - -/* * struct vsp1_partition - A description of a slice for the partition algorithm * @rpf: The RPF partition window configuration * @uds_sink: The UDS input partition window configuration @@ -73,11 +63,11 @@ struct vsp1_partition_window { * @wpf: The WPF partition window configuration */ struct vsp1_partition { - struct vsp1_partition_window rpf; - struct vsp1_partition_window uds_sink; - struct vsp1_partition_window uds_source; - struct vsp1_partition_window sru; - struct vsp1_partition_window wpf; + struct v4l2_rect rpf[VSP1_MAX_RPF]; + struct v4l2_rect uds_sink; + struct v4l2_rect uds_source; + struct v4l2_rect sru; + struct v4l2_rect wpf; }; /* @@ -106,7 +96,6 @@ struct vsp1_partition { * @configured: when false the @stream_config shall be written to the hardware * @interlaced: True when the pipeline is configured in interlaced mode * @partitions: The number of partitions used to process one frame - * @partition: The current partition for configuration to process * @part_table: The pre-calculated partitions used by the pipeline */ struct vsp1_pipeline { @@ -146,7 +135,6 @@ struct vsp1_pipeline { bool interlaced; unsigned int partitions; - struct vsp1_partition *partition; struct vsp1_partition *part_table; u32 underrun_count; @@ -155,6 +143,24 @@ struct vsp1_pipeline { void vsp1_pipeline_reset(struct vsp1_pipeline *pipe); void vsp1_pipeline_init(struct vsp1_pipeline *pipe); +void __vsp1_pipeline_dump(struct _ddebug *, struct vsp1_pipeline *pipe, + const char *msg); + +#if defined(CONFIG_DYNAMIC_DEBUG) || \ + (defined(CONFIG_DYNAMIC_DEBUG_CORE) && defined(DYNAMIC_DEBUG_MODULE)) +#define vsp1_pipeline_dump(pipe, msg) \ + _dynamic_func_call("vsp1_pipeline_dump()", __vsp1_pipeline_dump, pipe, msg) +#elif defined(DEBUG) +#define vsp1_pipeline_dump(pipe, msg) \ + __vsp1_pipeline_dump(NULL, pipe, msg) +#else +#define vsp1_pipeline_dump(pipe, msg) \ +({ \ + if (0) \ + __vsp1_pipeline_dump(NULL, pipe, msg); \ +}) +#endif + void vsp1_pipeline_run(struct vsp1_pipeline *pipe); bool vsp1_pipeline_stopped(struct vsp1_pipeline *pipe); int vsp1_pipeline_stop(struct vsp1_pipeline *pipe); @@ -166,10 +172,10 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, struct vsp1_dl_body *dlb, unsigned int alpha); -void vsp1_pipeline_propagate_partition(struct vsp1_pipeline *pipe, +void vsp1_pipeline_calculate_partition(struct vsp1_pipeline *pipe, struct vsp1_partition *partition, - unsigned int index, - struct vsp1_partition_window *window); + unsigned int div_size, + unsigned int index); const struct vsp1_format_info *vsp1_get_format_info(struct vsp1_device *vsp1, u32 fourcc); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c index c47579efc65f..5c8b3ba1bd3c 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c @@ -48,6 +48,7 @@ static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf, */ static void rpf_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -80,12 +81,8 @@ static void rpf_configure_stream(struct vsp1_entity *entity, vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_PSTRIDE, pstride); /* Format */ - sink_format = vsp1_entity_get_pad_format(&rpf->entity, - rpf->entity.state, - RWPF_PAD_SINK); - source_format = vsp1_entity_get_pad_format(&rpf->entity, - rpf->entity.state, - RWPF_PAD_SOURCE); + sink_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); + source_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE); infmt = VI6_RPF_INFMT_CIPM | (fmtinfo->hwfmt << VI6_RPF_INFMT_RDFMT_SHIFT); @@ -157,10 +154,8 @@ static void rpf_configure_stream(struct vsp1_entity *entity, if (pipe->brx) { const struct v4l2_rect *compose; - compose = vsp1_entity_get_pad_selection(pipe->brx, - pipe->brx->state, - rpf->brx_input, - V4L2_SEL_TGT_COMPOSE); + compose = v4l2_subdev_state_get_compose(pipe->brx->state, + rpf->brx_input); left = compose->left; top = compose->top; } @@ -284,6 +279,7 @@ static void rpf_configure_frame(struct vsp1_entity *entity, static void rpf_configure_partition(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + const struct vsp1_partition *partition, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { @@ -292,7 +288,7 @@ static void rpf_configure_partition(struct vsp1_entity *entity, struct vsp1_device *vsp1 = rpf->entity.vsp1; const struct vsp1_format_info *fmtinfo = rpf->fmtinfo; const struct v4l2_pix_format_mplane *format = &rpf->format; - struct v4l2_rect crop; + struct v4l2_rect crop = partition->rpf[rpf->entity.index]; /* * Source size and crop offsets. @@ -302,22 +298,6 @@ static void rpf_configure_partition(struct vsp1_entity *entity, * offsets are needed, as planes 2 and 3 always have identical * strides. */ - crop = *vsp1_rwpf_get_crop(rpf, rpf->entity.state); - - /* - * Partition Algorithm Control - * - * The partition algorithm can split this frame into multiple - * slices. We must scale our partition window based on the pipe - * configuration to match the destination partition window. - * To achieve this, we adjust our crop to provide a 'sub-crop' - * matching the expected partition window. Only 'left' and - * 'width' need to be adjusted. - */ - if (pipe->partitions > 1) { - crop.width = pipe->partition->rpf.width; - crop.left += pipe->partition->rpf.left; - } if (pipe->interlaced) { crop.height = round_down(crop.height / 2, fmtinfo->vsub); @@ -366,12 +346,30 @@ static void rpf_configure_partition(struct vsp1_entity *entity, } static void rpf_partition(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_partition *partition, unsigned int partition_idx, - struct vsp1_partition_window *window) + struct v4l2_rect *window) { - partition->rpf = *window; + struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev); + struct v4l2_rect *rpf_rect = &partition->rpf[rpf->entity.index]; + + /* + * Partition Algorithm Control + * + * The partition algorithm can split this frame into multiple slices. We + * must adjust our partition window based on the pipe configuration to + * match the destination partition window. To achieve this, we adjust + * our crop to provide a 'sub-crop' matching the expected partition + * window. + */ + *rpf_rect = *v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK); + + if (pipe->partitions > 1) { + rpf_rect->width = window->width; + rpf_rect->left += window->left; + } } static const struct vsp1_entity_operations rpf_entity_ops = { diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c index 09fb6ffa14e2..9d38203e73d0 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c @@ -16,12 +16,6 @@ #define RWPF_MIN_WIDTH 1 #define RWPF_MIN_HEIGHT 1 -struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, - struct v4l2_subdev_state *sd_state) -{ - return v4l2_subdev_state_get_crop(sd_state, RWPF_PAD_SINK); -} - /* ----------------------------------------------------------------------------- * V4L2 Subdevice Operations */ @@ -79,7 +73,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32) fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32; - format = vsp1_entity_get_pad_format(&rwpf->entity, state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); if (fmt->pad == RWPF_PAD_SOURCE) { /* @@ -105,7 +99,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_rect *crop; /* Update the sink crop rectangle. */ - crop = vsp1_rwpf_get_crop(rwpf, state); + crop = v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK); crop->left = 0; crop->top = 0; crop->width = fmt->format.width; @@ -113,8 +107,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, } /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&rwpf->entity, state, - RWPF_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE); *format = fmt->format; if (rwpf->flip.rotate) { @@ -153,12 +146,11 @@ static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, switch (sel->target) { case V4L2_SEL_TGT_CROP: - sel->r = *vsp1_rwpf_get_crop(rwpf, state); + sel->r = *v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK); break; case V4L2_SEL_TGT_CROP_BOUNDS: - format = vsp1_entity_get_pad_format(&rwpf->entity, state, - RWPF_PAD_SINK); + format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); sel->r.left = 0; sel->r.top = 0; sel->r.width = format->width; @@ -204,8 +196,7 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, } /* Make sure the crop rectangle is entirely contained in the image. */ - format = vsp1_entity_get_pad_format(&rwpf->entity, state, - RWPF_PAD_SINK); + format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); /* * Restrict the crop rectangle coordinates to multiples of 2 to avoid @@ -225,12 +216,11 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, sel->r.height = min_t(unsigned int, sel->r.height, format->height - sel->r.top); - crop = vsp1_rwpf_get_crop(rwpf, state); + crop = v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK); *crop = sel->r; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&rwpf->entity, state, - RWPF_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE); format->width = crop->width; format->height = crop->height; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h index e0d212c70b2f..5ac9f0a6fafc 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h @@ -85,7 +85,4 @@ int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf, unsigned int ncontrols); extern const struct v4l2_subdev_ops vsp1_rwpf_subdev_ops; -struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, - struct v4l2_subdev_state *sd_state); - #endif /* __VSP1_RWPF_H__ */ diff --git a/drivers/media/platform/renesas/vsp1/vsp1_sru.c b/drivers/media/platform/renesas/vsp1/vsp1_sru.c index 11e008aa9f20..1759ce642e6e 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_sru.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_sru.c @@ -131,7 +131,7 @@ static int sru_enum_frame_size(struct v4l2_subdev *subdev, if (!state) return -EINVAL; - format = vsp1_entity_get_pad_format(&sru->entity, state, SRU_PAD_SINK); + format = v4l2_subdev_state_get_format(state, SRU_PAD_SINK); mutex_lock(&sru->entity.lock); @@ -184,8 +184,7 @@ static void sru_try_format(struct vsp1_sru *sru, case SRU_PAD_SOURCE: /* The SRU can't perform format conversion. */ - format = vsp1_entity_get_pad_format(&sru->entity, sd_state, - SRU_PAD_SINK); + format = v4l2_subdev_state_get_format(sd_state, SRU_PAD_SINK); fmt->code = format->code; /* @@ -234,13 +233,12 @@ static int sru_set_format(struct v4l2_subdev *subdev, sru_try_format(sru, state, fmt->pad, &fmt->format); - format = vsp1_entity_get_pad_format(&sru->entity, state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); *format = fmt->format; if (fmt->pad == SRU_PAD_SINK) { /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&sru->entity, state, - SRU_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE); *format = fmt->format; sru_try_format(sru, state, SRU_PAD_SOURCE, format); @@ -267,6 +265,7 @@ static const struct v4l2_subdev_ops sru_ops = { */ static void sru_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -277,10 +276,8 @@ static void sru_configure_stream(struct vsp1_entity *entity, struct v4l2_mbus_framefmt *output; u32 ctrl0; - input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, - SRU_PAD_SINK); - output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, - SRU_PAD_SOURCE); + input = v4l2_subdev_state_get_format(state, SRU_PAD_SINK); + output = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE); if (input->code == MEDIA_BUS_FMT_ARGB8888_1X32) ctrl0 = VI6_SRU_CTRL0_PARAM2 | VI6_SRU_CTRL0_PARAM3 @@ -301,16 +298,14 @@ static void sru_configure_stream(struct vsp1_entity *entity, } static unsigned int sru_max_width(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe) { - struct vsp1_sru *sru = to_sru(&entity->subdev); struct v4l2_mbus_framefmt *input; struct v4l2_mbus_framefmt *output; - input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, - SRU_PAD_SINK); - output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, - SRU_PAD_SOURCE); + input = v4l2_subdev_state_get_format(state, SRU_PAD_SINK); + output = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE); /* * The maximum input width of the SRU is 288 input pixels, but 32 @@ -324,24 +319,24 @@ static unsigned int sru_max_width(struct vsp1_entity *entity, } static void sru_partition(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_partition *partition, unsigned int partition_idx, - struct vsp1_partition_window *window) + struct v4l2_rect *window) { - struct vsp1_sru *sru = to_sru(&entity->subdev); struct v4l2_mbus_framefmt *input; struct v4l2_mbus_framefmt *output; - input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, - SRU_PAD_SINK); - output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, - SRU_PAD_SOURCE); + input = v4l2_subdev_state_get_format(state, SRU_PAD_SINK); + output = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE); /* Adapt if SRUx2 is enabled. */ if (input->width != output->width) { window->width /= 2; window->left /= 2; + window->height /= 2; + window->top /= 2; } partition->sru = *window; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uds.c b/drivers/media/platform/renesas/vsp1/vsp1_uds.c index d89f1197b86c..c5a38478cf8c 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_uds.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uds.c @@ -136,7 +136,7 @@ static int uds_enum_frame_size(struct v4l2_subdev *subdev, if (!state) return -EINVAL; - format = vsp1_entity_get_pad_format(&uds->entity, state, UDS_PAD_SINK); + format = v4l2_subdev_state_get_format(state, UDS_PAD_SINK); mutex_lock(&uds->entity.lock); @@ -183,8 +183,7 @@ static void uds_try_format(struct vsp1_uds *uds, case UDS_PAD_SOURCE: /* The UDS scales but can't perform format conversion. */ - format = vsp1_entity_get_pad_format(&uds->entity, sd_state, - UDS_PAD_SINK); + format = v4l2_subdev_state_get_format(sd_state, UDS_PAD_SINK); fmt->code = format->code; uds_output_limits(format->width, &minimum, &maximum); @@ -217,13 +216,12 @@ static int uds_set_format(struct v4l2_subdev *subdev, uds_try_format(uds, state, fmt->pad, &fmt->format); - format = vsp1_entity_get_pad_format(&uds->entity, state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); *format = fmt->format; if (fmt->pad == UDS_PAD_SINK) { /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&uds->entity, state, - UDS_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, UDS_PAD_SOURCE); *format = fmt->format; uds_try_format(uds, state, UDS_PAD_SOURCE, format); @@ -254,6 +252,7 @@ static const struct v4l2_subdev_ops uds_ops = { */ static void uds_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -265,10 +264,8 @@ static void uds_configure_stream(struct vsp1_entity *entity, unsigned int vscale; bool multitap; - input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SINK); - output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SOURCE); + input = v4l2_subdev_state_get_format(state, UDS_PAD_SINK); + output = v4l2_subdev_state_get_format(state, UDS_PAD_SOURCE); hscale = uds_compute_ratio(input->width, output->width); vscale = uds_compute_ratio(input->height, output->height); @@ -303,15 +300,11 @@ static void uds_configure_stream(struct vsp1_entity *entity, static void uds_configure_partition(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + const struct vsp1_partition *partition, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { struct vsp1_uds *uds = to_uds(&entity->subdev); - struct vsp1_partition *partition = pipe->partition; - const struct v4l2_mbus_framefmt *output; - - output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SOURCE); /* Input size clipping. */ vsp1_uds_write(uds, dlb, VI6_UDS_HSZCLIP, VI6_UDS_HSZCLIP_HCEN | @@ -323,22 +316,20 @@ static void uds_configure_partition(struct vsp1_entity *entity, vsp1_uds_write(uds, dlb, VI6_UDS_CLIP_SIZE, (partition->uds_source.width << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) | - (output->height + (partition->uds_source.height << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT)); } static unsigned int uds_max_width(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe) { - struct vsp1_uds *uds = to_uds(&entity->subdev); const struct v4l2_mbus_framefmt *output; const struct v4l2_mbus_framefmt *input; unsigned int hscale; - input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SINK); - output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SOURCE); + input = v4l2_subdev_state_get_format(state, UDS_PAD_SINK); + output = v4l2_subdev_state_get_format(state, UDS_PAD_SOURCE); hscale = output->width / input->width; /* @@ -364,28 +355,26 @@ static unsigned int uds_max_width(struct vsp1_entity *entity, */ static void uds_partition(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_partition *partition, unsigned int partition_idx, - struct vsp1_partition_window *window) + struct v4l2_rect *window) { - struct vsp1_uds *uds = to_uds(&entity->subdev); const struct v4l2_mbus_framefmt *output; const struct v4l2_mbus_framefmt *input; - /* Initialise the partition state. */ - partition->uds_sink = *window; - partition->uds_source = *window; - - input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SINK); - output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SOURCE); + input = v4l2_subdev_state_get_format(state, UDS_PAD_SINK); + output = v4l2_subdev_state_get_format(state, UDS_PAD_SOURCE); partition->uds_sink.width = window->width * input->width / output->width; partition->uds_sink.left = window->left * input->width / output->width; + partition->uds_sink.height = input->height; + partition->uds_sink.top = 0; + + partition->uds_source = *window; *window = partition->uds_sink; } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uif.c b/drivers/media/platform/renesas/vsp1/vsp1_uif.c index f66936a28a2a..edaf28b544d2 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_uif.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uif.c @@ -104,8 +104,7 @@ static int uif_get_selection(struct v4l2_subdev *subdev, switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: case V4L2_SEL_TGT_CROP_DEFAULT: - format = vsp1_entity_get_pad_format(&uif->entity, state, - UIF_PAD_SINK); + format = v4l2_subdev_state_get_format(state, UIF_PAD_SINK); sel->r.left = 0; sel->r.top = 0; sel->r.width = format->width; @@ -113,8 +112,7 @@ static int uif_get_selection(struct v4l2_subdev *subdev, break; case V4L2_SEL_TGT_CROP: - sel->r = *vsp1_entity_get_pad_selection(&uif->entity, state, - sel->pad, sel->target); + sel->r = *v4l2_subdev_state_get_crop(state, sel->pad); break; default: @@ -150,7 +148,7 @@ static int uif_set_selection(struct v4l2_subdev *subdev, } /* The crop rectangle must be inside the input frame. */ - format = vsp1_entity_get_pad_format(&uif->entity, state, UIF_PAD_SINK); + format = v4l2_subdev_state_get_format(state, UIF_PAD_SINK); sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1); sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1); @@ -160,8 +158,7 @@ static int uif_set_selection(struct v4l2_subdev *subdev, format->height - sel->r.top); /* Store the crop rectangle. */ - selection = vsp1_entity_get_pad_selection(&uif->entity, state, - sel->pad, V4L2_SEL_TGT_CROP); + selection = v4l2_subdev_state_get_crop(state, sel->pad); *selection = sel->r; done: @@ -191,6 +188,7 @@ static const struct v4l2_subdev_ops uif_ops = { */ static void uif_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -203,8 +201,7 @@ static void uif_configure_stream(struct vsp1_entity *entity, vsp1_uif_write(uif, dlb, VI6_UIF_DISCOM_DOCMPMR, VI6_UIF_DISCOM_DOCMPMR_SEL(9)); - crop = vsp1_entity_get_pad_selection(entity, entity->state, - UIF_PAD_SINK, V4L2_SEL_TGT_CROP); + crop = v4l2_subdev_state_get_crop(state, UIF_PAD_SINK); left = crop->left; width = crop->width; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c index 5a9cb0e5640e..fdb46ec0c872 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_video.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c @@ -78,8 +78,14 @@ static int vsp1_video_verify_format(struct vsp1_video *video) if (video->rwpf->fmtinfo->mbus != fmt.format.code || video->rwpf->format.height != fmt.format.height || - video->rwpf->format.width != fmt.format.width) - return -EINVAL; + video->rwpf->format.width != fmt.format.width) { + dev_dbg(video->vsp1->dev, + "Format mismatch: 0x%04x/%ux%u != 0x%04x/%ux%u\n", + video->rwpf->fmtinfo->mbus, video->rwpf->format.width, + video->rwpf->format.height, fmt.format.code, + fmt.format.width, fmt.format.height); + return -EPIPE; + } return 0; } @@ -173,131 +179,6 @@ static int __vsp1_video_try_format(struct vsp1_video *video, } /* ----------------------------------------------------------------------------- - * VSP1 Partition Algorithm support - */ - -/** - * vsp1_video_calculate_partition - Calculate the active partition output window - * - * @pipe: the pipeline - * @partition: partition that will hold the calculated values - * @div_size: pre-determined maximum partition division size - * @index: partition index - */ -static void vsp1_video_calculate_partition(struct vsp1_pipeline *pipe, - struct vsp1_partition *partition, - unsigned int div_size, - unsigned int index) -{ - const struct v4l2_mbus_framefmt *format; - struct vsp1_partition_window window; - unsigned int modulus; - - /* - * Partitions are computed on the size before rotation, use the format - * at the WPF sink. - */ - format = vsp1_entity_get_pad_format(&pipe->output->entity, - pipe->output->entity.state, - RWPF_PAD_SINK); - - /* A single partition simply processes the output size in full. */ - if (pipe->partitions <= 1) { - window.left = 0; - window.width = format->width; - - vsp1_pipeline_propagate_partition(pipe, partition, index, - &window); - return; - } - - /* Initialise the partition with sane starting conditions. */ - window.left = index * div_size; - window.width = div_size; - - modulus = format->width % div_size; - - /* - * We need to prevent the last partition from being smaller than the - * *minimum* width of the hardware capabilities. - * - * If the modulus is less than half of the partition size, - * the penultimate partition is reduced to half, which is added - * to the final partition: |1234|1234|1234|12|341| - * to prevent this: |1234|1234|1234|1234|1|. - */ - if (modulus) { - /* - * pipe->partitions is 1 based, whilst index is a 0 based index. - * Normalise this locally. - */ - unsigned int partitions = pipe->partitions - 1; - - if (modulus < div_size / 2) { - if (index == partitions - 1) { - /* Halve the penultimate partition. */ - window.width = div_size / 2; - } else if (index == partitions) { - /* Increase the final partition. */ - window.width = (div_size / 2) + modulus; - window.left -= div_size / 2; - } - } else if (index == partitions) { - window.width = modulus; - } - } - - vsp1_pipeline_propagate_partition(pipe, partition, index, &window); -} - -static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe) -{ - struct vsp1_device *vsp1 = pipe->output->entity.vsp1; - const struct v4l2_mbus_framefmt *format; - struct vsp1_entity *entity; - unsigned int div_size; - unsigned int i; - - /* - * Partitions are computed on the size before rotation, use the format - * at the WPF sink. - */ - format = vsp1_entity_get_pad_format(&pipe->output->entity, - pipe->output->entity.state, - RWPF_PAD_SINK); - div_size = format->width; - - /* - * Only Gen3+ hardware requires image partitioning, Gen2 will operate - * with a single partition that covers the whole output. - */ - if (vsp1->info->gen >= 3) { - list_for_each_entry(entity, &pipe->entities, list_pipe) { - unsigned int entity_max; - - if (!entity->ops->max_width) - continue; - - entity_max = entity->ops->max_width(entity, pipe); - if (entity_max) - div_size = min(div_size, entity_max); - } - } - - pipe->partitions = DIV_ROUND_UP(format->width, div_size); - pipe->part_table = kcalloc(pipe->partitions, sizeof(*pipe->part_table), - GFP_KERNEL); - if (!pipe->part_table) - return -ENOMEM; - - for (i = 0; i < pipe->partitions; ++i) - vsp1_video_calculate_partition(pipe, &pipe->part_table[i], - div_size, i); - - return 0; -} - -/* ----------------------------------------------------------------------------- * Pipeline Management */ @@ -365,13 +246,12 @@ static void vsp1_video_pipeline_run_partition(struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, unsigned int partition) { + struct vsp1_partition *part = &pipe->part_table[partition]; struct vsp1_dl_body *dlb = vsp1_dl_list_get_body0(dl); struct vsp1_entity *entity; - pipe->partition = &pipe->part_table[partition]; - list_for_each_entry(entity, &pipe->entities, list_pipe) - vsp1_entity_configure_partition(entity, pipe, dl, dlb); + vsp1_entity_configure_partition(entity, pipe, part, dl, dlb); } static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe) @@ -646,11 +526,19 @@ static int vsp1_video_pipeline_build(struct vsp1_pipeline *pipe, static int vsp1_video_pipeline_init(struct vsp1_pipeline *pipe, struct vsp1_video *video) { + int ret; + vsp1_pipeline_init(pipe); pipe->frame_end = vsp1_video_pipeline_frame_end; - return vsp1_video_pipeline_build(pipe, video); + ret = vsp1_video_pipeline_build(pipe, video); + if (ret) + return ret; + + vsp1_pipeline_dump(pipe, "video"); + + return 0; } static struct vsp1_pipeline *vsp1_video_pipeline_get(struct vsp1_video *video) @@ -784,6 +672,54 @@ static void vsp1_video_buffer_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&pipe->irqlock, flags); } +static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe) +{ + struct vsp1_device *vsp1 = pipe->output->entity.vsp1; + const struct v4l2_mbus_framefmt *format; + struct vsp1_entity *entity; + unsigned int div_size; + unsigned int i; + + /* + * Partitions are computed on the size before rotation, use the format + * at the WPF sink. + */ + format = v4l2_subdev_state_get_format(pipe->output->entity.state, + RWPF_PAD_SINK); + div_size = format->width; + + /* + * Only Gen3+ hardware requires image partitioning, Gen2 will operate + * with a single partition that covers the whole output. + */ + if (vsp1->info->gen >= 3) { + list_for_each_entry(entity, &pipe->entities, list_pipe) { + unsigned int entity_max; + + if (!entity->ops->max_width) + continue; + + entity_max = entity->ops->max_width(entity, + entity->state, + pipe); + if (entity_max) + div_size = min(div_size, entity_max); + } + } + + pipe->partitions = DIV_ROUND_UP(format->width, div_size); + pipe->part_table = kcalloc(pipe->partitions, sizeof(*pipe->part_table), + GFP_KERNEL); + if (!pipe->part_table) + return -ENOMEM; + + for (i = 0; i < pipe->partitions; ++i) + vsp1_pipeline_calculate_partition(pipe, &pipe->part_table[i], + div_size, i); + + return 0; +} + static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe) { struct vsp1_entity *entity; @@ -826,7 +762,7 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe) list_for_each_entry(entity, &pipe->entities, list_pipe) { vsp1_entity_route_setup(entity, pipe, pipe->stream_config); - vsp1_entity_configure_stream(entity, pipe, NULL, + vsp1_entity_configure_stream(entity, entity->state, pipe, NULL, pipe->stream_config); } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c index 9693aeab1cac..f176750ccd98 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c @@ -65,12 +65,10 @@ static int vsp1_wpf_set_rotation(struct vsp1_rwpf *wpf, unsigned int rotation) goto done; } - sink_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.state, - RWPF_PAD_SINK); - source_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.state, - RWPF_PAD_SOURCE); + sink_format = v4l2_subdev_state_get_format(wpf->entity.state, + RWPF_PAD_SINK); + source_format = v4l2_subdev_state_get_format(wpf->entity.state, + RWPF_PAD_SOURCE); mutex_lock(&wpf->entity.lock); @@ -231,6 +229,7 @@ static int wpf_configure_writeback_chain(struct vsp1_rwpf *wpf, } static void wpf_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -245,12 +244,8 @@ static void wpf_configure_stream(struct vsp1_entity *entity, u32 srcrpf = 0; int ret; - sink_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.state, - RWPF_PAD_SINK); - source_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.state, - RWPF_PAD_SOURCE); + sink_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); + source_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE); /* Format */ if (!pipe->lif || wpf->writeback) { @@ -367,13 +362,13 @@ static void wpf_configure_frame(struct vsp1_entity *entity, static void wpf_configure_partition(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + const struct vsp1_partition *partition, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); struct vsp1_device *vsp1 = wpf->entity.vsp1; struct vsp1_rwpf_memory mem = wpf->mem; - const struct v4l2_mbus_framefmt *sink_format; const struct v4l2_pix_format_mplane *format = &wpf->format; const struct vsp1_format_info *fmtinfo = wpf->fmtinfo; unsigned int width; @@ -383,21 +378,13 @@ static void wpf_configure_partition(struct vsp1_entity *entity, unsigned int flip; unsigned int i; - sink_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.state, - RWPF_PAD_SINK); - width = sink_format->width; - height = sink_format->height; - left = 0; - /* - * Cropping. The partition algorithm can split the image into - * multiple slices. + * Cropping. The partition algorithm can split the image into multiple + * slices. */ - if (pipe->partitions > 1) { - width = pipe->partition->wpf.width; - left = pipe->partition->wpf.left; - } + width = partition->wpf.width; + left = partition->wpf.left; + height = partition->wpf.height; vsp1_wpf_write(wpf, dlb, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN | (0 << VI6_WPF_SZCLIP_OFST_SHIFT) | @@ -508,6 +495,7 @@ static void wpf_configure_partition(struct vsp1_entity *entity, } static unsigned int wpf_max_width(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe) { struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); @@ -516,10 +504,11 @@ static unsigned int wpf_max_width(struct vsp1_entity *entity, } static void wpf_partition(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_partition *partition, unsigned int partition_idx, - struct vsp1_partition_window *window) + struct v4l2_rect *window) { partition->wpf = *window; } diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c index 662c81b6d0b5..70808049d2e8 100644 --- a/drivers/media/platform/rockchip/rga/rga-buf.c +++ b/drivers/media/platform/rockchip/rga/rga-buf.c @@ -195,6 +195,11 @@ static int rga_buf_start_streaming(struct vb2_queue *q, unsigned int count) return ret; } + if (V4L2_TYPE_IS_OUTPUT(q->type)) + ctx->osequence = 0; + else + ctx->csequence = 0; + return 0; } diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index 00fdfa9e10bc..0e768f3e9eda 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -43,6 +43,8 @@ static void device_run(void *prv) rga->curr = ctx; src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + src->sequence = ctx->osequence++; + dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); rga_hw_start(rga, vb_to_rga(src), vb_to_rga(dst)); @@ -75,6 +77,8 @@ static irqreturn_t rga_isr(int irq, void *prv) v4l2_m2m_buf_copy_metadata(src, dst, true); + dst->sequence = ctx->csequence++; + v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE); v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE); v4l2_m2m_job_finish(rga->m2m_dev, ctx->fh.m2m_ctx); diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index 3502dff6055c..8105bb2efe57 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -57,6 +57,9 @@ struct rga_ctx { struct rga_frame out; struct v4l2_ctrl_handler ctrl_handler; + int osequence; + int csequence; + /* Control values */ u32 op; u32 hflip; diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c index e45a213baf49..91301d17d356 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c @@ -173,7 +173,7 @@ static void rkisp1_gasket_disable(struct rkisp1_device *rkisp1) * or at the frame end interrupt */ static void rkisp1_config_ism(struct rkisp1_isp *isp, - struct v4l2_subdev_state *sd_state) + const struct v4l2_subdev_state *sd_state) { const struct v4l2_rect *src_crop = v4l2_subdev_state_get_crop(sd_state, @@ -201,7 +201,7 @@ static void rkisp1_config_ism(struct rkisp1_isp *isp, * configure ISP blocks with input format, size...... */ static int rkisp1_config_isp(struct rkisp1_isp *isp, - struct v4l2_subdev_state *sd_state, + const struct v4l2_subdev_state *sd_state, enum v4l2_mbus_type mbus_type, u32 mbus_flags) { struct rkisp1_device *rkisp1 = isp->rkisp1; @@ -309,7 +309,7 @@ static int rkisp1_config_isp(struct rkisp1_isp *isp, if (src_fmt->pixel_enc == V4L2_PIXEL_ENC_BAYER) { rkisp1_params_disable(&rkisp1->params); } else { - struct v4l2_mbus_framefmt *src_frm; + const struct v4l2_mbus_framefmt *src_frm; src_frm = v4l2_subdev_state_get_format(sd_state, RKISP1_ISP_PAD_SOURCE_VIDEO); @@ -429,7 +429,7 @@ static void rkisp1_config_clk(struct rkisp1_isp *isp) } static int rkisp1_isp_start(struct rkisp1_isp *isp, - struct v4l2_subdev_state *sd_state, + const struct v4l2_subdev_state *sd_state, struct media_pad *source) { struct rkisp1_device *rkisp1 = isp->rkisp1; diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c index 6f3931ca5b51..1fa991227fa9 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c @@ -135,11 +135,11 @@ static void rkisp1_dcrop_disable(struct rkisp1_resizer *rsz, /* configure dual-crop unit */ static void rkisp1_dcrop_config(struct rkisp1_resizer *rsz, - struct v4l2_subdev_state *sd_state) + const struct v4l2_subdev_state *sd_state) { struct rkisp1_device *rkisp1 = rsz->rkisp1; - struct v4l2_mbus_framefmt *sink_fmt; - struct v4l2_rect *sink_crop; + const struct v4l2_mbus_framefmt *sink_fmt; + const struct v4l2_rect *sink_crop; u32 dc_ctrl; sink_crop = v4l2_subdev_state_get_crop(sd_state, RKISP1_RSZ_PAD_SINK); @@ -264,7 +264,7 @@ static void rkisp1_rsz_config_regs(struct rkisp1_resizer *rsz, } static void rkisp1_rsz_config(struct rkisp1_resizer *rsz, - struct v4l2_subdev_state *sd_state, + const struct v4l2_subdev_state *sd_state, enum rkisp1_shadow_regs_when when) { const struct rkisp1_rsz_yuv_mbus_info *sink_yuv_info, *src_yuv_info; diff --git a/drivers/media/platform/samsung/exynos4-is/common.c b/drivers/media/platform/samsung/exynos4-is/common.c index e41333535eac..77007f1a909b 100644 --- a/drivers/media/platform/samsung/exynos4-is/common.c +++ b/drivers/media/platform/samsung/exynos4-is/common.c @@ -44,4 +44,5 @@ void __fimc_vidioc_querycap(struct device *dev, struct v4l2_capability *cap) } EXPORT_SYMBOL(__fimc_vidioc_querycap); +MODULE_DESCRIPTION("Samsung S5P/EXYNOS4 SoC Camera Subsystem driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is.c b/drivers/media/platform/samsung/exynos4-is/fimc-is.c index 39aab667910d..0a4b58daf924 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-is.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is.c @@ -999,4 +999,5 @@ module_exit(fimc_is_module_exit); MODULE_ALIAS("platform:" FIMC_IS_DRV_NAME); MODULE_AUTHOR("Younghwan Joo <yhwan.joo@samsung.com>"); MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); +MODULE_DESCRIPTION("Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c index d1d860fa3454..1a4d75443215 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c @@ -1662,5 +1662,6 @@ static struct platform_driver fimc_lite_driver = { } }; module_platform_driver(fimc_lite_driver); +MODULE_DESCRIPTION("Samsung EXYNOS FIMC-LITE (camera host interface) driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:" FIMC_LITE_DRV_NAME); diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c index 2f58a0d0df85..67d3d6e50d2e 100644 --- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c +++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c @@ -72,16 +72,16 @@ static void c8sectpfe_timer_interrupt(struct timer_list *t) /* is this descriptor initialised and TP enabled */ if (channel->irec && readl(channel->irec + DMA_PRDS_TPENABLE)) - tasklet_schedule(&channel->tsklet); + queue_work(system_bh_wq, &channel->bh_work); } fei->timer.expires = jiffies + msecs_to_jiffies(POLL_MSECS); add_timer(&fei->timer); } -static void channel_swdemux_tsklet(struct tasklet_struct *t) +static void channel_swdemux_bh_work(struct work_struct *t) { - struct channel_info *channel = from_tasklet(channel, t, tsklet); + struct channel_info *channel = from_work(channel, t, bh_work); struct c8sectpfei *fei; unsigned long wp, rp; int pos, num_packets, n, size; @@ -210,7 +210,7 @@ static int c8sectpfe_start_feed(struct dvb_demux_feed *dvbdmxfeed) dev_dbg(fei->dev, "Starting channel=%p\n", channel); - tasklet_setup(&channel->tsklet, channel_swdemux_tsklet); + INIT_WORK(&channel->bh_work, channel_swdemux_bh_work); /* Reset the internal inputblock sram pointers */ writel(channel->fifo, @@ -303,7 +303,7 @@ static int c8sectpfe_stop_feed(struct dvb_demux_feed *dvbdmxfeed) /* disable this channels descriptor */ writel(0, channel->irec + DMA_PRDS_TPENABLE); - tasklet_disable(&channel->tsklet); + disable_work_sync(&channel->bh_work); /* now request memdma channel goes idle */ idlereq = (1 << channel->tsin_id) | IDLEREQ; @@ -630,8 +630,8 @@ static int configure_memdma_and_inputblock(struct c8sectpfei *fei, writel(tsin->back_buffer_busaddr, tsin->irec + DMA_PRDS_BUSWP_TP(0)); writel(tsin->back_buffer_busaddr, tsin->irec + DMA_PRDS_BUSRP_TP(0)); - /* initialize tasklet */ - tasklet_setup(&tsin->tsklet, channel_swdemux_tsklet); + /* initialize bh work */ + INIT_WORK(&tsin->bh_work, channel_swdemux_bh_work); return 0; diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.h b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.h index bf377cc82225..c1b124c6ef12 100644 --- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.h +++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.h @@ -51,7 +51,7 @@ struct channel_info { unsigned long fifo; struct completion idle_completion; - struct tasklet_struct tsklet; + struct work_struct bh_work; struct c8sectpfei *fei; void __iomem *irec; diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h index 8e1bfd860524..3fe177b59b16 100644 --- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h +++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h @@ -16,8 +16,8 @@ void c8sectpfe_debugfs_init(struct c8sectpfei *); void c8sectpfe_debugfs_exit(struct c8sectpfei *); #else -static inline void c8sectpfe_debugfs_init(struct c8sectpfei *) {}; -static inline void c8sectpfe_debugfs_exit(struct c8sectpfei *) {}; +static inline void c8sectpfe_debugfs_init(struct c8sectpfei *fei) {}; +static inline void c8sectpfe_debugfs_exit(struct c8sectpfei *fei) {}; #endif #endif /* __C8SECTPFE_DEBUG_H */ diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c index 4acc3b90d03a..7f771ea49b78 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c @@ -202,8 +202,8 @@ static int dcmipp_create_subdevs(struct dcmipp_device *dcmipp) return 0; err_init_entity: - while (i > 0) - dcmipp->pipe_cfg->ents[i - 1].release(dcmipp->entity[i - 1]); + while (i-- > 0) + dcmipp->pipe_cfg->ents[i].release(dcmipp->entity[i]); return ret; } diff --git a/drivers/media/platform/ti/vpe/vpdma.c b/drivers/media/platform/ti/vpe/vpdma.c index f8998a8ad371..da90d7f03f82 100644 --- a/drivers/media/platform/ti/vpe/vpdma.c +++ b/drivers/media/platform/ti/vpe/vpdma.c @@ -1173,4 +1173,5 @@ EXPORT_SYMBOL(vpdma_create); MODULE_AUTHOR("Texas Instruments Inc."); MODULE_FIRMWARE(VPDMA_FIRMWARE); +MODULE_DESCRIPTION("TI VPDMA helper library"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/verisilicon/Kconfig b/drivers/media/platform/verisilicon/Kconfig index 9a34d14c6e40..149d0b32c324 100644 --- a/drivers/media/platform/verisilicon/Kconfig +++ b/drivers/media/platform/verisilicon/Kconfig @@ -12,6 +12,7 @@ config VIDEO_HANTRO select VIDEOBUF2_VMALLOC select V4L2_MEM2MEM_DEV select V4L2_H264 + select V4L2_JPEG_HELPER select V4L2_VP9 help Support for the Hantro IP based Video Processing Units present on diff --git a/drivers/media/platform/verisilicon/hantro_jpeg.c b/drivers/media/platform/verisilicon/hantro_jpeg.c index d07b1b449b61..13a60638e43f 100644 --- a/drivers/media/platform/verisilicon/hantro_jpeg.c +++ b/drivers/media/platform/verisilicon/hantro_jpeg.c @@ -11,6 +11,7 @@ #include <linux/build_bug.h> #include <linux/kernel.h> #include <linux/string.h> +#include <media/v4l2-jpeg.h> #include "hantro_jpeg.h" #include "hantro.h" @@ -24,42 +25,6 @@ #define HUFF_CHROMA_DC_OFF 394 #define HUFF_CHROMA_AC_OFF 427 -/* Default tables from JPEG ITU-T.81 - * (ISO/IEC 10918-1) Annex K, tables K.1 and K.2 - */ -static const unsigned char luma_q_table[] = { - 0x10, 0x0b, 0x0a, 0x10, 0x18, 0x28, 0x33, 0x3d, - 0x0c, 0x0c, 0x0e, 0x13, 0x1a, 0x3a, 0x3c, 0x37, - 0x0e, 0x0d, 0x10, 0x18, 0x28, 0x39, 0x45, 0x38, - 0x0e, 0x11, 0x16, 0x1d, 0x33, 0x57, 0x50, 0x3e, - 0x12, 0x16, 0x25, 0x38, 0x44, 0x6d, 0x67, 0x4d, - 0x18, 0x23, 0x37, 0x40, 0x51, 0x68, 0x71, 0x5c, - 0x31, 0x40, 0x4e, 0x57, 0x67, 0x79, 0x78, 0x65, - 0x48, 0x5c, 0x5f, 0x62, 0x70, 0x64, 0x67, 0x63 -}; - -static const unsigned char chroma_q_table[] = { - 0x11, 0x12, 0x18, 0x2f, 0x63, 0x63, 0x63, 0x63, - 0x12, 0x15, 0x1a, 0x42, 0x63, 0x63, 0x63, 0x63, - 0x18, 0x1a, 0x38, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x2f, 0x42, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63 -}; - -static const unsigned char zigzag[] = { - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63 -}; - static const u32 hw_reorder[] = { 0, 8, 16, 24, 1, 9, 17, 25, 32, 40, 48, 56, 33, 41, 49, 57, @@ -71,73 +36,6 @@ static const u32 hw_reorder[] = { 38, 46, 54, 62, 39, 47, 55, 63 }; -/* Huffman tables are shared with CODA */ -static const unsigned char luma_dc_table[] = { - 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, -}; - -static const unsigned char chroma_dc_table[] = { - 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, -}; - -static const unsigned char luma_ac_table[] = { - 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, - 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, - 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, - 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, - 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, - 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, - 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, - 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, - 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, - 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, - 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, - 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, - 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, - 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, - 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, - 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, - 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, - 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, - 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, - 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, - 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, - 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, - 0xf9, 0xfa, -}; - -static const unsigned char chroma_ac_table[] = { - 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, - 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, - 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, - 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, - 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, - 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, - 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, - 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, - 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, - 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, - 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, - 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, - 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, - 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, - 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, - 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, - 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, - 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, - 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, - 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, - 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, - 0xf9, 0xfa, -}; - /* For simplicity, we keep a pre-formatted JPEG header, * and we'll use fixed offsets to change the width, height * quantization tables, etc. @@ -292,11 +190,11 @@ jpeg_scale_quant_table(unsigned char *file_q_tab, { int i; - BUILD_BUG_ON(ARRAY_SIZE(zigzag) != JPEG_QUANT_SIZE); + BUILD_BUG_ON(ARRAY_SIZE(v4l2_jpeg_zigzag_scan_index) != JPEG_QUANT_SIZE); BUILD_BUG_ON(ARRAY_SIZE(hw_reorder) != JPEG_QUANT_SIZE); for (i = 0; i < JPEG_QUANT_SIZE; i++) { - file_q_tab[i] = jpeg_scale_qp(tab[zigzag[i]], scale); + file_q_tab[i] = jpeg_scale_qp(tab[v4l2_jpeg_zigzag_scan_index[i]], scale); reordered_q_tab[i] = jpeg_scale_qp(tab[hw_reorder[i]], scale); } } @@ -304,7 +202,6 @@ jpeg_scale_quant_table(unsigned char *file_q_tab, static void jpeg_set_quality(struct hantro_jpeg_ctx *ctx) { int scale; - /* * Non-linear scaling factor: * [5,50] -> [1000..100], [51,100] -> [98..0] @@ -314,15 +211,17 @@ static void jpeg_set_quality(struct hantro_jpeg_ctx *ctx) else scale = 200 - 2 * ctx->quality; - BUILD_BUG_ON(ARRAY_SIZE(luma_q_table) != JPEG_QUANT_SIZE); - BUILD_BUG_ON(ARRAY_SIZE(chroma_q_table) != JPEG_QUANT_SIZE); + BUILD_BUG_ON(ARRAY_SIZE(v4l2_jpeg_ref_table_luma_qt) != JPEG_QUANT_SIZE); + BUILD_BUG_ON(ARRAY_SIZE(v4l2_jpeg_ref_table_chroma_qt) != JPEG_QUANT_SIZE); BUILD_BUG_ON(ARRAY_SIZE(ctx->hw_luma_qtable) != JPEG_QUANT_SIZE); BUILD_BUG_ON(ARRAY_SIZE(ctx->hw_chroma_qtable) != JPEG_QUANT_SIZE); jpeg_scale_quant_table(ctx->buffer + LUMA_QUANT_OFF, - ctx->hw_luma_qtable, luma_q_table, scale); + ctx->hw_luma_qtable, + (const unsigned char *)v4l2_jpeg_ref_table_luma_qt, scale); jpeg_scale_quant_table(ctx->buffer + CHROMA_QUANT_OFF, - ctx->hw_chroma_qtable, chroma_q_table, scale); + ctx->hw_chroma_qtable, + (const unsigned char *)v4l2_jpeg_ref_table_chroma_qt, scale); } void hantro_jpeg_header_assemble(struct hantro_jpeg_ctx *ctx) @@ -337,12 +236,10 @@ void hantro_jpeg_header_assemble(struct hantro_jpeg_ctx *ctx) buf[WIDTH_OFF + 0] = ctx->width >> 8; buf[WIDTH_OFF + 1] = ctx->width; - memcpy(buf + HUFF_LUMA_DC_OFF, luma_dc_table, sizeof(luma_dc_table)); - memcpy(buf + HUFF_LUMA_AC_OFF, luma_ac_table, sizeof(luma_ac_table)); - memcpy(buf + HUFF_CHROMA_DC_OFF, chroma_dc_table, - sizeof(chroma_dc_table)); - memcpy(buf + HUFF_CHROMA_AC_OFF, chroma_ac_table, - sizeof(chroma_ac_table)); + memcpy(buf + HUFF_LUMA_DC_OFF, v4l2_jpeg_ref_table_luma_dc_ht, V4L2_JPEG_REF_HT_DC_LEN); + memcpy(buf + HUFF_LUMA_AC_OFF, v4l2_jpeg_ref_table_luma_ac_ht, V4L2_JPEG_REF_HT_AC_LEN); + memcpy(buf + HUFF_CHROMA_DC_OFF, v4l2_jpeg_ref_table_chroma_dc_ht, V4L2_JPEG_REF_HT_DC_LEN); + memcpy(buf + HUFF_CHROMA_AC_OFF, v4l2_jpeg_ref_table_chroma_ac_ht, V4L2_JPEG_REF_HT_AC_LEN); jpeg_set_quality(ctx); } diff --git a/drivers/media/platform/xilinx/xilinx-dma.c b/drivers/media/platform/xilinx/xilinx-dma.c index a96de5d388a1..a1687b868a44 100644 --- a/drivers/media/platform/xilinx/xilinx-dma.c +++ b/drivers/media/platform/xilinx/xilinx-dma.c @@ -348,8 +348,8 @@ static void xvip_dma_buffer_queue(struct vb2_buffer *vb) } dma->xt.frame_size = 1; - dma->sgl[0].size = dma->format.width * dma->fmtinfo->bpp; - dma->sgl[0].icg = dma->format.bytesperline - dma->sgl[0].size; + dma->sgl.size = dma->format.width * dma->fmtinfo->bpp; + dma->sgl.icg = dma->format.bytesperline - dma->sgl.size; dma->xt.numf = dma->format.height; desc = dmaengine_prep_interleaved_dma(dma->dma, &dma->xt, flags); diff --git a/drivers/media/platform/xilinx/xilinx-dma.h b/drivers/media/platform/xilinx/xilinx-dma.h index 9c6d4c18d1a9..18f77e1a7b39 100644 --- a/drivers/media/platform/xilinx/xilinx-dma.h +++ b/drivers/media/platform/xilinx/xilinx-dma.h @@ -97,7 +97,7 @@ struct xvip_dma { struct dma_chan *dma; unsigned int align; struct dma_interleaved_template xt; - struct data_chunk sgl[1]; + struct data_chunk sgl; }; #define to_xvip_dma(vdev) container_of(vdev, struct xvip_dma, video) diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c index dc0c1d8d23f0..af0a70910099 100644 --- a/drivers/media/radio/si470x/radio-si470x-common.c +++ b/drivers/media/radio/si470x/radio-si470x-common.c @@ -790,4 +790,5 @@ const struct video_device si470x_viddev_template = { }; EXPORT_SYMBOL_GPL(si470x_viddev_template); +MODULE_DESCRIPTION("Core radio driver for Si470x FM Radio Receivers"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/radio/wl128x/fmdrv.h b/drivers/media/radio/wl128x/fmdrv.h index da8920169df8..03117a41dbd4 100644 --- a/drivers/media/radio/wl128x/fmdrv.h +++ b/drivers/media/radio/wl128x/fmdrv.h @@ -15,6 +15,7 @@ #include <sound/core.h> #include <sound/initval.h> #include <linux/timer.h> +#include <linux/workqueue.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-common.h> #include <media/v4l2-device.h> @@ -200,10 +201,10 @@ struct fmdev { int streg_cbdata; /* status of ST registration */ struct sk_buff_head rx_q; /* RX queue */ - struct tasklet_struct rx_task; /* RX Tasklet */ + struct work_struct rx_bh_work; /* RX BH Work */ struct sk_buff_head tx_q; /* TX queue */ - struct tasklet_struct tx_task; /* TX Tasklet */ + struct work_struct tx_bh_work; /* TX BH Work */ unsigned long last_tx_jiffies; /* Timestamp of last pkt sent */ atomic_t tx_cnt; /* Number of packets can send at a time */ diff --git a/drivers/media/radio/wl128x/fmdrv_common.c b/drivers/media/radio/wl128x/fmdrv_common.c index 3da8e5102bec..3d36f323a8f8 100644 --- a/drivers/media/radio/wl128x/fmdrv_common.c +++ b/drivers/media/radio/wl128x/fmdrv_common.c @@ -9,7 +9,7 @@ * one Channel-8 command to be sent to the chip). * 2) Sending each Channel-8 command to the chip and reading * response back over Shared Transport. - * 3) Managing TX and RX Queues and Tasklets. + * 3) Managing TX and RX Queues and BH bh Works. * 4) Handling FM Interrupt packet and taking appropriate action. * 5) Loading FM firmware to the chip (common, FM TX, and FM RX * firmware files based on mode selection) @@ -244,10 +244,10 @@ void fmc_update_region_info(struct fmdev *fmdev, u8 region_to_set) } /* - * FM common sub-module will schedule this tasklet whenever it receives + * FM common sub-module will queue this bh work whenever it receives * FM packet from ST driver. */ -static void recv_tasklet(struct tasklet_struct *t) +static void recv_bh_work(struct work_struct *t) { struct fmdev *fmdev; struct fm_irq *irq_info; @@ -256,7 +256,7 @@ static void recv_tasklet(struct tasklet_struct *t) u8 num_fm_hci_cmds; unsigned long flags; - fmdev = from_tasklet(fmdev, t, tx_task); + fmdev = from_work(fmdev, t, tx_bh_work); irq_info = &fmdev->irq_info; /* Process all packets in the RX queue */ while ((skb = skb_dequeue(&fmdev->rx_q))) { @@ -322,22 +322,22 @@ static void recv_tasklet(struct tasklet_struct *t) /* * Check flow control field. If Num_FM_HCI_Commands field is - * not zero, schedule FM TX tasklet. + * not zero, queue FM TX bh work. */ if (num_fm_hci_cmds && atomic_read(&fmdev->tx_cnt)) if (!skb_queue_empty(&fmdev->tx_q)) - tasklet_schedule(&fmdev->tx_task); + queue_work(system_bh_wq, &fmdev->tx_bh_work); } } -/* FM send tasklet: is scheduled when FM packet has to be sent to chip */ -static void send_tasklet(struct tasklet_struct *t) +/* FM send_bh_work: is scheduled when FM packet has to be sent to chip */ +static void send_bh_work(struct work_struct *t) { struct fmdev *fmdev; struct sk_buff *skb; int len; - fmdev = from_tasklet(fmdev, t, tx_task); + fmdev = from_work(fmdev, t, tx_bh_work); if (!atomic_read(&fmdev->tx_cnt)) return; @@ -366,7 +366,7 @@ static void send_tasklet(struct tasklet_struct *t) if (len < 0) { kfree_skb(skb); fmdev->resp_comp = NULL; - fmerr("TX tasklet failed to send skb(%p)\n", skb); + fmerr("TX bh work failed to send skb(%p)\n", skb); atomic_set(&fmdev->tx_cnt, 1); } else { fmdev->last_tx_jiffies = jiffies; @@ -374,7 +374,7 @@ static void send_tasklet(struct tasklet_struct *t) } /* - * Queues FM Channel-8 packet to FM TX queue and schedules FM TX tasklet for + * Queues FM Channel-8 packet to FM TX queue and schedules FM TX bh work for * transmission */ static int fm_send_cmd(struct fmdev *fmdev, u8 fm_op, u16 type, void *payload, @@ -440,7 +440,7 @@ static int fm_send_cmd(struct fmdev *fmdev, u8 fm_op, u16 type, void *payload, fm_cb(skb)->completion = wait_completion; skb_queue_tail(&fmdev->tx_q, skb); - tasklet_schedule(&fmdev->tx_task); + queue_work(system_bh_wq, &fmdev->tx_bh_work); return 0; } @@ -462,7 +462,7 @@ int fmc_send_cmd(struct fmdev *fmdev, u8 fm_op, u16 type, void *payload, if (!wait_for_completion_timeout(&fmdev->maintask_comp, FM_DRV_TX_TIMEOUT)) { - fmerr("Timeout(%d sec),didn't get regcompletion signal from RX tasklet\n", + fmerr("Timeout(%d sec),didn't get regcompletion signal from RX bh work\n", jiffies_to_msecs(FM_DRV_TX_TIMEOUT) / 1000); return -ETIMEDOUT; } @@ -1455,7 +1455,7 @@ static long fm_st_receive(void *arg, struct sk_buff *skb) memcpy(skb_push(skb, 1), &skb->cb[0], 1); skb_queue_tail(&fmdev->rx_q, skb); - tasklet_schedule(&fmdev->rx_task); + queue_work(system_bh_wq, &fmdev->rx_bh_work); return 0; } @@ -1537,13 +1537,13 @@ int fmc_prepare(struct fmdev *fmdev) spin_lock_init(&fmdev->rds_buff_lock); spin_lock_init(&fmdev->resp_skb_lock); - /* Initialize TX queue and TX tasklet */ + /* Initialize TX queue and TX bh work */ skb_queue_head_init(&fmdev->tx_q); - tasklet_setup(&fmdev->tx_task, send_tasklet); + INIT_WORK(&fmdev->tx_bh_work, send_bh_work); - /* Initialize RX Queue and RX tasklet */ + /* Initialize RX Queue and RX bh work */ skb_queue_head_init(&fmdev->rx_q); - tasklet_setup(&fmdev->rx_task, recv_tasklet); + INIT_WORK(&fmdev->rx_bh_work, recv_bh_work); fmdev->irq_info.stage = 0; atomic_set(&fmdev->tx_cnt, 1); @@ -1589,8 +1589,8 @@ int fmc_release(struct fmdev *fmdev) /* Service pending read */ wake_up_interruptible(&fmdev->rx.rds.read_queue); - tasklet_kill(&fmdev->tx_task); - tasklet_kill(&fmdev->rx_task); + cancel_work_sync(&fmdev->tx_bh_work); + cancel_work_sync(&fmdev->rx_bh_work); skb_queue_purge(&fmdev->tx_q); skb_queue_purge(&fmdev->rx_q); diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index 0b55314a8082..8f1361bcce3a 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -1148,10 +1148,7 @@ static int imon_ir_change_protocol(struct rc_dev *rc, u64 *rc_proto) memcpy(ictx->usb_tx_buf, &ir_proto_packet, sizeof(ir_proto_packet)); - if (!mutex_is_locked(&ictx->lock)) { - unlock = true; - mutex_lock(&ictx->lock); - } + unlock = mutex_trylock(&ictx->lock); retval = send_packet(ictx); if (retval) diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index f19558fdab0c..7fdf0d9edbfd 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -84,6 +84,7 @@ obj-$(CONFIG_RC_MAP) += \ rc-msi-digivox-ii.o \ rc-msi-tvanywhere.o \ rc-msi-tvanywhere-plus.o \ + rc-mygica-utv3.o \ rc-nebula.o \ rc-nec-terratec-cinergy-xs.o \ rc-norwood.o \ diff --git a/drivers/media/rc/keymaps/rc-mygica-utv3.c b/drivers/media/rc/keymaps/rc-mygica-utv3.c new file mode 100644 index 000000000000..f32b8281459b --- /dev/null +++ b/drivers/media/rc/keymaps/rc-mygica-utv3.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* rc-mygica-utv3.c - Keytable for the MyGica UTV3 Analog USB2.0 TV Box + * + * Copyright (c) 2024 by Nils Rothaug + */ + +#include <media/rc-map.h> +#include <linux/module.h> + +static struct rc_map_table mygica_utv3[] = { + { 0x0d, KEY_MUTE }, + { 0x38, KEY_VIDEO }, /* Source */ + { 0x14, KEY_RADIO }, /* FM Radio */ + { 0x0c, KEY_POWER2 }, + + { 0x01, KEY_NUMERIC_1}, + { 0x02, KEY_NUMERIC_2}, + { 0x03, KEY_NUMERIC_3}, + { 0x04, KEY_NUMERIC_4}, + { 0x05, KEY_NUMERIC_5}, + { 0x06, KEY_NUMERIC_6}, + { 0x07, KEY_NUMERIC_7}, + { 0x08, KEY_NUMERIC_8}, + { 0x09, KEY_NUMERIC_9}, + { 0x00, KEY_NUMERIC_0}, + + { 0x0a, KEY_DIGITS }, /* Single/double/triple digit */ + { 0x0e, KEY_CAMERA }, /* Snapshot */ + { 0x0f, KEY_ZOOM }, /* Full Screen */ + { 0x29, KEY_LAST }, /* Recall (return to previous channel) */ + + { 0x17, KEY_PLAY }, + { 0x1f, KEY_RECORD }, + { 0x0b, KEY_STOP }, + { 0x16, KEY_PAUSE }, + + { 0x20, KEY_CHANNELUP }, + { 0x21, KEY_CHANNELDOWN }, + { 0x10, KEY_VOLUMEUP }, + { 0x11, KEY_VOLUMEDOWN }, + { 0x26, KEY_REWIND }, + { 0x27, KEY_FASTFORWARD }, +}; + +static struct rc_map_list mygica_utv3_map = { + .map = { + .scan = mygica_utv3, + .size = ARRAY_SIZE(mygica_utv3), + .rc_proto = RC_PROTO_RC5, + .name = RC_MAP_MYGICA_UTV3, + } +}; + +static int __init init_rc_map_mygica_utv3(void) +{ + return rc_map_register(&mygica_utv3_map); +} + +static void __exit exit_rc_map_mygica_utv3(void) +{ + rc_map_unregister(&mygica_utv3_map); +} + +module_init(init_rc_map_mygica_utv3) +module_exit(exit_rc_map_mygica_utv3) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Nils Rothaug"); +MODULE_DESCRIPTION("MyGica UTV3 Analog USB2.0 TV Box remote keytable"); diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index c76ba24c1f55..cd7af4d88b7f 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -494,7 +494,6 @@ struct mceusb_dev { u32 carrier; unsigned char tx_mask; - char name[128]; char phys[64]; enum mceusb_model_type model; @@ -774,7 +773,7 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, u8 *buf, int buf_len, /* * Schedule work that can't be done in interrupt handlers - * (mceusb_dev_recv() and mce_write_callback()) nor tasklets. + * (mceusb_dev_recv() and mce_write_callback()) nor BH work. * Invokes mceusb_deferred_kevent() for recovering from * error events specified by the kevent bit field. */ @@ -1591,16 +1590,10 @@ static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir) goto out; } - snprintf(ir->name, sizeof(ir->name), "%s (%04x:%04x)", - mceusb_model[ir->model].name ? - mceusb_model[ir->model].name : - "Media Center Ed. eHome Infrared Remote Transceiver", - le16_to_cpu(ir->usbdev->descriptor.idVendor), - le16_to_cpu(ir->usbdev->descriptor.idProduct)); - usb_make_path(ir->usbdev, ir->phys, sizeof(ir->phys)); - rc->device_name = ir->name; + rc->device_name = mceusb_model[ir->model].name ? : + "Media Center Ed. eHome Infrared Remote Transceiver"; rc->input_phys = ir->phys; usb_to_input_id(ir->usbdev, &rc->input_id); rc->dev.parent = dev; diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 6bdad6341844..a4c539b17cf3 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -2092,4 +2092,5 @@ subsys_initcall(rc_core_init); module_exit(rc_core_exit); MODULE_AUTHOR("Mauro Carvalho Chehab"); +MODULE_DESCRIPTION("Remote Controller core module"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/spi/gs1662.c b/drivers/media/spi/gs1662.c index dc5c4c055d29..7adf55fd0707 100644 --- a/drivers/media/spi/gs1662.c +++ b/drivers/media/spi/gs1662.c @@ -55,14 +55,6 @@ struct gs_reg_fmt { struct v4l2_dv_timings format; }; -struct gs_reg_fmt_custom { - u16 reg_value; - __u32 width; - __u32 height; - __u64 pixelclock; - __u32 interlaced; -}; - static const struct spi_device_id gs_id[] = { { "gs1662", 0 }, { } diff --git a/drivers/media/test-drivers/vimc/vimc-capture.c b/drivers/media/test-drivers/vimc/vimc-capture.c index ba7550b8ba7e..89506ae00901 100644 --- a/drivers/media/test-drivers/vimc/vimc-capture.c +++ b/drivers/media/test-drivers/vimc/vimc-capture.c @@ -494,7 +494,7 @@ err_free_vcapture: return ERR_PTR(ret); } -struct vimc_ent_type vimc_capture_type = { +const 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.c b/drivers/media/test-drivers/vimc/vimc-common.c index 2e72974e35b4..4f4fcb26e236 100644 --- a/drivers/media/test-drivers/vimc/vimc-common.c +++ b/drivers/media/test-drivers/vimc/vimc-common.c @@ -8,6 +8,8 @@ #include <linux/init.h> #include <linux/module.h> +#include <media/v4l2-ctrls.h> + #include "vimc-common.h" /* @@ -358,6 +360,7 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved, u32 function, u16 num_pads, struct media_pad *pads, + const struct v4l2_subdev_internal_ops *int_ops, const struct v4l2_subdev_ops *sd_ops) { int ret; @@ -367,6 +370,7 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved, /* Initialize the subdev */ v4l2_subdev_init(sd, sd_ops); + sd->internal_ops = int_ops; sd->entity.function = function; sd->entity.ops = &vimc_ent_sd_mops; sd->owner = THIS_MODULE; @@ -383,17 +387,36 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved, if (ret) return ret; + /* + * Finalize the subdev initialization if it supports active states. Use + * the control handler lock as the state lock if available. + */ + if (int_ops && int_ops->init_state) { + if (sd->ctrl_handler) + sd->state_lock = sd->ctrl_handler->lock; + + ret = v4l2_subdev_init_finalize(sd); + if (ret) { + dev_err(v4l2_dev->dev, + "%s: subdev initialization failed (err=%d)\n", + name, ret); + goto err_clean_m_ent; + } + } + /* Register the subdev with the v4l2 and the media framework */ ret = v4l2_device_register_subdev(v4l2_dev, sd); if (ret) { dev_err(v4l2_dev->dev, "%s: subdev register failed (err=%d)\n", name, ret); - goto err_clean_m_ent; + goto err_clean_sd; } return 0; +err_clean_sd: + v4l2_subdev_cleanup(sd); err_clean_m_ent: media_entity_cleanup(&sd->entity); return ret; diff --git a/drivers/media/test-drivers/vimc/vimc-common.h b/drivers/media/test-drivers/vimc/vimc-common.h index 7641a101a728..7a45a2117748 100644 --- a/drivers/media/test-drivers/vimc/vimc-common.h +++ b/drivers/media/test-drivers/vimc/vimc-common.h @@ -156,7 +156,7 @@ struct vimc_ent_type { */ struct vimc_ent_config { const char *name; - struct vimc_ent_type *type; + const struct vimc_ent_type *type; }; /** @@ -167,11 +167,11 @@ struct vimc_ent_config { */ bool vimc_is_source(struct media_entity *ent); -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; +extern const struct vimc_ent_type vimc_sensor_type; +extern const struct vimc_ent_type vimc_debayer_type; +extern const struct vimc_ent_type vimc_scaler_type; +extern const struct vimc_ent_type vimc_capture_type; +extern const struct vimc_ent_type vimc_lens_type; /** * vimc_pix_map_by_index - get vimc_pix_map struct by its index @@ -215,6 +215,7 @@ const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat); * @num_pads: number of pads to initialize * @pads: the array of pads of the entity, the caller should set the * flags of the pads + * @int_ops: pointer to &struct v4l2_subdev_internal_ops. * @sd_ops: pointer to &struct v4l2_subdev_ops. * * Helper function initialize and register the struct vimc_ent_device and struct @@ -227,6 +228,7 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved, u32 function, u16 num_pads, struct media_pad *pads, + const struct v4l2_subdev_internal_ops *int_ops, const struct v4l2_subdev_ops *sd_ops); /** diff --git a/drivers/media/test-drivers/vimc/vimc-core.c b/drivers/media/test-drivers/vimc/vimc-core.c index af127476e920..2083c60e34d6 100644 --- a/drivers/media/test-drivers/vimc/vimc-core.c +++ b/drivers/media/test-drivers/vimc/vimc-core.c @@ -81,7 +81,7 @@ struct vimc_pipeline_config { * Topology Configuration */ -static struct vimc_ent_config ent_config[] = { +static const struct vimc_ent_config ent_config[] = { [SENSOR_A] = { .name = "Sensor A", .type = &vimc_sensor_type diff --git a/drivers/media/test-drivers/vimc/vimc-debayer.c b/drivers/media/test-drivers/vimc/vimc-debayer.c index d72ed086e00b..bbb7c7a86df0 100644 --- a/drivers/media/test-drivers/vimc/vimc-debayer.c +++ b/drivers/media/test-drivers/vimc/vimc-debayer.c @@ -15,6 +15,9 @@ #include "vimc-common.h" +/* TODO: Add support for more output formats, we only support RGB888 for now. */ +#define VIMC_DEBAYER_SOURCE_MBUS_FMT MEDIA_BUS_FMT_RGB888_1X24 + enum vimc_debayer_rgb_colors { VIMC_DEBAYER_RED = 0, VIMC_DEBAYER_GREEN = 1, @@ -29,19 +32,26 @@ struct vimc_debayer_pix_map { 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; + struct v4l2_ctrl_handler hdl; + struct media_pad pads[2]; + + u8 *src_frame; + 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_debayer_pix_map *sink_pix_map; - unsigned int sink_bpp; - unsigned int mean_win_size; - struct v4l2_ctrl_handler hdl; - struct media_pad pads[2]; + + /* + * Virtual "hardware" configuration, filled when the stream starts or + * when controls are set. + */ + struct { + const struct vimc_debayer_pix_map *sink_pix_map; + unsigned int sink_bpp; + struct v4l2_area size; + unsigned int mean_win_size; + u32 src_code; + } hw; }; static const struct v4l2_mbus_framefmt sink_fmt_default = { @@ -153,18 +163,14 @@ static bool vimc_debayer_src_code_is_valid(u32 code) static int vimc_debayer_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state) { - struct vimc_debayer_device *vdebayer = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *mf; - unsigned int i; mf = v4l2_subdev_state_get_format(sd_state, 0); *mf = sink_fmt_default; - for (i = 1; i < sd->entity.num_pads; i++) { - mf = v4l2_subdev_state_get_format(sd_state, i); - *mf = sink_fmt_default; - mf->code = vdebayer->src_code; - } + mf = v4l2_subdev_state_get_format(sd_state, 1); + *mf = sink_fmt_default; + mf->code = VIMC_DEBAYER_SOURCE_MBUS_FMT; return 0; } @@ -213,24 +219,6 @@ static int vimc_debayer_enum_frame_size(struct v4l2_subdev *sd, return 0; } -static int vimc_debayer_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct vimc_debayer_device *vdebayer = v4l2_get_subdevdata(sd); - - /* Get the current sink format */ - fmt->format = fmt->which == V4L2_SUBDEV_FORMAT_TRY ? - *v4l2_subdev_state_get_format(sd_state, 0) : - vdebayer->sink_fmt; - - /* Set the right code for the source pad */ - if (VIMC_IS_SRC(fmt->pad)) - fmt->format.code = vdebayer->src_code; - - return 0; -} - static void vimc_debayer_adjust_sink_fmt(struct v4l2_mbus_framefmt *fmt) { const struct vimc_debayer_pix_map *vpix; @@ -256,52 +244,42 @@ static int vimc_debayer_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_format *fmt) { struct vimc_debayer_device *vdebayer = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *sink_fmt; - u32 *src_code; + struct v4l2_mbus_framefmt *format; - if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - /* Do not change the format while stream is on */ - if (vdebayer->src_frame) - return -EBUSY; - - sink_fmt = &vdebayer->sink_fmt; - src_code = &vdebayer->src_code; - } else { - sink_fmt = v4l2_subdev_state_get_format(sd_state, 0); - src_code = &v4l2_subdev_state_get_format(sd_state, 1)->code; - } + /* Do not change the format while stream is on. */ + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE && vdebayer->src_frame) + return -EBUSY; /* - * Do not change the format of the source pad, - * it is propagated from the sink + * Do not change the format of the source pad, it is propagated from + * the sink. */ - if (VIMC_IS_SRC(fmt->pad)) { - u32 code = fmt->format.code; + if (VIMC_IS_SRC(fmt->pad)) + return v4l2_subdev_get_fmt(sd, sd_state, fmt); - fmt->format = *sink_fmt; + /* Set the new format in the sink pad. */ + vimc_debayer_adjust_sink_fmt(&fmt->format); - if (vimc_debayer_src_code_is_valid(code)) - *src_code = code; + format = v4l2_subdev_state_get_format(sd_state, 0); - fmt->format.code = *src_code; - } else { - /* Set the new format in the sink pad */ - vimc_debayer_adjust_sink_fmt(&fmt->format); - - 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", vdebayer->sd.name, - /* old */ - sink_fmt->width, sink_fmt->height, sink_fmt->code, - sink_fmt->colorspace, sink_fmt->quantization, - sink_fmt->xfer_func, sink_fmt->ycbcr_enc, - /* new */ - fmt->format.width, fmt->format.height, fmt->format.code, - fmt->format.colorspace, fmt->format.quantization, - fmt->format.xfer_func, fmt->format.ycbcr_enc); - - *sink_fmt = fmt->format; - } + 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", vdebayer->sd.name, + /* old */ + format->width, format->height, format->code, + format->colorspace, format->quantization, + format->xfer_func, format->ycbcr_enc, + /* new */ + fmt->format.width, fmt->format.height, fmt->format.code, + fmt->format.colorspace, fmt->format.quantization, + fmt->format.xfer_func, fmt->format.ycbcr_enc); + + *format = fmt->format; + + /* Propagate the format to the source pad. */ + format = v4l2_subdev_state_get_format(sd_state, 1); + *format = fmt->format; + format->code = VIMC_DEBAYER_SOURCE_MBUS_FMT; return 0; } @@ -309,7 +287,7 @@ static int vimc_debayer_set_fmt(struct v4l2_subdev *sd, static const struct v4l2_subdev_pad_ops vimc_debayer_pad_ops = { .enum_mbus_code = vimc_debayer_enum_mbus_code, .enum_frame_size = vimc_debayer_enum_frame_size, - .get_fmt = vimc_debayer_get_fmt, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = vimc_debayer_set_fmt, }; @@ -321,8 +299,8 @@ static void vimc_debayer_process_rgb_frame(struct vimc_debayer_device *vdebayer, const struct vimc_pix_map *vpix; unsigned int i, index; - vpix = vimc_pix_map_by_code(vdebayer->src_code); - index = VIMC_FRAME_INDEX(lin, col, vdebayer->sink_fmt.width, 3); + vpix = vimc_pix_map_by_code(vdebayer->hw.src_code); + index = VIMC_FRAME_INDEX(lin, col, vdebayer->hw.size.width, 3); for (i = 0; i < 3; i++) { switch (vpix->pixelformat) { case V4L2_PIX_FMT_RGB24: @@ -340,24 +318,37 @@ static int vimc_debayer_s_stream(struct v4l2_subdev *sd, int enable) struct vimc_debayer_device *vdebayer = v4l2_get_subdevdata(sd); if (enable) { + const struct v4l2_mbus_framefmt *sink_fmt; + const struct v4l2_mbus_framefmt *src_fmt; + struct v4l2_subdev_state *state; const struct vimc_pix_map *vpix; unsigned int frame_size; if (vdebayer->src_frame) return 0; + state = v4l2_subdev_lock_and_get_active_state(sd); + sink_fmt = v4l2_subdev_state_get_format(state, 0); + src_fmt = v4l2_subdev_state_get_format(state, 1); + /* Calculate the frame size of the source pad */ - vpix = vimc_pix_map_by_code(vdebayer->src_code); - frame_size = vdebayer->sink_fmt.width * vdebayer->sink_fmt.height * - vpix->bpp; + vpix = vimc_pix_map_by_code(src_fmt->code); + frame_size = src_fmt->width * src_fmt->height * vpix->bpp; /* Save the bytes per pixel of the sink */ - vpix = vimc_pix_map_by_code(vdebayer->sink_fmt.code); - vdebayer->sink_bpp = vpix->bpp; + vpix = vimc_pix_map_by_code(sink_fmt->code); + vdebayer->hw.sink_bpp = vpix->bpp; /* Get the corresponding pixel map from the table */ - vdebayer->sink_pix_map = - vimc_debayer_pix_map_by_code(vdebayer->sink_fmt.code); + vdebayer->hw.sink_pix_map = + vimc_debayer_pix_map_by_code(sink_fmt->code); + + vdebayer->hw.size.width = sink_fmt->width; + vdebayer->hw.size.height = sink_fmt->height; + + vdebayer->hw.src_code = src_fmt->code; + + v4l2_subdev_unlock_state(state); /* * Allocate the frame buffer. Use vmalloc to be able to @@ -366,7 +357,6 @@ static int vimc_debayer_s_stream(struct v4l2_subdev *sd, int enable) vdebayer->src_frame = vmalloc(frame_size); if (!vdebayer->src_frame) return -ENOMEM; - } else { if (!vdebayer->src_frame) return 0; @@ -427,13 +417,13 @@ static void vimc_debayer_calc_rgb_sink(struct vimc_debayer_device *vdebayer, * the top left corner of the mean window (considering the current * pixel as the center) */ - seek = vdebayer->mean_win_size / 2; + seek = vdebayer->hw.mean_win_size / 2; /* Sum the values of the colors in the mean window */ dev_dbg(vdebayer->ved.dev, "deb: %s: --- Calc pixel %dx%d, window mean %d, seek %d ---\n", - vdebayer->sd.name, lin, col, vdebayer->sink_fmt.height, seek); + vdebayer->sd.name, lin, col, vdebayer->hw.size.height, seek); /* * Iterate through all the lines in the mean window, start @@ -442,7 +432,7 @@ static void vimc_debayer_calc_rgb_sink(struct vimc_debayer_device *vdebayer, * frame */ for (wlin = seek > lin ? 0 : lin - seek; - wlin < lin + seek + 1 && wlin < vdebayer->sink_fmt.height; + wlin < lin + seek + 1 && wlin < vdebayer->hw.size.height; wlin++) { /* @@ -452,17 +442,17 @@ static void vimc_debayer_calc_rgb_sink(struct vimc_debayer_device *vdebayer, * frame */ for (wcol = seek > col ? 0 : col - seek; - wcol < col + seek + 1 && wcol < vdebayer->sink_fmt.width; + wcol < col + seek + 1 && wcol < vdebayer->hw.size.width; wcol++) { enum vimc_debayer_rgb_colors color; unsigned int index; /* Check which color this pixel is */ - color = vdebayer->sink_pix_map->order[wlin % 2][wcol % 2]; + color = vdebayer->hw.sink_pix_map->order[wlin % 2][wcol % 2]; index = VIMC_FRAME_INDEX(wlin, wcol, - vdebayer->sink_fmt.width, - vdebayer->sink_bpp); + vdebayer->hw.size.width, + vdebayer->hw.sink_bpp); dev_dbg(vdebayer->ved.dev, "deb: %s: RGB CALC: frame index %d, win pos %dx%d, color %d\n", @@ -471,7 +461,7 @@ static void vimc_debayer_calc_rgb_sink(struct vimc_debayer_device *vdebayer, /* Get its value */ rgb[color] = rgb[color] + vimc_debayer_get_val(&frame[index], - vdebayer->sink_bpp); + vdebayer->hw.sink_bpp); /* Save how many values we already added */ n_rgb[color]++; @@ -509,8 +499,8 @@ static void *vimc_debayer_process_frame(struct vimc_ent_device *ved, if (!vdebayer->src_frame) return ERR_PTR(-EINVAL); - for (i = 0; i < vdebayer->sink_fmt.height; i++) - for (j = 0; j < vdebayer->sink_fmt.width; j++) { + for (i = 0; i < vdebayer->hw.size.height; i++) + for (j = 0; j < vdebayer->hw.size.width; j++) { vimc_debayer_calc_rgb_sink(vdebayer, sink_frame, i, j, rgb); vdebayer->set_rgb_src(vdebayer, i, j, rgb); } @@ -525,7 +515,7 @@ static int vimc_debayer_s_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case VIMC_CID_MEAN_WIN_SIZE: - vdebayer->mean_win_size = ctrl->val; + vdebayer->hw.mean_win_size = ctrl->val; break; default: return -EINVAL; @@ -543,6 +533,7 @@ static void vimc_debayer_release(struct vimc_ent_device *ved) container_of(ved, struct vimc_debayer_device, ved); v4l2_ctrl_handler_free(&vdebayer->hdl); + v4l2_subdev_cleanup(&vdebayer->sd); media_entity_cleanup(vdebayer->ved.ent); kfree(vdebayer); } @@ -594,25 +585,15 @@ static struct vimc_ent_device *vimc_debayer_add(struct vimc_device *vimc, ret = vimc_ent_sd_register(&vdebayer->ved, &vdebayer->sd, v4l2_dev, vcfg_name, MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV, 2, - vdebayer->pads, &vimc_debayer_ops); + vdebayer->pads, &vimc_debayer_internal_ops, + &vimc_debayer_ops); if (ret) goto err_free_hdl; - vdebayer->sd.internal_ops = &vimc_debayer_internal_ops; - 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; + vdebayer->hw.mean_win_size = vimc_debayer_ctrl_mean_win_size.def; - /* Initialize the frame format */ - 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 - */ - vdebayer->src_code = MEDIA_BUS_FMT_RGB888_1X24; vdebayer->set_rgb_src = vimc_debayer_process_rgb_frame; return &vdebayer->ved; @@ -625,7 +606,7 @@ err_free_vdebayer: return ERR_PTR(ret); } -struct vimc_ent_type vimc_debayer_type = { +const 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 index 3ce7f4b4d2cc..96399057a2b5 100644 --- a/drivers/media/test-drivers/vimc/vimc-lens.c +++ b/drivers/media/test-drivers/vimc/vimc-lens.c @@ -72,7 +72,7 @@ static struct vimc_ent_device *vimc_lens_add(struct vimc_device *vimc, ret = vimc_ent_sd_register(&vlens->ved, &vlens->sd, v4l2_dev, vcfg_name, MEDIA_ENT_F_LENS, 0, - NULL, &vimc_lens_ops); + NULL, NULL, &vimc_lens_ops); if (ret) goto err_free_hdl; @@ -92,11 +92,12 @@ static void vimc_lens_release(struct vimc_ent_device *ved) container_of(ved, struct vimc_lens_device, ved); v4l2_ctrl_handler_free(&vlens->hdl); + v4l2_subdev_cleanup(&vlens->sd); media_entity_cleanup(vlens->ved.ent); kfree(vlens); } -struct vimc_ent_type vimc_lens_type = { +const 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 afe13d6af321..47d0d63865a0 100644 --- a/drivers/media/test-drivers/vimc/vimc-scaler.c +++ b/drivers/media/test-drivers/vimc/vimc-scaler.c @@ -15,7 +15,7 @@ #include "vimc-common.h" /* Pad identifier */ -enum vic_sca_pad { +enum vimc_scaler_pad { VIMC_SCALER_SINK = 0, VIMC_SCALER_SRC = 1, }; @@ -26,13 +26,20 @@ enum vic_sca_pad { struct vimc_scaler_device { struct vimc_ent_device ved; struct v4l2_subdev sd; - struct v4l2_rect crop_rect; - /* Frame format for both sink and src pad */ - struct v4l2_mbus_framefmt fmt[2]; - /* Values calculated when the stream starts */ - u8 *src_frame; - unsigned int bpp; struct media_pad pads[2]; + + u8 *src_frame; + + /* + * Virtual "hardware" configuration, filled when the stream starts or + * when controls are set. + */ + struct { + struct v4l2_mbus_framefmt sink_fmt; + struct v4l2_mbus_framefmt src_fmt; + struct v4l2_rect sink_crop; + unsigned int bpp; + } hw; }; static const struct v4l2_mbus_framefmt fmt_default = { @@ -132,39 +139,6 @@ static int vimc_scaler_enum_frame_size(struct v4l2_subdev *sd, return 0; } -static struct v4l2_mbus_framefmt * -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_state_get_format(sd_state, pad); - else - return &vscaler->fmt[pad]; -} - -static struct v4l2_rect * -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_state_get_crop(sd_state, VIMC_SCALER_SINK); - else - return &vscaler->crop_rect; -} - -static int vimc_scaler_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) -{ - struct vimc_scaler_device *vscaler = v4l2_get_subdevdata(sd); - - format->format = *vimc_scaler_pad_format(vscaler, sd_state, format->pad, - format->which); - return 0; -} - static int vimc_scaler_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) @@ -176,7 +150,7 @@ static int vimc_scaler_set_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && vscaler->src_frame) return -EBUSY; - fmt = vimc_scaler_pad_format(vscaler, sd_state, format->pad, format->which); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); /* * The media bus code and colorspace can only be changed on the sink @@ -214,14 +188,13 @@ static int vimc_scaler_set_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *src_fmt; struct v4l2_rect *crop; - crop = vimc_scaler_pad_crop(vscaler, sd_state, format->which); + crop = v4l2_subdev_state_get_crop(sd_state, VIMC_SCALER_SINK); crop->width = fmt->width; crop->height = fmt->height; crop->top = 0; crop->left = 0; - src_fmt = vimc_scaler_pad_format(vscaler, sd_state, VIMC_SCALER_SRC, - format->which); + src_fmt = v4l2_subdev_state_get_format(sd_state, VIMC_SCALER_SRC); *src_fmt = *fmt; } @@ -234,7 +207,6 @@ static int vimc_scaler_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { - struct vimc_scaler_device *vscaler = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *sink_fmt; if (VIMC_IS_SRC(sel->pad)) @@ -242,11 +214,10 @@ static int vimc_scaler_get_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_CROP: - sel->r = *vimc_scaler_pad_crop(vscaler, sd_state, sel->which); + sel->r = *v4l2_subdev_state_get_crop(sd_state, VIMC_SCALER_SINK); break; case V4L2_SEL_TGT_CROP_BOUNDS: - sink_fmt = vimc_scaler_pad_format(vscaler, sd_state, VIMC_SCALER_SINK, - sel->which); + sink_fmt = v4l2_subdev_state_get_format(sd_state, VIMC_SCALER_SINK); sel->r = vimc_scaler_get_crop_bound_sink(sink_fmt); break; default: @@ -282,9 +253,8 @@ static int vimc_scaler_set_selection(struct v4l2_subdev *sd, if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE && vscaler->src_frame) return -EBUSY; - 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); + crop_rect = v4l2_subdev_state_get_crop(sd_state, VIMC_SCALER_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, VIMC_SCALER_SINK); vimc_scaler_adjust_sink_crop(&sel->r, sink_fmt); *crop_rect = sel->r; @@ -294,7 +264,7 @@ static int vimc_scaler_set_selection(struct v4l2_subdev *sd, static const struct v4l2_subdev_pad_ops vimc_scaler_pad_ops = { .enum_mbus_code = vimc_scaler_enum_mbus_code, .enum_frame_size = vimc_scaler_enum_frame_size, - .get_fmt = vimc_scaler_get_fmt, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = vimc_scaler_set_fmt, .get_selection = vimc_scaler_get_selection, .set_selection = vimc_scaler_set_selection, @@ -305,27 +275,38 @@ static int vimc_scaler_s_stream(struct v4l2_subdev *sd, int enable) struct vimc_scaler_device *vscaler = v4l2_get_subdevdata(sd); if (enable) { - const struct vimc_pix_map *vpix; + struct v4l2_subdev_state *state; + const struct v4l2_mbus_framefmt *format; + const struct v4l2_rect *rect; unsigned int frame_size; if (vscaler->src_frame) return 0; - /* Save the bytes per pixel of the sink */ - vpix = vimc_pix_map_by_code(vscaler->fmt[VIMC_SCALER_SINK].code); - vscaler->bpp = vpix->bpp; + state = v4l2_subdev_lock_and_get_active_state(sd); - /* Calculate the frame size of the source pad */ - frame_size = vscaler->fmt[VIMC_SCALER_SRC].width - * vscaler->fmt[VIMC_SCALER_SRC].height * vscaler->bpp; + /* Save the bytes per pixel of the sink. */ + format = v4l2_subdev_state_get_format(state, VIMC_SCALER_SINK); + vscaler->hw.sink_fmt = *format; + vscaler->hw.bpp = vimc_pix_map_by_code(format->code)->bpp; - /* Allocate the frame buffer. Use vmalloc to be able to - * allocate a large amount of memory + /* Calculate the frame size of the source pad. */ + format = v4l2_subdev_state_get_format(state, VIMC_SCALER_SRC); + vscaler->hw.src_fmt = *format; + frame_size = format->width * format->height * vscaler->hw.bpp; + + rect = v4l2_subdev_state_get_crop(state, VIMC_SCALER_SINK); + vscaler->hw.sink_crop = *rect; + + v4l2_subdev_unlock_state(state); + + /* + * Allocate the frame buffer. Use vmalloc to be able to allocate + * a large amount of memory. */ vscaler->src_frame = vmalloc(frame_size); if (!vscaler->src_frame) return -ENOMEM; - } else { if (!vscaler->src_frame) return 0; @@ -353,9 +334,9 @@ static const struct v4l2_subdev_internal_ops vimc_scaler_internal_ops = { 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 = &vscaler->fmt[VIMC_SCALER_SRC]; - const struct v4l2_rect *r = &vscaler->crop_rect; - unsigned int snk_width = vscaler->fmt[VIMC_SCALER_SINK].width; + const struct v4l2_mbus_framefmt *sink_fmt = &vscaler->hw.sink_fmt; + const struct v4l2_mbus_framefmt *src_fmt = &vscaler->hw.src_fmt; + const struct v4l2_rect *r = &vscaler->hw.sink_crop; unsigned int src_x, src_y; u8 *walker = vscaler->src_frame; @@ -364,16 +345,16 @@ static void vimc_scaler_fill_src_frame(const struct vimc_scaler_device *const vs unsigned int snk_y, y_offset; snk_y = (src_y * r->height) / src_fmt->height + r->top; - y_offset = snk_y * snk_width * vscaler->bpp; + y_offset = snk_y * sink_fmt->width * vscaler->hw.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 * vscaler->bpp; + x_offset = snk_x * vscaler->hw.bpp; index = y_offset + x_offset; - memcpy(walker, &sink_frame[index], vscaler->bpp); - walker += vscaler->bpp; + memcpy(walker, &sink_frame[index], vscaler->hw.bpp); + walker += vscaler->hw.bpp; } } } @@ -398,6 +379,7 @@ static void vimc_scaler_release(struct vimc_ent_device *ved) struct vimc_scaler_device *vscaler = container_of(ved, struct vimc_scaler_device, ved); + v4l2_subdev_cleanup(&vscaler->sd); media_entity_cleanup(vscaler->ved.ent); kfree(vscaler); } @@ -421,28 +403,20 @@ static struct vimc_ent_device *vimc_scaler_add(struct vimc_device *vimc, ret = vimc_ent_sd_register(&vscaler->ved, &vscaler->sd, v4l2_dev, vcfg_name, MEDIA_ENT_F_PROC_VIDEO_SCALER, 2, - vscaler->pads, &vimc_scaler_ops); + vscaler->pads, &vimc_scaler_internal_ops, + &vimc_scaler_ops); if (ret) { kfree(vscaler); return ERR_PTR(ret); } - vscaler->sd.internal_ops = &vimc_scaler_internal_ops; - vscaler->ved.process_frame = vimc_scaler_process_frame; vscaler->ved.dev = vimc->mdev.dev; - /* Initialize the frame format */ - vscaler->fmt[VIMC_SCALER_SINK] = fmt_default; - vscaler->fmt[VIMC_SCALER_SRC] = fmt_default; - - /* Initialize the crop selection */ - vscaler->crop_rect = crop_rect_default; - return &vscaler->ved; } -struct vimc_ent_type vimc_scaler_type = { +const 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 5e34b1aed95e..027767777763 100644 --- a/drivers/media/test-drivers/vimc/vimc-sensor.c +++ b/drivers/media/test-drivers/vimc/vimc-sensor.c @@ -24,13 +24,20 @@ struct vimc_sensor_device { struct vimc_ent_device ved; struct v4l2_subdev sd; struct tpg_data tpg; - u8 *frame; - enum vimc_sensor_osd_mode osd_value; - u64 start_stream_ts; - /* The active format */ - struct v4l2_mbus_framefmt mbus_format; struct v4l2_ctrl_handler hdl; struct media_pad pad; + + u8 *frame; + + /* + * Virtual "hardware" configuration, filled when the stream starts or + * when controls are set. + */ + struct { + struct v4l2_area size; + enum vimc_sensor_osd_mode osd_value; + u64 start_stream_ts; + } hw; }; static const struct v4l2_mbus_framefmt fmt_default = { @@ -44,14 +51,10 @@ static const struct v4l2_mbus_framefmt fmt_default = { static int vimc_sensor_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state) { - unsigned int i; - - for (i = 0; i < sd->entity.num_pads; i++) { - struct v4l2_mbus_framefmt *mf; + struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_state_get_format(sd_state, i); - *mf = fmt_default; - } + mf = v4l2_subdev_state_get_format(sd_state, 0); + *mf = fmt_default; return 0; } @@ -92,36 +95,22 @@ static int vimc_sensor_enum_frame_size(struct v4l2_subdev *sd, return 0; } -static int vimc_sensor_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) +static void vimc_sensor_tpg_s_format(struct vimc_sensor_device *vsensor, + const struct v4l2_mbus_framefmt *format) { - struct vimc_sensor_device *vsensor = - container_of(sd, struct vimc_sensor_device, sd); - - fmt->format = fmt->which == V4L2_SUBDEV_FORMAT_TRY ? - *v4l2_subdev_state_get_format(sd_state, fmt->pad) : - vsensor->mbus_format; + const struct vimc_pix_map *vpix = vimc_pix_map_by_code(format->code); - return 0; -} - -static void vimc_sensor_tpg_s_format(struct vimc_sensor_device *vsensor) -{ - const struct vimc_pix_map *vpix = - vimc_pix_map_by_code(vsensor->mbus_format.code); - - 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_reset_source(&vsensor->tpg, format->width, format->height, + format->field); + tpg_s_bytesperline(&vsensor->tpg, 0, format->width * vpix->bpp); + tpg_s_buf_height(&vsensor->tpg, format->height); tpg_s_fourcc(&vsensor->tpg, vpix->pixelformat); /* TODO: add support for V4L2_FIELD_ALTERNATE */ - 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); + tpg_s_field(&vsensor->tpg, format->field, false); + tpg_s_colorspace(&vsensor->tpg, format->colorspace); + tpg_s_ycbcr_enc(&vsensor->tpg, format->ycbcr_enc); + tpg_s_quantization(&vsensor->tpg, format->quantization); + tpg_s_xfer_func(&vsensor->tpg, format->xfer_func); } static void vimc_sensor_adjust_fmt(struct v4l2_mbus_framefmt *fmt) @@ -152,15 +141,11 @@ static int vimc_sensor_set_fmt(struct v4l2_subdev *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 (vsensor->frame) - return -EBUSY; + /* Do not change the format while stream is on */ + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE && vsensor->frame) + return -EBUSY; - mf = &vsensor->mbus_format; - } else { - mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); - } + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); /* Set the new format */ vimc_sensor_adjust_fmt(&fmt->format); @@ -185,7 +170,7 @@ static int vimc_sensor_set_fmt(struct v4l2_subdev *sd, static const struct v4l2_subdev_pad_ops vimc_sensor_pad_ops = { .enum_mbus_code = vimc_sensor_enum_mbus_code, .enum_frame_size = vimc_sensor_enum_frame_size, - .get_fmt = vimc_sensor_get_fmt, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = vimc_sensor_set_fmt, }; @@ -202,7 +187,7 @@ static void *vimc_sensor_process_frame(struct vimc_ent_device *ved, tpg_fill_plane_buffer(&vsensor->tpg, 0, 0, vsensor->frame); tpg_calc_text_basep(&vsensor->tpg, basep, 0, vsensor->frame); - switch (vsensor->osd_value) { + switch (vsensor->hw.osd_value) { case VIMC_SENSOR_OSD_SHOW_ALL: { const char *order = tpg_g_color_order(&vsensor->tpg); @@ -216,15 +201,14 @@ static void *vimc_sensor_process_frame(struct vimc_ent_device *ved, vsensor->tpg.hue); tpg_gen_text(&vsensor->tpg, basep, line++ * line_height, 16, str); snprintf(str, sizeof(str), "sensor size: %dx%d", - vsensor->mbus_format.width, - vsensor->mbus_format.height); + vsensor->hw.size.width, vsensor->hw.size.height); tpg_gen_text(&vsensor->tpg, basep, line++ * line_height, 16, str); fallthrough; } case VIMC_SENSOR_OSD_SHOW_COUNTERS: { unsigned int ms; - ms = div_u64(ktime_get_ns() - vsensor->start_stream_ts, 1000000); + ms = div_u64(ktime_get_ns() - vsensor->hw.start_stream_ts, 1000000); snprintf(str, sizeof(str), "%02d:%02d:%02d:%03d", (ms / (60 * 60 * 1000)) % 24, (ms / (60 * 1000)) % 60, @@ -247,15 +231,25 @@ static int vimc_sensor_s_stream(struct v4l2_subdev *sd, int enable) container_of(sd, struct vimc_sensor_device, sd); if (enable) { + const struct v4l2_mbus_framefmt *format; + struct v4l2_subdev_state *state; const struct vimc_pix_map *vpix; unsigned int frame_size; - vsensor->start_stream_ts = ktime_get_ns(); + state = v4l2_subdev_lock_and_get_active_state(sd); + format = v4l2_subdev_state_get_format(state, 0); - /* Calculate the frame size */ - vpix = vimc_pix_map_by_code(vsensor->mbus_format.code); - frame_size = vsensor->mbus_format.width * vpix->bpp * - vsensor->mbus_format.height; + /* Configure the test pattern generator. */ + vimc_sensor_tpg_s_format(vsensor, format); + + /* Calculate the frame size. */ + vpix = vimc_pix_map_by_code(format->code); + frame_size = format->width * vpix->bpp * format->height; + + vsensor->hw.size.width = format->width; + vsensor->hw.size.height = format->height; + + v4l2_subdev_unlock_state(state); /* * Allocate the frame buffer. Use vmalloc to be able to @@ -265,9 +259,7 @@ static int vimc_sensor_s_stream(struct v4l2_subdev *sd, int enable) if (!vsensor->frame) return -ENOMEM; - /* configure the test pattern generator */ - vimc_sensor_tpg_s_format(vsensor); - + vsensor->hw.start_stream_ts = ktime_get_ns(); } else { vfree(vsensor->frame); @@ -325,7 +317,7 @@ static int vimc_sensor_s_ctrl(struct v4l2_ctrl *ctrl) tpg_s_saturation(&vsensor->tpg, ctrl->val); break; case VIMC_CID_OSD_TEXT_MODE: - vsensor->osd_value = ctrl->val; + vsensor->hw.osd_value = ctrl->val; break; default: return -EINVAL; @@ -344,6 +336,7 @@ static void vimc_sensor_release(struct vimc_ent_device *ved) v4l2_ctrl_handler_free(&vsensor->hdl); tpg_free(&vsensor->tpg); + v4l2_subdev_cleanup(&vsensor->sd); media_entity_cleanup(vsensor->ved.ent); kfree(vsensor); } @@ -417,8 +410,7 @@ static struct vimc_ent_device *vimc_sensor_add(struct vimc_device *vimc, } /* Initialize the test pattern generator */ - tpg_init(&vsensor->tpg, vsensor->mbus_format.width, - vsensor->mbus_format.height); + tpg_init(&vsensor->tpg, fmt_default.width, fmt_default.height); ret = tpg_alloc(&vsensor->tpg, VIMC_FRAME_MAX_WIDTH); if (ret) goto err_free_hdl; @@ -428,18 +420,13 @@ static struct vimc_ent_device *vimc_sensor_add(struct vimc_device *vimc, ret = vimc_ent_sd_register(&vsensor->ved, &vsensor->sd, v4l2_dev, vcfg_name, MEDIA_ENT_F_CAM_SENSOR, 1, &vsensor->pad, - &vimc_sensor_ops); + &vimc_sensor_internal_ops, &vimc_sensor_ops); if (ret) goto err_free_tpg; - vsensor->sd.internal_ops = &vimc_sensor_internal_ops; - vsensor->ved.process_frame = vimc_sensor_process_frame; vsensor->ved.dev = vimc->mdev.dev; - /* Initialize the frame format */ - vsensor->mbus_format = fmt_default; - return &vsensor->ved; err_free_tpg: @@ -452,7 +439,7 @@ err_free_vsensor: return ERR_PTR(ret); } -struct vimc_ent_type vimc_sensor_type = { +const struct vimc_ent_type vimc_sensor_type = { .add = vimc_sensor_add, .release = vimc_sensor_release }; diff --git a/drivers/media/test-drivers/vivid/vivid-cec.c b/drivers/media/test-drivers/vivid/vivid-cec.c index 1f7469ff04d5..941ef4263214 100644 --- a/drivers/media/test-drivers/vivid/vivid-cec.c +++ b/drivers/media/test-drivers/vivid/vivid-cec.c @@ -23,7 +23,7 @@ struct xfer_on_bus { static bool find_dest_adap(struct vivid_dev *dev, struct cec_adapter *adap, u8 dest) { - unsigned int i; + unsigned int i, j; if (dest >= 0xf) return false; @@ -33,12 +33,29 @@ static bool find_dest_adap(struct vivid_dev *dev, cec_has_log_addr(dev->cec_rx_adap, dest)) return true; - for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++) { - if (adap == dev->cec_tx_adap[i]) + for (i = 0, j = 0; i < dev->num_inputs; i++) { + unsigned int menu_idx = + dev->input_is_connected_to_output[i]; + + if (dev->input_type[i] != HDMI) + continue; + j++; + if (menu_idx < FIXED_MENU_ITEMS) + continue; + + struct vivid_dev *dev_tx = vivid_ctrl_hdmi_to_output_instance[menu_idx]; + unsigned int output = vivid_ctrl_hdmi_to_output_index[menu_idx]; + + if (!dev_tx) continue; - if (!dev->cec_tx_adap[i]->is_configured) + + unsigned int hdmi_output = dev_tx->output_to_iface_index[output]; + + if (adap == dev_tx->cec_tx_adap[hdmi_output]) + continue; + if (!dev_tx->cec_tx_adap[hdmi_output]->is_configured) continue; - if (cec_has_log_addr(dev->cec_tx_adap[i], dest)) + if (cec_has_log_addr(dev_tx->cec_tx_adap[hdmi_output], dest)) return true; } return false; @@ -96,7 +113,7 @@ static void adjust_sfts(struct vivid_dev *dev) int vivid_cec_bus_thread(void *_dev) { u32 last_sft; - unsigned int i; + unsigned int i, j; unsigned int dest; ktime_t start, end; s64 delta_us, retry_us; @@ -193,9 +210,27 @@ int vivid_cec_bus_thread(void *_dev) if (first_status == CEC_TX_STATUS_OK) { if (xfers_on_bus[first_idx].adap != dev->cec_rx_adap) cec_received_msg(dev->cec_rx_adap, &first_msg); - for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++) - if (xfers_on_bus[first_idx].adap != dev->cec_tx_adap[i]) - cec_received_msg(dev->cec_tx_adap[i], &first_msg); + for (i = 0, j = 0; i < dev->num_inputs; i++) { + unsigned int menu_idx = + dev->input_is_connected_to_output[i]; + + if (dev->input_type[i] != HDMI) + continue; + j++; + if (menu_idx < FIXED_MENU_ITEMS) + continue; + + struct vivid_dev *dev_tx = vivid_ctrl_hdmi_to_output_instance[menu_idx]; + unsigned int output = vivid_ctrl_hdmi_to_output_index[menu_idx]; + + if (!dev_tx) + continue; + + unsigned int hdmi_output = dev_tx->output_to_iface_index[output]; + + if (xfers_on_bus[first_idx].adap != dev_tx->cec_tx_adap[hdmi_output]) + cec_received_msg(dev_tx->cec_tx_adap[hdmi_output], &first_msg); + } } end = ktime_get(); /* @@ -242,21 +277,36 @@ static int vivid_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, u32 signal_free_time, struct cec_msg *msg) { struct vivid_dev *dev = cec_get_drvdata(adap); + struct vivid_dev *dev_rx = dev; u8 idx = cec_msg_initiator(msg); + u8 output = 0; - spin_lock(&dev->cec_xfers_slock); - dev->xfers[idx].adap = adap; - memcpy(dev->xfers[idx].msg, msg->msg, CEC_MAX_MSG_SIZE); - dev->xfers[idx].len = msg->len; - dev->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_RETRY; + if (dev->cec_rx_adap != adap) { + int i; + + for (i = 0; i < dev->num_hdmi_outputs; i++) + if (dev->cec_tx_adap[i] == adap) + break; + if (i == dev->num_hdmi_outputs) + return -ENONET; + output = dev->hdmi_index_to_output_index[i]; + dev_rx = dev->output_to_input_instance[output]; + if (!dev_rx) + return -ENONET; + } + spin_lock(&dev_rx->cec_xfers_slock); + dev_rx->xfers[idx].adap = adap; + memcpy(dev_rx->xfers[idx].msg, msg->msg, CEC_MAX_MSG_SIZE); + dev_rx->xfers[idx].len = msg->len; + dev_rx->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_RETRY; if (signal_free_time > CEC_SIGNAL_FREE_TIME_RETRY) { - if (idx == dev->last_initiator) - dev->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_NEXT_XFER; + if (idx == dev_rx->last_initiator) + dev_rx->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_NEXT_XFER; else - dev->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_NEW_INITIATOR; + dev_rx->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_NEW_INITIATOR; } - spin_unlock(&dev->cec_xfers_slock); - wake_up_interruptible(&dev->kthread_waitq_cec); + spin_unlock(&dev_rx->cec_xfers_slock); + wake_up_interruptible(&dev_rx->kthread_waitq_cec); return 0; } diff --git a/drivers/media/test-drivers/vivid/vivid-core.c b/drivers/media/test-drivers/vivid/vivid-core.c index 0273bc9863b0..00e0d08af357 100644 --- a/drivers/media/test-drivers/vivid/vivid-core.c +++ b/drivers/media/test-drivers/vivid/vivid-core.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only + /* * vivid-core.c - A Virtual Video Test Driver, core initialization * @@ -42,15 +43,13 @@ #include "vivid-touch-cap.h" #define VIVID_MODULE_NAME "vivid" - -/* The maximum number of vivid devices */ -#define VIVID_MAX_DEVS CONFIG_VIDEO_VIVID_MAX_DEVS +#define MAX_STRING_LENGTH 23 MODULE_DESCRIPTION("Virtual Video Test Driver"); MODULE_AUTHOR("Hans Verkuil"); MODULE_LICENSE("GPL"); -static unsigned n_devs = 1; +unsigned int n_devs = 1; module_param(n_devs, uint, 0444); MODULE_PARM_DESC(n_devs, " number of driver instances to create"); @@ -186,7 +185,32 @@ MODULE_PARM_DESC(supports_requests, " support for requests, default is 1.\n" "\t\t 1 == supports requests\n" "\t\t 2 == requires requests"); -static struct vivid_dev *vivid_devs[VIVID_MAX_DEVS]; +struct vivid_dev *vivid_devs[VIVID_MAX_DEVS]; + +DEFINE_SPINLOCK(hdmi_output_skip_mask_lock); +struct workqueue_struct *update_hdmi_ctrls_workqueue; +u64 hdmi_to_output_menu_skip_mask; +u64 hdmi_input_update_outputs_mask; + +struct vivid_dev *vivid_ctrl_hdmi_to_output_instance[MAX_MENU_ITEMS]; +unsigned int vivid_ctrl_hdmi_to_output_index[MAX_MENU_ITEMS]; + +char *vivid_ctrl_hdmi_to_output_strings[MAX_MENU_ITEMS + 1] = { + "Test Pattern Generator", + "None" +}; + +DEFINE_SPINLOCK(svid_output_skip_mask_lock); +struct workqueue_struct *update_svid_ctrls_workqueue; +u64 svid_to_output_menu_skip_mask; + +struct vivid_dev *vivid_ctrl_svid_to_output_instance[MAX_MENU_ITEMS]; +unsigned int vivid_ctrl_svid_to_output_index[MAX_MENU_ITEMS]; + +char *vivid_ctrl_svid_to_output_strings[MAX_MENU_ITEMS + 1] = { + "Test Pattern Generator", + "None" +}; const struct v4l2_rect vivid_min_rect = { 0, 0, MIN_WIDTH, MIN_HEIGHT @@ -218,7 +242,7 @@ static const u8 vivid_hdmi_edid[256] = { 0x5e, 0x5d, 0x10, 0x1f, 0x04, 0x13, 0x22, 0x21, 0x20, 0x05, 0x14, 0x02, 0x11, 0x01, 0x23, 0x09, 0x07, 0x07, 0x83, 0x01, 0x00, 0x00, 0x6d, 0x03, - 0x0c, 0x00, 0x10, 0x00, 0x00, 0x3c, 0x21, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x21, 0x00, 0x60, 0x01, 0x02, 0x03, 0x67, 0xd8, 0x5d, 0xc4, 0x01, 0x78, 0x00, 0x00, 0xe2, 0x00, 0xca, 0xe3, 0x05, 0x00, 0x00, 0xe3, 0x06, 0x01, 0x00, 0x4d, @@ -229,7 +253,7 @@ static const u8 vivid_hdmi_edid[256] = { 0x00, 0x00, 0x1a, 0x1a, 0x1d, 0x00, 0x80, 0x51, 0xd0, 0x1c, 0x20, 0x40, 0x80, 0x35, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, }; static int vidioc_querycap(struct file *file, void *priv, @@ -827,6 +851,7 @@ static void vivid_dev_release(struct v4l2_device *v4l2_dev) { struct vivid_dev *dev = container_of(v4l2_dev, struct vivid_dev, v4l2_dev); + cancel_work_sync(&dev->update_hdmi_ctrl_work); vivid_free_controls(dev); v4l2_device_unregister(&dev->v4l2_dev); #ifdef CONFIG_MEDIA_CONTROLLER @@ -946,6 +971,7 @@ static int vivid_detect_feature_set(struct vivid_dev *dev, int inst, dev->num_inputs--; } dev->num_hdmi_inputs = in_type_counter[HDMI]; + dev->num_svid_inputs = in_type_counter[SVID]; /* how many outputs do we have and of what type? */ dev->num_outputs = num_outputs[inst]; @@ -960,7 +986,6 @@ static int vivid_detect_feature_set(struct vivid_dev *dev, int inst, for (i = 0; i < dev->num_outputs; i++) { dev->output_type[i] = ((output_types[inst] >> i) & 1) ? HDMI : SVID; dev->output_name_counter[i] = out_type_counter[dev->output_type[i]]++; - dev->display_present[i] = true; } dev->has_audio_outputs = out_type_counter[SVID]; if (out_type_counter[HDMI] == 16) { @@ -1393,7 +1418,6 @@ static int vivid_create_queues(struct vivid_dev *dev) static int vivid_create_devnodes(struct platform_device *pdev, struct vivid_dev *dev, int inst, - unsigned int cec_tx_bus_cnt, v4l2_std_id tvnorms_cap, v4l2_std_id tvnorms_out, unsigned in_type_counter[4], @@ -1437,7 +1461,7 @@ static int vivid_create_devnodes(struct platform_device *pdev, return ret; } cec_s_phys_addr(dev->cec_rx_adap, 0, false); - v4l2_info(&dev->v4l2_dev, "CEC adapter %s registered for HDMI input 0\n", + v4l2_info(&dev->v4l2_dev, "CEC adapter %s registered for HDMI input\n", dev_name(&dev->cec_rx_adap->devnode.dev)); } #endif @@ -1480,10 +1504,10 @@ static int vivid_create_devnodes(struct platform_device *pdev, #endif #ifdef CONFIG_VIDEO_VIVID_CEC - for (i = 0; i < cec_tx_bus_cnt; i++) { + for (i = 0; i < dev->num_hdmi_outputs; i++) { ret = cec_register_adapter(dev->cec_tx_adap[i], &pdev->dev); if (ret < 0) { - for (; i < cec_tx_bus_cnt; i++) { + for (; i >= 0; i--) { cec_delete_adapter(dev->cec_tx_adap[i]); dev->cec_tx_adap[i] = NULL; } @@ -1491,10 +1515,6 @@ static int vivid_create_devnodes(struct platform_device *pdev, } v4l2_info(&dev->v4l2_dev, "CEC adapter %s registered for HDMI output %d\n", dev_name(&dev->cec_tx_adap[i]->devnode.dev), i); - if (i < out_type_counter[HDMI]) - cec_s_phys_addr(dev->cec_tx_adap[i], (i + 1) << 12, false); - else - cec_s_phys_addr(dev->cec_tx_adap[i], 0x1000, false); } #endif @@ -1734,6 +1754,47 @@ static int vivid_create_devnodes(struct platform_device *pdev, return 0; } +static void update_hdmi_ctrls_work_handler(struct work_struct *work) +{ + u64 skip_mask; + u64 update_mask; + + spin_lock(&hdmi_output_skip_mask_lock); + skip_mask = hdmi_to_output_menu_skip_mask; + update_mask = hdmi_input_update_outputs_mask; + hdmi_input_update_outputs_mask = 0; + spin_unlock(&hdmi_output_skip_mask_lock); + for (int i = 0; i < n_devs && vivid_devs[i]; i++) { + if (update_mask & (1 << i)) + vivid_update_connected_outputs(vivid_devs[i]); + for (int j = 0; j < vivid_devs[i]->num_hdmi_inputs; j++) { + struct v4l2_ctrl *c = vivid_devs[i]->ctrl_hdmi_to_output[j]; + + v4l2_ctrl_modify_range(c, c->minimum, c->maximum, + skip_mask & ~(1ULL << c->cur.val), + c->default_value); + } + } +} + +static void update_svid_ctrls_work_handler(struct work_struct *work) +{ + u64 skip_mask; + + spin_lock(&svid_output_skip_mask_lock); + skip_mask = svid_to_output_menu_skip_mask; + spin_unlock(&svid_output_skip_mask_lock); + for (int i = 0; i < n_devs && vivid_devs[i]; i++) { + for (int j = 0; j < vivid_devs[i]->num_svid_inputs; j++) { + struct v4l2_ctrl *c = vivid_devs[i]->ctrl_svid_to_output[j]; + + v4l2_ctrl_modify_range(c, c->minimum, c->maximum, + skip_mask & ~(1ULL << c->cur.val), + c->default_value); + } + } +} + static int vivid_create_instance(struct platform_device *pdev, int inst) { static const struct v4l2_dv_timings def_dv_timings = @@ -1747,7 +1808,6 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) struct vivid_dev *dev; unsigned node_type = node_types[inst]; v4l2_std_id tvnorms_cap = 0, tvnorms_out = 0; - unsigned int cec_tx_bus_cnt = 0; int ret; int i; @@ -1850,6 +1910,22 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) dev->edid_max_blocks = dev->edid_blocks = 2; memcpy(dev->edid, vivid_hdmi_edid, sizeof(vivid_hdmi_edid)); dev->radio_rds_init_time = ktime_get(); + INIT_WORK(&dev->update_hdmi_ctrl_work, update_hdmi_ctrls_work_handler); + INIT_WORK(&dev->update_svid_ctrl_work, update_svid_ctrls_work_handler); + for (int j = 0, k = 0; j < dev->num_inputs; ++j) + if (dev->input_type[j] == HDMI) + dev->hdmi_index_to_input_index[k++] = j; + for (int j = 0, k = 0; j < dev->num_outputs; ++j) + if (dev->output_type[j] == HDMI) { + dev->output_to_iface_index[j] = k; + dev->hdmi_index_to_output_index[k++] = j; + } + for (int j = 0, k = 0; j < dev->num_inputs; ++j) + if (dev->input_type[j] == SVID) + dev->svid_index_to_input_index[k++] = j; + for (int j = 0, k = 0; j < dev->num_outputs; ++j) + if (dev->output_type[j] == SVID) + dev->output_to_iface_index[j] = k++; /* create all controls */ ret = vivid_create_controls(dev, ccs_cap == -1, ccs_out == -1, no_error_inj, @@ -1860,8 +1936,6 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) goto unreg_dev; /* enable/disable interface specific controls */ - if (dev->num_outputs && dev->output_type[0] != HDMI) - v4l2_ctrl_activate(dev->ctrl_display_present, false); if (dev->num_inputs && dev->input_type[0] != HDMI) { v4l2_ctrl_activate(dev->ctrl_dv_timings_signal_mode, false); v4l2_ctrl_activate(dev->ctrl_dv_timings, false); @@ -1917,27 +1991,27 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) } if (dev->has_vid_out) { - for (i = 0; i < dev->num_outputs; i++) { + int j; + + for (i = j = 0; i < dev->num_outputs; i++) { struct cec_adapter *adap; if (dev->output_type[i] != HDMI) continue; - dev->cec_output2bus_map[i] = cec_tx_bus_cnt; - adap = vivid_cec_alloc_adap(dev, cec_tx_bus_cnt, true); + adap = vivid_cec_alloc_adap(dev, j, true); ret = PTR_ERR_OR_ZERO(adap); if (ret < 0) { - for (i = 0; i < dev->num_outputs; i++) - cec_delete_adapter(dev->cec_tx_adap[i]); + while (j--) + cec_delete_adapter(dev->cec_tx_adap[j]); goto unreg_dev; } - dev->cec_tx_adap[cec_tx_bus_cnt] = adap; - cec_tx_bus_cnt++; + dev->cec_tx_adap[j++] = adap; } } - if (dev->cec_rx_adap || cec_tx_bus_cnt) { + if (dev->cec_rx_adap || dev->num_hdmi_outputs) { init_waitqueue_head(&dev->kthread_waitq_cec); dev->kthread_cec = kthread_run(vivid_cec_bus_thread, dev, "vivid_cec-%s", dev->v4l2_dev.name); @@ -1963,7 +2037,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) v4l2_ctrl_handler_setup(&dev->ctrl_hdl_touch_cap); /* finally start creating the device nodes */ - ret = vivid_create_devnodes(pdev, dev, inst, cec_tx_bus_cnt, + ret = vivid_create_devnodes(pdev, dev, inst, tvnorms_cap, tvnorms_out, in_type_counter, out_type_counter); if (ret) @@ -1986,7 +2060,7 @@ unreg_dev: vb2_video_unregister_device(&dev->vid_out_dev); vb2_video_unregister_device(&dev->vid_cap_dev); cec_unregister_adapter(dev->cec_rx_adap); - for (i = 0; i < MAX_OUTPUTS; i++) + for (i = 0; i < MAX_HDMI_OUTPUTS; i++) cec_unregister_adapter(dev->cec_tx_adap[i]); if (dev->kthread_cec) kthread_stop(dev->kthread_cec); @@ -2033,6 +2107,42 @@ static int vivid_probe(struct platform_device *pdev) /* n_devs will reflect the actual number of allocated devices */ n_devs = i; + /* Determine qmenu items actually in use */ + int hdmi_count = FIXED_MENU_ITEMS; + int svid_count = FIXED_MENU_ITEMS; + + for (int i = 0; i < n_devs; i++) { + struct vivid_dev *dev = vivid_devs[i]; + + if (!dev->has_vid_out) + continue; + for (int j = 0; j < dev->num_outputs && hdmi_count < MAX_MENU_ITEMS; ++j) { + if (dev->output_type[j] == HDMI) { + vivid_ctrl_hdmi_to_output_instance[hdmi_count] = vivid_devs[i]; + vivid_ctrl_hdmi_to_output_index[hdmi_count++] = j; + } + } + for (int j = 0; j < dev->num_outputs && svid_count < MAX_MENU_ITEMS; ++j) { + if (dev->output_type[j] == SVID) { + vivid_ctrl_svid_to_output_instance[svid_count] = vivid_devs[i]; + vivid_ctrl_svid_to_output_index[svid_count++] = j; + } + } + } + hdmi_count = min(hdmi_count, MAX_MENU_ITEMS); + svid_count = min(svid_count, MAX_MENU_ITEMS); + for (int i = 0; i < n_devs; i++) { + for (int j = 0; j < vivid_devs[i]->num_hdmi_inputs; j++) { + struct v4l2_ctrl *c = vivid_devs[i]->ctrl_hdmi_to_output[j]; + + v4l2_ctrl_modify_range(c, c->minimum, hdmi_count - 1, 0, c->default_value); + } + for (int j = 0; j < vivid_devs[i]->num_svid_inputs; j++) { + struct v4l2_ctrl *c = vivid_devs[i]->ctrl_svid_to_output[j]; + + v4l2_ctrl_modify_range(c, c->minimum, svid_count - 1, 0, c->default_value); + } + } return ret; } @@ -2109,7 +2219,7 @@ static void vivid_remove(struct platform_device *pdev) vb2_video_unregister_device(&dev->touch_cap_dev); } cec_unregister_adapter(dev->cec_rx_adap); - for (j = 0; j < MAX_OUTPUTS; j++) + for (j = 0; j < MAX_HDMI_OUTPUTS; j++) cec_unregister_adapter(dev->cec_tx_adap[j]); if (dev->kthread_cec) kthread_stop(dev->kthread_cec); @@ -2137,21 +2247,91 @@ static struct platform_driver vivid_pdrv = { static int __init vivid_init(void) { - int ret; - + int hdmi_count = FIXED_MENU_ITEMS; + int svid_count = FIXED_MENU_ITEMS; + int ret = -ENOMEM; + unsigned int ndevs; + + /* Sanity check, prevent insane number of vivid instances */ + if (n_devs > 64) + n_devs = 64; + ndevs = clamp_t(unsigned int, n_devs, 1, VIVID_MAX_DEVS); + + for (unsigned int i = 0; i < ndevs; i++) { + if (!(node_types[i] & (1 << 8))) + continue; + unsigned int n_outputs = min(num_outputs[i], MAX_OUTPUTS); + + for (u8 j = 0, k = 0; j < n_outputs && hdmi_count < MAX_MENU_ITEMS && + k < MAX_HDMI_OUTPUTS; ++j) { + if (output_types[i] & BIT(j)) { + vivid_ctrl_hdmi_to_output_strings[hdmi_count] = + kmalloc(MAX_STRING_LENGTH, GFP_KERNEL); + if (!vivid_ctrl_hdmi_to_output_strings[hdmi_count]) + goto free_output_strings; + snprintf(vivid_ctrl_hdmi_to_output_strings[hdmi_count], + MAX_STRING_LENGTH, "Output HDMI %03d-%d", + i & 0xff, k); + k++; + hdmi_count++; + } + } + for (u8 j = 0, k = 0; j < n_outputs && svid_count < MAX_MENU_ITEMS; ++j) { + if (!(output_types[i] & BIT(j))) { + vivid_ctrl_svid_to_output_strings[svid_count] = + kmalloc(MAX_STRING_LENGTH, GFP_KERNEL); + if (!vivid_ctrl_svid_to_output_strings[svid_count]) + goto free_output_strings; + snprintf(vivid_ctrl_svid_to_output_strings[svid_count], + MAX_STRING_LENGTH, "Output S-Video %03d-%d", + i & 0xff, k); + k++; + svid_count++; + } + } + } ret = platform_device_register(&vivid_pdev); if (ret) - return ret; - + goto free_output_strings; ret = platform_driver_register(&vivid_pdrv); if (ret) - platform_device_unregister(&vivid_pdev); + goto unreg_device; + /* Initialize workqueue before module is loaded */ + update_hdmi_ctrls_workqueue = create_workqueue("update_hdmi_ctrls_wq"); + if (!update_hdmi_ctrls_workqueue) { + ret = -ENOMEM; + goto unreg_driver; + } + update_svid_ctrls_workqueue = create_workqueue("update_svid_ctrls_wq"); + if (!update_svid_ctrls_workqueue) { + ret = -ENOMEM; + goto destroy_hdmi_wq; + } + return ret; + +destroy_hdmi_wq: + destroy_workqueue(update_hdmi_ctrls_workqueue); +unreg_driver: + platform_driver_register(&vivid_pdrv); +unreg_device: + platform_device_unregister(&vivid_pdev); +free_output_strings: + for (int i = FIXED_MENU_ITEMS; i < MAX_MENU_ITEMS; i++) { + kfree(vivid_ctrl_hdmi_to_output_strings[i]); + kfree(vivid_ctrl_svid_to_output_strings[i]); + } return ret; } static void __exit vivid_exit(void) { + for (int i = FIXED_MENU_ITEMS; i < MAX_MENU_ITEMS; i++) { + kfree(vivid_ctrl_hdmi_to_output_strings[i]); + kfree(vivid_ctrl_svid_to_output_strings[i]); + } + destroy_workqueue(update_svid_ctrls_workqueue); + destroy_workqueue(update_hdmi_ctrls_workqueue); platform_driver_unregister(&vivid_pdrv); platform_device_unregister(&vivid_pdev); } diff --git a/drivers/media/test-drivers/vivid/vivid-core.h b/drivers/media/test-drivers/vivid/vivid-core.h index cfb8e66083f6..cc18a3bc6dc0 100644 --- a/drivers/media/test-drivers/vivid/vivid-core.h +++ b/drivers/media/test-drivers/vivid/vivid-core.h @@ -50,10 +50,95 @@ #define JIFFIES_PER_DAY (3600U * 24U * HZ) #define JIFFIES_RESYNC (JIFFIES_PER_DAY * (0xf0000000U / JIFFIES_PER_DAY)) +/* + * Maximum number of HDMI inputs allowed by vivid, due to limitations + * of the Physical Address in the EDID and used by CEC we stop at 15 + * inputs and outputs. + */ +#define MAX_HDMI_INPUTS 15 +#define MAX_HDMI_OUTPUTS 15 + +/* Maximum number of S-Video inputs allowed by vivid */ +#define MAX_SVID_INPUTS 16 + +/* The maximum number of items in a menu control */ +#define MAX_MENU_ITEMS BITS_PER_LONG_LONG + +/* Number of fixed menu items in the 'Connected To' menu controls */ +#define FIXED_MENU_ITEMS 2 + +/* The maximum number of vivid devices */ +#define VIVID_MAX_DEVS CONFIG_VIDEO_VIVID_MAX_DEVS + extern const struct v4l2_rect vivid_min_rect; extern const struct v4l2_rect vivid_max_rect; extern unsigned vivid_debug; +/* + * NULL-terminated string array for the HDMI 'Connected To' menu controls + * with the list of possible HDMI outputs. + * + * The first two items are fixed ("TPG" and "None"). + */ +extern char *vivid_ctrl_hdmi_to_output_strings[1 + MAX_MENU_ITEMS]; +/* Menu control skip mask of all HDMI outputs that are in use */ +extern u64 hdmi_to_output_menu_skip_mask; +/* + * Bitmask of which vivid instances need to update any connected + * HDMI outputs. + */ +extern u64 hdmi_input_update_outputs_mask; +/* + * Spinlock for access to hdmi_to_output_menu_skip_mask and + * hdmi_input_update_outputs_mask. + */ +extern spinlock_t hdmi_output_skip_mask_lock; +/* + * Workqueue that updates the menu controls whenever the HDMI menu skip mask + * changes. + */ +extern struct workqueue_struct *update_hdmi_ctrls_workqueue; + +/* + * The HDMI menu control value (index in the menu list) maps to an HDMI + * output that is part of the given vivid_dev instance and has the given + * output index (as returned by VIDIOC_G_OUTPUT). + * + * NULL/0 if not available. + */ +extern struct vivid_dev *vivid_ctrl_hdmi_to_output_instance[MAX_MENU_ITEMS]; +extern unsigned int vivid_ctrl_hdmi_to_output_index[MAX_MENU_ITEMS]; + +/* + * NULL-terminated string array for the S-Video 'Connected To' menu controls + * with the list of possible S-Video outputs. + * + * The first two items are fixed ("TPG" and "None"). + */ +extern char *vivid_ctrl_svid_to_output_strings[1 + MAX_MENU_ITEMS]; +/* Menu control skip mask of all S-Video outputs that are in use */ +extern u64 svid_to_output_menu_skip_mask; +/* Spinlock for access to svid_to_output_menu_skip_mask */ +extern spinlock_t svid_output_skip_mask_lock; +/* + * Workqueue that updates the menu controls whenever the S-Video menu skip mask + * changes. + */ +extern struct workqueue_struct *update_svid_ctrls_workqueue; + +/* + * The S-Video menu control value (index in the menu list) maps to an S-Video + * output that is part of the given vivid_dev instance and has the given + * output index (as returned by VIDIOC_G_OUTPUT). + * + * NULL/0 if not available. + */ +extern struct vivid_dev *vivid_ctrl_svid_to_output_instance[MAX_MENU_ITEMS]; +extern unsigned int vivid_ctrl_svid_to_output_index[MAX_MENU_ITEMS]; + +extern struct vivid_dev *vivid_devs[VIVID_MAX_DEVS]; +extern unsigned int n_devs; + struct vivid_fmt { u32 fourcc; /* v4l2 format id */ enum tgp_color_enc color_enc; @@ -118,7 +203,7 @@ struct vivid_cec_xfer { }; struct vivid_dev { - unsigned inst; + u8 inst; struct v4l2_device v4l2_dev; #ifdef CONFIG_MEDIA_CONTROLLER struct media_device mdev; @@ -161,6 +246,8 @@ struct vivid_dev { spinlock_t slock; struct mutex mutex; + struct work_struct update_hdmi_ctrl_work; + struct work_struct update_svid_ctrl_work; /* capabilities */ u32 vid_cap_caps; @@ -176,12 +263,13 @@ struct vivid_dev { /* supported features */ bool multiplanar; - unsigned num_inputs; - unsigned int num_hdmi_inputs; + u8 num_inputs; + u8 num_hdmi_inputs; + u8 num_svid_inputs; u8 input_type[MAX_INPUTS]; u8 input_name_counter[MAX_INPUTS]; - unsigned num_outputs; - unsigned int num_hdmi_outputs; + u8 num_outputs; + u8 num_hdmi_outputs; u8 output_type[MAX_OUTPUTS]; u8 output_name_counter[MAX_OUTPUTS]; bool has_audio_inputs; @@ -203,7 +291,20 @@ struct vivid_dev { bool has_tv_tuner; bool has_touch_cap; - bool can_loop_video; + /* Output index (0-MAX_OUTPUTS) to vivid instance of connected input */ + struct vivid_dev *output_to_input_instance[MAX_OUTPUTS]; + /* Output index (0-MAX_OUTPUTS) to input index (0-MAX_INPUTS) of connected input */ + u8 output_to_input_index[MAX_OUTPUTS]; + /* Output index (0-MAX_OUTPUTS) to HDMI or S-Video output index (0-MAX_HDMI/SVID_OUTPUTS) */ + u8 output_to_iface_index[MAX_OUTPUTS]; + /* ctrl_hdmi_to_output or ctrl_svid_to_output control value for each input */ + s32 input_is_connected_to_output[MAX_INPUTS]; + /* HDMI index (0-MAX_HDMI_OUTPUTS) to output index (0-MAX_OUTPUTS) */ + u8 hdmi_index_to_output_index[MAX_HDMI_OUTPUTS]; + /* HDMI index (0-MAX_HDMI_INPUTS) to input index (0-MAX_INPUTS) */ + u8 hdmi_index_to_input_index[MAX_HDMI_INPUTS]; + /* S-Video index (0-MAX_SVID_INPUTS) to input index (0-MAX_INPUTS) */ + u8 svid_index_to_input_index[MAX_SVID_INPUTS]; /* controls */ struct v4l2_ctrl *brightness; @@ -242,7 +343,6 @@ struct vivid_dev { struct v4l2_ctrl *ctrl_dv_timings_signal_mode; struct v4l2_ctrl *ctrl_dv_timings; }; - struct v4l2_ctrl *ctrl_display_present; struct v4l2_ctrl *ctrl_has_crop_cap; struct v4l2_ctrl *ctrl_has_compose_cap; struct v4l2_ctrl *ctrl_has_scaler_cap; @@ -276,6 +376,11 @@ struct vivid_dev { struct v4l2_ctrl *radio_rx_rds_psname; struct v4l2_ctrl *radio_rx_rds_radiotext; + struct v4l2_ctrl *ctrl_hdmi_to_output[MAX_HDMI_INPUTS]; + char ctrl_hdmi_to_output_names[MAX_HDMI_INPUTS][32]; + struct v4l2_ctrl *ctrl_svid_to_output[MAX_SVID_INPUTS]; + char ctrl_svid_to_output_names[MAX_SVID_INPUTS][32]; + unsigned input_brightness[MAX_INPUTS]; unsigned osd_mode; unsigned button_pressed; @@ -364,7 +469,6 @@ struct vivid_dev { u8 *scaled_line; u8 *blended_line; unsigned cur_scaled_line; - bool display_present[MAX_OUTPUTS]; /* Output Overlay */ void *fb_vbase_out; @@ -544,11 +648,10 @@ struct vivid_dev { /* CEC */ struct cec_adapter *cec_rx_adap; - struct cec_adapter *cec_tx_adap[MAX_OUTPUTS]; - u8 cec_output2bus_map[MAX_OUTPUTS]; + struct cec_adapter *cec_tx_adap[MAX_HDMI_OUTPUTS]; struct task_struct *kthread_cec; wait_queue_head_t kthread_waitq_cec; - struct vivid_cec_xfer xfers[MAX_OUTPUTS]; + struct vivid_cec_xfer xfers[MAX_OUTPUTS]; spinlock_t cec_xfers_slock; /* read and write cec messages */ u32 cec_sft; /* bus signal free time, in bit periods */ u8 last_initiator; diff --git a/drivers/media/test-drivers/vivid/vivid-ctrls.c b/drivers/media/test-drivers/vivid/vivid-ctrls.c index f2b20e25a7a4..8bb38bc7b8cc 100644 --- a/drivers/media/test-drivers/vivid/vivid-ctrls.c +++ b/drivers/media/test-drivers/vivid/vivid-ctrls.c @@ -18,7 +18,6 @@ #include "vivid-radio-common.h" #include "vivid-osd.h" #include "vivid-ctrls.h" -#include "vivid-cec.h" #define VIVID_CID_CUSTOM_BASE (V4L2_CID_USER_BASE | 0xf000) #define VIVID_CID_BUTTON (VIVID_CID_CUSTOM_BASE + 0) @@ -69,14 +68,12 @@ #define VIVID_CID_HAS_CROP_OUT (VIVID_CID_VIVID_BASE + 34) #define VIVID_CID_HAS_COMPOSE_OUT (VIVID_CID_VIVID_BASE + 35) #define VIVID_CID_HAS_SCALER_OUT (VIVID_CID_VIVID_BASE + 36) -#define VIVID_CID_LOOP_VIDEO (VIVID_CID_VIVID_BASE + 37) #define VIVID_CID_SEQ_WRAP (VIVID_CID_VIVID_BASE + 38) #define VIVID_CID_TIME_WRAP (VIVID_CID_VIVID_BASE + 39) #define VIVID_CID_MAX_EDID_BLOCKS (VIVID_CID_VIVID_BASE + 40) #define VIVID_CID_PERCENTAGE_FILL (VIVID_CID_VIVID_BASE + 41) #define VIVID_CID_REDUCED_FPS (VIVID_CID_VIVID_BASE + 42) #define VIVID_CID_HSV_ENC (VIVID_CID_VIVID_BASE + 43) -#define VIVID_CID_DISPLAY_PRESENT (VIVID_CID_VIVID_BASE + 44) #define VIVID_CID_STD_SIGNAL_MODE (VIVID_CID_VIVID_BASE + 60) #define VIVID_CID_STANDARD (VIVID_CID_VIVID_BASE + 61) @@ -104,6 +101,12 @@ #define VIVID_CID_META_CAP_GENERATE_PTS (VIVID_CID_VIVID_BASE + 111) #define VIVID_CID_META_CAP_GENERATE_SCR (VIVID_CID_VIVID_BASE + 112) +/* HDMI inputs are in the range 0-14. The next available CID is VIVID_CID_VIVID_BASE + 128 */ +#define VIVID_CID_HDMI_IS_CONNECTED_TO_OUTPUT(input) (VIVID_CID_VIVID_BASE + 113 + (input)) + +/* S-Video inputs are in the range 0-15. The next available CID is VIVID_CID_VIVID_BASE + 144 */ +#define VIVID_CID_SVID_IS_CONNECTED_TO_OUTPUT(input) (VIVID_CID_VIVID_BASE + 128 + (input)) + /* General User Controls */ static void vivid_unregister_dev(bool valid, struct video_device *vdev) @@ -439,6 +442,33 @@ static const struct v4l2_ctrl_ops vivid_user_vid_ctrl_ops = { /* Video Capture Controls */ +static void vivid_update_power_present(struct vivid_dev *dev) +{ + unsigned int i, j; + + dev->power_present = 0; + for (i = 0, j = 0; + i < ARRAY_SIZE(dev->dv_timings_signal_mode); i++) { + if (dev->input_type[i] != HDMI) + continue; + /* + * If connected to TPG or HDMI output, and the signal + * mode is not NO_SIGNAL, then there is power present. + */ + if (dev->input_is_connected_to_output[i] != 1 && + dev->dv_timings_signal_mode[i] != NO_SIGNAL) + dev->power_present |= (1 << j); + j++; + } + + __v4l2_ctrl_s_ctrl(dev->ctrl_rx_power_present, + dev->power_present); + + v4l2_ctrl_activate(dev->ctrl_dv_timings, + dev->dv_timings_signal_mode[dev->input] == + SELECTED_DV_TIMINGS); +} + static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl) { static const u32 colorspaces[] = { @@ -453,7 +483,11 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl) V4L2_COLORSPACE_470_SYSTEM_BG, }; struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_vid_cap); - unsigned int i, j; + unsigned int i; + struct vivid_dev *output_inst = NULL; + int index = 0; + int hdmi_index, svid_index; + s32 input_index = 0; switch (ctrl->id) { case VIVID_CID_TEST_PATTERN: @@ -569,25 +603,9 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl) dev->dv_timings_signal_mode[dev->input] = dev->ctrl_dv_timings_signal_mode->val; dev->query_dv_timings[dev->input] = dev->ctrl_dv_timings->val; - - dev->power_present = 0; - for (i = 0, j = 0; - i < ARRAY_SIZE(dev->dv_timings_signal_mode); - i++) - if (dev->input_type[i] == HDMI) { - if (dev->dv_timings_signal_mode[i] != NO_SIGNAL) - dev->power_present |= (1 << j); - j++; - } - __v4l2_ctrl_s_ctrl(dev->ctrl_rx_power_present, - dev->power_present); - - v4l2_ctrl_activate(dev->ctrl_dv_timings, - dev->dv_timings_signal_mode[dev->input] == - SELECTED_DV_TIMINGS); - + vivid_update_power_present(dev); vivid_update_quality(dev); - vivid_send_source_change(dev, HDMI); + vivid_send_input_source_change(dev, dev->input); break; case VIVID_CID_DV_TIMINGS_ASPECT_RATIO: dev->dv_timings_aspect_ratio[dev->input] = ctrl->val; @@ -604,6 +622,67 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl) if (dev->edid_blocks > dev->edid_max_blocks) dev->edid_blocks = dev->edid_max_blocks; break; + case VIVID_CID_HDMI_IS_CONNECTED_TO_OUTPUT(0) ... VIVID_CID_HDMI_IS_CONNECTED_TO_OUTPUT(14): + hdmi_index = ctrl->id - VIVID_CID_HDMI_IS_CONNECTED_TO_OUTPUT(0); + output_inst = vivid_ctrl_hdmi_to_output_instance[ctrl->cur.val]; + index = vivid_ctrl_hdmi_to_output_index[ctrl->cur.val]; + input_index = dev->hdmi_index_to_input_index[hdmi_index]; + dev->input_is_connected_to_output[input_index] = ctrl->val; + + if (output_inst) { + output_inst->output_to_input_instance[index] = NULL; + vivid_update_outputs(output_inst); + cec_phys_addr_invalidate(output_inst->cec_tx_adap[index]); + } + if (ctrl->val >= FIXED_MENU_ITEMS) { + output_inst = vivid_ctrl_hdmi_to_output_instance[ctrl->val]; + index = vivid_ctrl_hdmi_to_output_index[ctrl->val]; + output_inst->output_to_input_instance[index] = dev; + output_inst->output_to_input_index[index] = + dev->hdmi_index_to_input_index[hdmi_index]; + } + spin_lock(&hdmi_output_skip_mask_lock); + hdmi_to_output_menu_skip_mask &= ~(1ULL << ctrl->cur.val); + if (ctrl->val >= FIXED_MENU_ITEMS) + hdmi_to_output_menu_skip_mask |= 1ULL << ctrl->val; + spin_unlock(&hdmi_output_skip_mask_lock); + vivid_update_power_present(dev); + vivid_update_quality(dev); + vivid_send_input_source_change(dev, dev->hdmi_index_to_input_index[hdmi_index]); + if (ctrl->val < FIXED_MENU_ITEMS && ctrl->cur.val < FIXED_MENU_ITEMS) + break; + spin_lock(&hdmi_output_skip_mask_lock); + hdmi_input_update_outputs_mask |= 1 << dev->inst; + spin_unlock(&hdmi_output_skip_mask_lock); + queue_work(update_hdmi_ctrls_workqueue, &dev->update_hdmi_ctrl_work); + break; + case VIVID_CID_SVID_IS_CONNECTED_TO_OUTPUT(0) ... VIVID_CID_SVID_IS_CONNECTED_TO_OUTPUT(15): + svid_index = ctrl->id - VIVID_CID_SVID_IS_CONNECTED_TO_OUTPUT(0); + output_inst = vivid_ctrl_svid_to_output_instance[ctrl->cur.val]; + index = vivid_ctrl_svid_to_output_index[ctrl->cur.val]; + input_index = dev->svid_index_to_input_index[svid_index]; + dev->input_is_connected_to_output[input_index] = ctrl->val; + + if (output_inst) + output_inst->output_to_input_instance[index] = NULL; + if (ctrl->val >= FIXED_MENU_ITEMS) { + output_inst = vivid_ctrl_svid_to_output_instance[ctrl->val]; + index = vivid_ctrl_svid_to_output_index[ctrl->val]; + output_inst->output_to_input_instance[index] = dev; + output_inst->output_to_input_index[index] = + dev->svid_index_to_input_index[svid_index]; + } + spin_lock(&svid_output_skip_mask_lock); + svid_to_output_menu_skip_mask &= ~(1ULL << ctrl->cur.val); + if (ctrl->val >= FIXED_MENU_ITEMS) + svid_to_output_menu_skip_mask |= 1ULL << ctrl->val; + spin_unlock(&svid_output_skip_mask_lock); + vivid_update_quality(dev); + vivid_send_input_source_change(dev, dev->svid_index_to_input_index[svid_index]); + if (ctrl->val < FIXED_MENU_ITEMS && ctrl->cur.val < FIXED_MENU_ITEMS) + break; + queue_work(update_svid_ctrls_workqueue, &dev->update_svid_ctrl_work); + break; } return 0; } @@ -966,37 +1045,6 @@ static const struct v4l2_ctrl_config vivid_ctrl_limited_rgb_range = { }; -/* Video Loop Control */ - -static int vivid_loop_cap_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_loop_cap); - - switch (ctrl->id) { - case VIVID_CID_LOOP_VIDEO: - dev->loop_video = ctrl->val; - vivid_update_quality(dev); - vivid_send_source_change(dev, SVID); - vivid_send_source_change(dev, HDMI); - break; - } - return 0; -} - -static const struct v4l2_ctrl_ops vivid_loop_cap_ctrl_ops = { - .s_ctrl = vivid_loop_cap_s_ctrl, -}; - -static const struct v4l2_ctrl_config vivid_ctrl_loop_video = { - .ops = &vivid_loop_cap_ctrl_ops, - .id = VIVID_CID_LOOP_VIDEO, - .name = "Loop Video", - .type = V4L2_CTRL_TYPE_BOOLEAN, - .max = 1, - .step = 1, -}; - - /* VBI Capture Control */ static int vivid_vbi_cap_s_ctrl(struct v4l2_ctrl *ctrl) @@ -1031,8 +1079,6 @@ static int vivid_vid_out_s_ctrl(struct v4l2_ctrl *ctrl) { struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_vid_out); struct v4l2_bt_timings *bt = &dev->dv_timings_out.bt; - u32 display_present = 0; - unsigned int i, j, bus_idx; switch (ctrl->id) { case VIVID_CID_HAS_CROP_OUT: @@ -1063,39 +1109,11 @@ static int vivid_vid_out_s_ctrl(struct v4l2_ctrl *ctrl) V4L2_QUANTIZATION_LIM_RANGE : V4L2_QUANTIZATION_DEFAULT; } - if (dev->loop_video) - vivid_send_source_change(dev, HDMI); - break; - case VIVID_CID_DISPLAY_PRESENT: - if (dev->output_type[dev->output] != HDMI) - break; - - dev->display_present[dev->output] = ctrl->val; - for (i = 0, j = 0; i < dev->num_outputs; i++) - if (dev->output_type[i] == HDMI) - display_present |= - dev->display_present[i] << j++; - - __v4l2_ctrl_s_ctrl(dev->ctrl_tx_rxsense, display_present); + if (vivid_output_is_connected_to(dev)) { + struct vivid_dev *dev_rx = vivid_output_is_connected_to(dev); - if (dev->edid_blocks) { - __v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present, - display_present); - __v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug, - display_present); + vivid_send_source_change(dev_rx, HDMI); } - - bus_idx = dev->cec_output2bus_map[dev->output]; - if (!dev->cec_tx_adap[bus_idx]) - break; - - if (ctrl->val && dev->edid_blocks) - cec_s_phys_addr(dev->cec_tx_adap[bus_idx], - dev->cec_tx_adap[bus_idx]->phys_addr, - false); - else - cec_phys_addr_invalidate(dev->cec_tx_adap[bus_idx]); - break; } return 0; @@ -1135,16 +1153,6 @@ static const struct v4l2_ctrl_config vivid_ctrl_has_scaler_out = { .step = 1, }; -static const struct v4l2_ctrl_config vivid_ctrl_display_present = { - .ops = &vivid_vid_out_ctrl_ops, - .id = VIVID_CID_DISPLAY_PRESENT, - .name = "Display Present", - .type = V4L2_CTRL_TYPE_BOOLEAN, - .max = 1, - .def = 1, - .step = 1, -}; - /* Streaming Controls */ static int vivid_streaming_s_ctrl(struct v4l2_ctrl *ctrl) @@ -1710,6 +1718,48 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, 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); + + WARN_ON(dev->num_hdmi_inputs > MAX_HDMI_INPUTS); + WARN_ON(dev->num_svid_inputs > MAX_SVID_INPUTS); + + for (u8 i = 0; i < dev->num_hdmi_inputs; i++) { + snprintf(dev->ctrl_hdmi_to_output_names[i], + sizeof(dev->ctrl_hdmi_to_output_names[i]), + "HDMI %03u-%u Is Connected To", dev->inst, i); + } + + for (u8 i = 0; i < dev->num_hdmi_inputs; i++) { + struct v4l2_ctrl_config ctrl_config = { + .ops = &vivid_vid_cap_ctrl_ops, + .id = VIVID_CID_HDMI_IS_CONNECTED_TO_OUTPUT(i), + .name = dev->ctrl_hdmi_to_output_names[i], + .type = V4L2_CTRL_TYPE_MENU, + .max = 1, + .qmenu = (const char * const *)vivid_ctrl_hdmi_to_output_strings, + }; + dev->ctrl_hdmi_to_output[i] = v4l2_ctrl_new_custom(hdl_vid_cap, + &ctrl_config, NULL); + } + + for (u8 i = 0; i < dev->num_svid_inputs; i++) { + snprintf(dev->ctrl_svid_to_output_names[i], + sizeof(dev->ctrl_svid_to_output_names[i]), + "S-Video %03u-%u Is Connected To", dev->inst, i); + } + + for (u8 i = 0; i < dev->num_svid_inputs; i++) { + struct v4l2_ctrl_config ctrl_config = { + .ops = &vivid_vid_cap_ctrl_ops, + .id = VIVID_CID_SVID_IS_CONNECTED_TO_OUTPUT(i), + .name = dev->ctrl_svid_to_output_names[i], + .type = V4L2_CTRL_TYPE_MENU, + .max = 1, + .qmenu = (const char * const *)vivid_ctrl_svid_to_output_strings, + }; + dev->ctrl_svid_to_output[i] = v4l2_ctrl_new_custom(hdl_vid_cap, + &ctrl_config, NULL); + } + if (show_ccs_cap) { dev->ctrl_has_crop_cap = v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_has_crop_cap, NULL); @@ -1812,21 +1862,13 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, dev->ctrl_tx_mode = v4l2_ctrl_new_std_menu(hdl_vid_out, NULL, V4L2_CID_DV_TX_MODE, V4L2_DV_TX_MODE_HDMI, 0, V4L2_DV_TX_MODE_HDMI); - dev->ctrl_display_present = v4l2_ctrl_new_custom(hdl_vid_out, - &vivid_ctrl_display_present, NULL); - dev->ctrl_tx_hotplug = v4l2_ctrl_new_std(hdl_vid_out, - NULL, V4L2_CID_DV_TX_HOTPLUG, 0, hdmi_output_mask, - 0, hdmi_output_mask); - dev->ctrl_tx_rxsense = v4l2_ctrl_new_std(hdl_vid_out, - NULL, V4L2_CID_DV_TX_RXSENSE, 0, hdmi_output_mask, - 0, hdmi_output_mask); - dev->ctrl_tx_edid_present = v4l2_ctrl_new_std(hdl_vid_out, - NULL, V4L2_CID_DV_TX_EDID_PRESENT, 0, hdmi_output_mask, - 0, hdmi_output_mask); + dev->ctrl_tx_hotplug = v4l2_ctrl_new_std(hdl_vid_out, NULL, + V4L2_CID_DV_TX_HOTPLUG, 0, hdmi_output_mask, 0, 0); + dev->ctrl_tx_rxsense = v4l2_ctrl_new_std(hdl_vid_out, NULL, + V4L2_CID_DV_TX_RXSENSE, 0, hdmi_output_mask, 0, 0); + dev->ctrl_tx_edid_present = v4l2_ctrl_new_std(hdl_vid_out, NULL, + V4L2_CID_DV_TX_EDID_PRESENT, 0, hdmi_output_mask, 0, 0); } - if ((dev->has_vid_cap && dev->has_vid_out) || - (dev->has_vbi_cap && dev->has_vbi_out)) - v4l2_ctrl_new_custom(hdl_loop_cap, &vivid_ctrl_loop_video, NULL); if (dev->has_fb) v4l2_ctrl_new_custom(hdl_fb, &vivid_ctrl_clear_fb, NULL); diff --git a/drivers/media/test-drivers/vivid/vivid-kthread-cap.c b/drivers/media/test-drivers/vivid/vivid-kthread-cap.c index 42048727d7ff..669bd96da4c7 100644 --- a/drivers/media/test-drivers/vivid/vivid-kthread-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-kthread-cap.c @@ -142,7 +142,7 @@ static void scale_line(const u8 *src, u8 *dst, unsigned srcw, unsigned dstw, uns * (loop_vid_overlay). Finally calculate the part of the capture buffer that * will receive that overlaid video. */ -static void vivid_precalc_copy_rects(struct vivid_dev *dev) +static void vivid_precalc_copy_rects(struct vivid_dev *dev, struct vivid_dev *out_dev) { /* Framebuffer rectangle */ struct v4l2_rect r_fb = { @@ -150,16 +150,16 @@ static void vivid_precalc_copy_rects(struct vivid_dev *dev) }; /* Overlay window rectangle in framebuffer coordinates */ struct v4l2_rect r_overlay = { - dev->overlay_out_left, dev->overlay_out_top, - dev->compose_out.width, dev->compose_out.height + out_dev->overlay_out_left, out_dev->overlay_out_top, + out_dev->compose_out.width, out_dev->compose_out.height }; - v4l2_rect_intersect(&dev->loop_vid_copy, &dev->crop_cap, &dev->compose_out); + v4l2_rect_intersect(&dev->loop_vid_copy, &dev->crop_cap, &out_dev->compose_out); dev->loop_vid_out = dev->loop_vid_copy; - v4l2_rect_scale(&dev->loop_vid_out, &dev->compose_out, &dev->crop_out); - dev->loop_vid_out.left += dev->crop_out.left; - dev->loop_vid_out.top += dev->crop_out.top; + v4l2_rect_scale(&dev->loop_vid_out, &out_dev->compose_out, &out_dev->crop_out); + dev->loop_vid_out.left += out_dev->crop_out.left; + dev->loop_vid_out.top += out_dev->crop_out.top; dev->loop_vid_cap = dev->loop_vid_copy; v4l2_rect_scale(&dev->loop_vid_cap, &dev->crop_cap, &dev->compose_cap); @@ -176,15 +176,15 @@ static void vivid_precalc_copy_rects(struct vivid_dev *dev) v4l2_rect_intersect(&r_overlay, &r_fb, &r_overlay); /* shift r_overlay to the same origin as compose_out */ - r_overlay.left += dev->compose_out.left - dev->overlay_out_left; - r_overlay.top += dev->compose_out.top - dev->overlay_out_top; + r_overlay.left += out_dev->compose_out.left - out_dev->overlay_out_left; + r_overlay.top += out_dev->compose_out.top - out_dev->overlay_out_top; v4l2_rect_intersect(&dev->loop_vid_overlay, &r_overlay, &dev->loop_vid_copy); dev->loop_fb_copy = dev->loop_vid_overlay; /* shift dev->loop_fb_copy back again to the fb origin */ - dev->loop_fb_copy.left -= dev->compose_out.left - dev->overlay_out_left; - dev->loop_fb_copy.top -= dev->compose_out.top - dev->overlay_out_top; + dev->loop_fb_copy.left -= out_dev->compose_out.left - out_dev->overlay_out_left; + dev->loop_fb_copy.top -= out_dev->compose_out.top - out_dev->overlay_out_top; dev->loop_vid_overlay_cap = dev->loop_vid_overlay; v4l2_rect_scale(&dev->loop_vid_overlay_cap, &dev->crop_cap, &dev->compose_cap); @@ -213,24 +213,25 @@ static void *plane_vaddr(struct tpg_data *tpg, struct vivid_buffer *buf, return vbuf; } -static noinline_for_stack int vivid_copy_buffer(struct vivid_dev *dev, unsigned p, - u8 *vcapbuf, struct vivid_buffer *vid_cap_buf) +static noinline_for_stack int vivid_copy_buffer(struct vivid_dev *dev, + struct vivid_dev *out_dev, unsigned p, + u8 *vcapbuf, struct vivid_buffer *vid_cap_buf) { bool blank = dev->must_blank[vid_cap_buf->vb.vb2_buf.index]; struct tpg_data *tpg = &dev->tpg; struct vivid_buffer *vid_out_buf = NULL; - unsigned vdiv = dev->fmt_out->vdownsampling[p]; + unsigned vdiv = out_dev->fmt_out->vdownsampling[p]; unsigned twopixsize = tpg_g_twopixelsize(tpg, p); unsigned img_width = tpg_hdiv(tpg, p, dev->compose_cap.width); unsigned img_height = dev->compose_cap.height; unsigned stride_cap = tpg->bytesperline[p]; - unsigned stride_out = dev->bytesperline_out[p]; + unsigned stride_out = out_dev->bytesperline_out[p]; unsigned stride_osd = dev->display_byte_stride; unsigned hmax = (img_height * tpg->perc_fill) / 100; u8 *voutbuf; u8 *vosdbuf = NULL; unsigned y; - bool blend = dev->fbuf_out_flags; + bool blend = out_dev->fbuf_out_flags; /* Coarse scaling with Bresenham */ unsigned vid_out_int_part; unsigned vid_out_fract_part; @@ -247,8 +248,8 @@ static noinline_for_stack int vivid_copy_buffer(struct vivid_dev *dev, unsigned vid_out_int_part = dev->loop_vid_out.height / dev->loop_vid_cap.height; vid_out_fract_part = dev->loop_vid_out.height % dev->loop_vid_cap.height; - if (!list_empty(&dev->vid_out_active)) - vid_out_buf = list_entry(dev->vid_out_active.next, + if (!list_empty(&out_dev->vid_out_active)) + vid_out_buf = list_entry(out_dev->vid_out_active.next, struct vivid_buffer, list); if (vid_out_buf == NULL) return -ENODATA; @@ -256,8 +257,8 @@ static noinline_for_stack int vivid_copy_buffer(struct vivid_dev *dev, unsigned vid_cap_buf->vb.field = vid_out_buf->vb.field; voutbuf = plane_vaddr(tpg, vid_out_buf, p, - dev->bytesperline_out, dev->fmt_out_rect.height); - if (p < dev->fmt_out->buffers) + out_dev->bytesperline_out, out_dev->fmt_out_rect.height); + if (p < out_dev->fmt_out->buffers) voutbuf += vid_out_buf->vb.vb2_buf.planes[p].data_offset; voutbuf += tpg_hdiv(tpg, p, dev->loop_vid_out.left) + (dev->loop_vid_out.top / vdiv) * stride_out; @@ -274,7 +275,7 @@ static noinline_for_stack int vivid_copy_buffer(struct vivid_dev *dev, unsigned return 0; } - if (dev->overlay_out_enabled && + if (out_dev->overlay_out_enabled && dev->loop_vid_overlay.width && dev->loop_vid_overlay.height) { vosdbuf = dev->video_vbase; vosdbuf += (dev->loop_fb_copy.left * twopixsize) / 2 + @@ -385,6 +386,7 @@ update_vid_out_y: static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf) { + struct vivid_dev *out_dev = NULL; struct tpg_data *tpg = &dev->tpg; unsigned factor = V4L2_FIELD_HAS_T_OR_B(dev->field_cap) ? 2 : 1; unsigned line_height = 16 / factor; @@ -396,14 +398,6 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf) unsigned ms; char str[100]; s32 gain; - bool is_loop = false; - - if (dev->loop_video && dev->can_loop_video && - ((vivid_is_svid_cap(dev) && - !VIVID_INVALID_SIGNAL(dev->std_signal_mode[dev->input])) || - (vivid_is_hdmi_cap(dev) && - !VIVID_INVALID_SIGNAL(dev->dv_timings_signal_mode[dev->input])))) - is_loop = true; buf->vb.sequence = dev->vid_cap_seq_count; v4l2_ctrl_s_ctrl(dev->ro_int32, buf->vb.sequence & 0xff); @@ -428,7 +422,34 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf) dev->field_cap == V4L2_FIELD_ALTERNATE); tpg_s_perc_fill_blank(tpg, dev->must_blank[buf->vb.vb2_buf.index]); - vivid_precalc_copy_rects(dev); + if (vivid_vid_can_loop(dev) && + ((vivid_is_svid_cap(dev) && + !VIVID_INVALID_SIGNAL(dev->std_signal_mode[dev->input])) || + (vivid_is_hdmi_cap(dev) && + !VIVID_INVALID_SIGNAL(dev->dv_timings_signal_mode[dev->input])))) { + out_dev = vivid_input_is_connected_to(dev); + /* + * If the vivid instance of the output device is different + * from the vivid instance of this input device, then we + * must take care to properly serialize the output device to + * prevent that the buffer we are copying from is being freed. + * + * If the output device is part of the same instance, then the + * lock is already taken and there is no need to take the mutex. + * + * The problem with taking the mutex is that you can get + * deadlocked if instance A locks instance B and vice versa. + * It is not really worth trying to be very smart about this, + * so just try to take the lock, and if you can't, then just + * set out_dev to NULL and you will end up with a single frame + * of Noise (the default test pattern in this case). + */ + if (out_dev && dev != out_dev && !mutex_trylock(&out_dev->mutex)) + out_dev = NULL; + } + + if (out_dev) + vivid_precalc_copy_rects(dev, out_dev); for (p = 0; p < tpg_g_planes(tpg); p++) { void *vbuf = plane_vaddr(tpg, buf, p, @@ -445,10 +466,13 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf) vbuf += dev->fmt_cap->data_offset[p]; } tpg_calc_text_basep(tpg, basep, p, vbuf); - if (!is_loop || vivid_copy_buffer(dev, p, vbuf, buf)) + if (!out_dev || vivid_copy_buffer(dev, out_dev, p, vbuf, buf)) tpg_fill_plane_buffer(tpg, vivid_get_std_cap(dev), p, vbuf); } + if (out_dev && dev != out_dev) + mutex_unlock(&out_dev->mutex); + dev->must_blank[buf->vb.vb2_buf.index] = false; /* Updates stream time, only update at the start of a new frame. */ diff --git a/drivers/media/test-drivers/vivid/vivid-sdr-cap.c b/drivers/media/test-drivers/vivid/vivid-sdr-cap.c index a81f26b76988..38cda33dffb2 100644 --- a/drivers/media/test-drivers/vivid/vivid-sdr-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-sdr-cap.c @@ -219,8 +219,13 @@ static int sdr_cap_queue_setup(struct vb2_queue *vq, unsigned sizes[], struct device *alloc_devs[]) { /* 2 = max 16-bit sample returned */ - sizes[0] = SDR_CAP_SAMPLES_PER_BUF * 2; + u32 size = SDR_CAP_SAMPLES_PER_BUF * 2; + + if (*nplanes) + return sizes[0] < size ? -EINVAL : 0; + *nplanes = 1; + sizes[0] = size; return 0; } diff --git a/drivers/media/test-drivers/vivid/vivid-touch-cap.c b/drivers/media/test-drivers/vivid/vivid-touch-cap.c index 3888c21b4d0c..3600b084bca5 100644 --- a/drivers/media/test-drivers/vivid/vivid-touch-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-touch-cap.c @@ -17,13 +17,13 @@ static int touch_cap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int size = f->sizeimage; if (*nplanes) { - if (sizes[0] < size) + if (*nplanes != 1) return -EINVAL; - } else { - sizes[0] = size; + return sizes[0] < size ? -EINVAL : 0; } *nplanes = 1; + sizes[0] = size; return 0; } diff --git a/drivers/media/test-drivers/vivid/vivid-vbi-cap.c b/drivers/media/test-drivers/vivid/vivid-vbi-cap.c index 3840b3a664ac..99138f63585c 100644 --- a/drivers/media/test-drivers/vivid/vivid-vbi-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-vbi-cap.c @@ -14,6 +14,7 @@ #include "vivid-kthread-cap.h" #include "vivid-vbi-cap.h" #include "vivid-vbi-gen.h" +#include "vivid-vid-common.h" static void vivid_sliced_vbi_cap_fill(struct vivid_dev *dev, unsigned seqnr) { @@ -23,7 +24,7 @@ static void vivid_sliced_vbi_cap_fill(struct vivid_dev *dev, unsigned seqnr) vivid_vbi_gen_sliced(vbi_gen, is_60hz, seqnr); if (!is_60hz) { - if (dev->loop_video) { + if (vivid_vid_can_loop(dev)) { if (dev->vbi_out_have_wss) { vbi_gen->data[12].data[0] = dev->vbi_out_wss[0]; vbi_gen->data[12].data[1] = dev->vbi_out_wss[1]; @@ -47,7 +48,7 @@ static void vivid_sliced_vbi_cap_fill(struct vivid_dev *dev, unsigned seqnr) break; } } - } else if (dev->loop_video && is_60hz) { + } else if (vivid_vid_can_loop(dev) && is_60hz) { if (dev->vbi_out_have_cc[0]) { vbi_gen->data[0].data[0] = dev->vbi_out_cc[0][0]; vbi_gen->data[0].data[1] = dev->vbi_out_cc[0][1]; @@ -132,6 +133,8 @@ static int vbi_cap_queue_setup(struct vb2_queue *vq, if (!vivid_is_sdtv_cap(dev)) return -EINVAL; + if (*nplanes) + return sizes[0] < size ? -EINVAL : 0; sizes[0] = size; *nplanes = 1; diff --git a/drivers/media/test-drivers/vivid/vivid-vbi-out.c b/drivers/media/test-drivers/vivid/vivid-vbi-out.c index 434a10676417..871a56d93425 100644 --- a/drivers/media/test-drivers/vivid/vivid-vbi-out.c +++ b/drivers/media/test-drivers/vivid/vivid-vbi-out.c @@ -28,6 +28,8 @@ static int vbi_out_queue_setup(struct vb2_queue *vq, if (!vivid_is_svid_out(dev)) return -EINVAL; + if (*nplanes) + return sizes[0] < size ? -EINVAL : 0; sizes[0] = size; *nplanes = 1; diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c index 2804975fe278..69620e0a35a0 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c @@ -106,8 +106,9 @@ static int vid_cap_queue_setup(struct vb2_queue *vq, if (*nplanes != buffers) return -EINVAL; for (p = 0; p < buffers; p++) { - if (sizes[p] < tpg_g_line_width(&dev->tpg, p) * h + - dev->fmt_cap->data_offset[p]) + if (sizes[p] < tpg_g_line_width(&dev->tpg, p) * h / + dev->fmt_cap->vdownsampling[p] + + dev->fmt_cap->data_offset[p]) return -EINVAL; } } else { @@ -210,9 +211,6 @@ static int vid_cap_start_streaming(struct vb2_queue *vq, unsigned count) unsigned i; int err; - if (vb2_is_streaming(&dev->vb_vid_out_q)) - dev->can_loop_video = vivid_vid_can_loop(dev); - dev->vid_cap_seq_count = 0; dprintk(dev, 1, "%s\n", __func__); for (i = 0; i < VIDEO_MAX_FRAME; i++) @@ -242,7 +240,6 @@ static void vid_cap_stop_streaming(struct vb2_queue *vq) dprintk(dev, 1, "%s\n", __func__); vivid_stop_generating_vid_cap(dev, &dev->vid_cap_streaming); - dev->can_loop_video = false; } static void vid_cap_buf_request_complete(struct vb2_buffer *vb) @@ -273,7 +270,7 @@ void vivid_update_quality(struct vivid_dev *dev) { unsigned freq_modulus; - if (dev->loop_video && (vivid_is_svid_cap(dev) || vivid_is_hdmi_cap(dev))) { + if (dev->input_is_connected_to_output[dev->input]) { /* * The 'noise' will only be replaced by the actual video * if the output video matches the input video settings. @@ -487,35 +484,35 @@ static enum v4l2_field vivid_field_cap(struct vivid_dev *dev, enum v4l2_field fi static unsigned vivid_colorspace_cap(struct vivid_dev *dev) { - if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev)) + if (!vivid_input_is_connected_to(dev)) return tpg_g_colorspace(&dev->tpg); return dev->colorspace_out; } static unsigned vivid_xfer_func_cap(struct vivid_dev *dev) { - if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev)) + if (!vivid_input_is_connected_to(dev)) return tpg_g_xfer_func(&dev->tpg); return dev->xfer_func_out; } static unsigned vivid_ycbcr_enc_cap(struct vivid_dev *dev) { - if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev)) + if (!vivid_input_is_connected_to(dev)) return tpg_g_ycbcr_enc(&dev->tpg); return dev->ycbcr_enc_out; } static unsigned int vivid_hsv_enc_cap(struct vivid_dev *dev) { - if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev)) + if (!vivid_input_is_connected_to(dev)) return tpg_g_hsv_enc(&dev->tpg); return dev->hsv_enc_out; } static unsigned vivid_quantization_cap(struct vivid_dev *dev) { - if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev)) + if (!vivid_input_is_connected_to(dev)) return tpg_g_quantization(&dev->tpg); return dev->quantization_out; } @@ -1072,13 +1069,13 @@ int vidioc_enum_input(struct file *file, void *priv, inp->type = V4L2_INPUT_TYPE_CAMERA; switch (dev->input_type[inp->index]) { case WEBCAM: - snprintf(inp->name, sizeof(inp->name), "Webcam %u", - dev->input_name_counter[inp->index]); + snprintf(inp->name, sizeof(inp->name), "Webcam %03u-%u", + dev->inst, dev->input_name_counter[inp->index]); inp->capabilities = 0; break; case TV: - snprintf(inp->name, sizeof(inp->name), "TV %u", - dev->input_name_counter[inp->index]); + snprintf(inp->name, sizeof(inp->name), "TV %03u-%u", + dev->inst, dev->input_name_counter[inp->index]); inp->type = V4L2_INPUT_TYPE_TUNER; inp->std = V4L2_STD_ALL; if (dev->has_audio_inputs) @@ -1086,16 +1083,16 @@ int vidioc_enum_input(struct file *file, void *priv, inp->capabilities = V4L2_IN_CAP_STD; break; case SVID: - snprintf(inp->name, sizeof(inp->name), "S-Video %u", - dev->input_name_counter[inp->index]); + snprintf(inp->name, sizeof(inp->name), "S-Video %03u-%u", + dev->inst, dev->input_name_counter[inp->index]); inp->std = V4L2_STD_ALL; if (dev->has_audio_inputs) inp->audioset = (1 << ARRAY_SIZE(vivid_audio_inputs)) - 1; inp->capabilities = V4L2_IN_CAP_STD; break; case HDMI: - snprintf(inp->name, sizeof(inp->name), "HDMI %u", - dev->input_name_counter[inp->index]); + snprintf(inp->name, sizeof(inp->name), "HDMI %03u-%u", + dev->inst, dev->input_name_counter[inp->index]); inp->capabilities = V4L2_IN_CAP_DV_TIMINGS; if (dev->edid_blocks == 0 || dev->dv_timings_signal_mode[dev->input] == NO_SIGNAL) @@ -1537,13 +1534,65 @@ int vidioc_query_dv_timings(struct file *file, void *_fh, return 0; } +void vivid_update_outputs(struct vivid_dev *dev) +{ + u32 edid_present = 0; + + if (!dev || !dev->num_outputs) + return; + for (unsigned int i = 0, j = 0; i < dev->num_outputs; i++) { + if (dev->output_type[i] != HDMI) + continue; + + struct vivid_dev *dev_rx = dev->output_to_input_instance[i]; + + if (dev_rx && dev_rx->edid_blocks) + edid_present |= 1 << j; + j++; + } + v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present, edid_present); + v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug, edid_present); + v4l2_ctrl_s_ctrl(dev->ctrl_tx_rxsense, edid_present); +} + +void vivid_update_connected_outputs(struct vivid_dev *dev) +{ + u16 phys_addr = cec_get_edid_phys_addr(dev->edid, dev->edid_blocks * 128, NULL); + + for (unsigned int i = 0, j = 0; i < dev->num_inputs; i++) { + unsigned int menu_idx = + dev->input_is_connected_to_output[i]; + + if (dev->input_type[i] != HDMI) + continue; + j++; + if (menu_idx < FIXED_MENU_ITEMS) + continue; + + struct vivid_dev *dev_tx = vivid_ctrl_hdmi_to_output_instance[menu_idx]; + unsigned int output = vivid_ctrl_hdmi_to_output_index[menu_idx]; + + if (!dev_tx) + continue; + + unsigned int hdmi_output = dev_tx->output_to_iface_index[output]; + + vivid_update_outputs(dev_tx); + if (dev->edid_blocks) { + cec_s_phys_addr(dev_tx->cec_tx_adap[hdmi_output], + v4l2_phys_addr_for_input(phys_addr, j), + false); + } else { + cec_phys_addr_invalidate(dev_tx->cec_tx_adap[hdmi_output]); + } + } +} + int vidioc_s_edid(struct file *file, void *_fh, struct v4l2_edid *edid) { struct vivid_dev *dev = video_drvdata(file); u16 phys_addr; - u32 display_present = 0; - unsigned int i, j; int ret; memset(edid->reserved, 0, sizeof(edid->reserved)); @@ -1552,11 +1601,11 @@ int vidioc_s_edid(struct file *file, void *_fh, if (dev->input_type[edid->pad] != HDMI || edid->start_block) return -EINVAL; if (edid->blocks == 0) { + if (vb2_is_busy(&dev->vb_vid_cap_q)) + return -EBUSY; dev->edid_blocks = 0; - v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present, 0); - v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug, 0); - phys_addr = CEC_PHYS_ADDR_INVALID; - goto set_phys_addr; + vivid_update_connected_outputs(dev); + return 0; } if (edid->blocks > dev->edid_max_blocks) { edid->blocks = dev->edid_max_blocks; @@ -1573,24 +1622,7 @@ int vidioc_s_edid(struct file *file, void *_fh, dev->edid_blocks = edid->blocks; memcpy(dev->edid, edid->edid, edid->blocks * 128); - for (i = 0, j = 0; i < dev->num_outputs; i++) - if (dev->output_type[i] == HDMI) - display_present |= - dev->display_present[i] << j++; - - v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present, display_present); - v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug, display_present); - -set_phys_addr: - /* TODO: a proper hotplug detect cycle should be emulated here */ - cec_s_phys_addr(dev->cec_rx_adap, phys_addr, false); - - for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++) - cec_s_phys_addr(dev->cec_tx_adap[i], - dev->display_present[i] ? - v4l2_phys_addr_for_input(phys_addr, i + 1) : - CEC_PHYS_ADDR_INVALID, - false); + vivid_update_connected_outputs(dev); return 0; } diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.h b/drivers/media/test-drivers/vivid/vivid-vid-cap.h index 949768652d38..7a8daf0af2ca 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-cap.h +++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.h @@ -10,6 +10,8 @@ void vivid_update_quality(struct vivid_dev *dev); void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls); +void vivid_update_outputs(struct vivid_dev *dev); +void vivid_update_connected_outputs(struct vivid_dev *dev); enum tpg_video_aspect vivid_get_video_aspect(const struct vivid_dev *dev); extern const v4l2_std_id vivid_standard[]; diff --git a/drivers/media/test-drivers/vivid/vivid-vid-common.c b/drivers/media/test-drivers/vivid/vivid-vid-common.c index 38d788b5cf19..df7678db67fb 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-common.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-common.c @@ -769,14 +769,55 @@ const struct vivid_fmt *vivid_get_format(struct vivid_dev *dev, u32 pixelformat) return NULL; } +struct vivid_dev *vivid_output_is_connected_to(struct vivid_dev *dev) +{ + struct vivid_dev *input_inst = dev->output_to_input_instance[dev->output]; + + if (!input_inst) + return NULL; + if (input_inst->input != dev->output_to_input_index[dev->output]) + return NULL; + return input_inst; +} + +struct vivid_dev *vivid_input_is_connected_to(struct vivid_dev *dev) +{ + s32 connected_output = dev->input_is_connected_to_output[dev->input]; + + if (connected_output < FIXED_MENU_ITEMS) + return NULL; + struct vivid_dev *output_inst = NULL; + + if (vivid_is_hdmi_cap(dev)) { + output_inst = vivid_ctrl_hdmi_to_output_instance[connected_output]; + if (vivid_ctrl_hdmi_to_output_index[connected_output] != output_inst->output) + return NULL; + return output_inst; + } else if (vivid_is_svid_cap(dev)) { + output_inst = vivid_ctrl_svid_to_output_instance[connected_output]; + if (vivid_ctrl_svid_to_output_index[connected_output] != output_inst->output) + return NULL; + return output_inst; + } else { + return NULL; + } + return output_inst; +} + bool vivid_vid_can_loop(struct vivid_dev *dev) { - if (dev->src_rect.width != dev->sink_rect.width || - dev->src_rect.height != dev->sink_rect.height) + struct vivid_dev *output_inst = vivid_input_is_connected_to(dev); + + if (!output_inst) return false; - if (dev->fmt_cap->fourcc != dev->fmt_out->fourcc) + if (!vb2_is_streaming(&output_inst->vb_vid_out_q)) return false; - if (dev->field_cap != dev->field_out) + if (dev->src_rect.width != output_inst->sink_rect.width || + dev->src_rect.height != output_inst->sink_rect.height) + return false; + if (dev->fmt_cap->fourcc != output_inst->fmt_out->fourcc) + return false; + if (dev->field_cap != output_inst->field_out) return false; /* * While this can be supported, it is just too much work @@ -785,34 +826,34 @@ bool vivid_vid_can_loop(struct vivid_dev *dev) if (dev->field_cap == V4L2_FIELD_SEQ_TB || dev->field_cap == V4L2_FIELD_SEQ_BT) return false; - if (vivid_is_svid_cap(dev) && vivid_is_svid_out(dev)) { - if (!(dev->std_cap[dev->input] & V4L2_STD_525_60) != - !(dev->std_out & V4L2_STD_525_60)) - return false; + if (vivid_is_hdmi_cap(dev)) return true; - } - if (vivid_is_hdmi_cap(dev) && vivid_is_hdmi_out(dev)) - return true; - return false; + if (!(dev->std_cap[dev->input] & V4L2_STD_525_60) != + !(output_inst->std_out & V4L2_STD_525_60)) + return false; + return true; } -void vivid_send_source_change(struct vivid_dev *dev, unsigned type) +void vivid_send_input_source_change(struct vivid_dev *dev, unsigned int input_index) { struct v4l2_event ev = { .type = V4L2_EVENT_SOURCE_CHANGE, .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, }; - unsigned i; + ev.id = input_index; - for (i = 0; i < dev->num_inputs; i++) { - ev.id = i; - if (dev->input_type[i] == type) { - if (video_is_registered(&dev->vid_cap_dev) && dev->has_vid_cap) - v4l2_event_queue(&dev->vid_cap_dev, &ev); - if (video_is_registered(&dev->vbi_cap_dev) && dev->has_vbi_cap) - v4l2_event_queue(&dev->vbi_cap_dev, &ev); - } - } + if (video_is_registered(&dev->vid_cap_dev) && dev->has_vid_cap) + v4l2_event_queue(&dev->vid_cap_dev, &ev); + if (dev->input_type[input_index] == TV || dev->input_type[input_index] == SVID) + if (video_is_registered(&dev->vbi_cap_dev) && dev->has_vbi_cap) + v4l2_event_queue(&dev->vbi_cap_dev, &ev); +} + +void vivid_send_source_change(struct vivid_dev *dev, unsigned int type) +{ + for (int i = 0; i < dev->num_inputs; i++) + if (dev->input_type[i] == type) + vivid_send_input_source_change(dev, i); } /* @@ -1036,8 +1077,10 @@ int vidioc_g_edid(struct file *file, void *_fh, struct v4l2_edid *edid) { struct vivid_dev *dev = video_drvdata(file); + struct vivid_dev *dev_rx = dev; struct video_device *vdev = video_devdata(file); struct cec_adapter *adap; + unsigned int loc; memset(edid->reserved, 0, sizeof(edid->reserved)); if (vdev->vfl_dir == VFL_DIR_RX) { @@ -1047,29 +1090,48 @@ int vidioc_g_edid(struct file *file, void *_fh, return -EINVAL; adap = dev->cec_rx_adap; } else { - unsigned int bus_idx; - if (edid->pad >= dev->num_outputs) return -EINVAL; if (dev->output_type[edid->pad] != HDMI) return -EINVAL; - if (!dev->display_present[edid->pad]) + dev_rx = dev->output_to_input_instance[edid->pad]; + if (!dev_rx) return -ENODATA; - bus_idx = dev->cec_output2bus_map[edid->pad]; - adap = dev->cec_tx_adap[bus_idx]; + + unsigned int hdmi_output = dev->output_to_iface_index[edid->pad]; + + adap = dev->cec_tx_adap[hdmi_output]; } if (edid->start_block == 0 && edid->blocks == 0) { - edid->blocks = dev->edid_blocks; + edid->blocks = dev_rx->edid_blocks; return 0; } - if (dev->edid_blocks == 0) + if (dev_rx->edid_blocks == 0) return -ENODATA; - if (edid->start_block >= dev->edid_blocks) + if (edid->start_block >= dev_rx->edid_blocks) return -EINVAL; - if (edid->blocks > dev->edid_blocks - edid->start_block) - edid->blocks = dev->edid_blocks - edid->start_block; - if (adap) - v4l2_set_edid_phys_addr(dev->edid, dev->edid_blocks * 128, adap->phys_addr); - memcpy(edid->edid, dev->edid + edid->start_block * 128, edid->blocks * 128); + if (edid->blocks > dev_rx->edid_blocks - edid->start_block) + edid->blocks = dev_rx->edid_blocks - edid->start_block; + + memcpy(edid->edid, dev_rx->edid + edid->start_block * 128, edid->blocks * 128); + + loc = cec_get_edid_spa_location(dev_rx->edid, + dev_rx->edid_blocks * 128); + if (vdev->vfl_dir == VFL_DIR_TX && adap && loc && + loc >= edid->start_block * 128 && + loc < (edid->start_block + edid->blocks) * 128) { + unsigned int i; + u8 sum = 0; + + loc -= edid->start_block * 128; + edid->edid[loc] = adap->phys_addr >> 8; + edid->edid[loc + 1] = adap->phys_addr & 0xff; + loc &= ~0x7f; + + /* update the checksum */ + for (i = loc; i < loc + 127; i++) + sum += edid->edid[i]; + edid->edid[i] = 256 - sum; + } return 0; } diff --git a/drivers/media/test-drivers/vivid/vivid-vid-common.h b/drivers/media/test-drivers/vivid/vivid-vid-common.h index d908d9725283..c49ac85abaed 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-common.h +++ b/drivers/media/test-drivers/vivid/vivid-vid-common.h @@ -22,8 +22,11 @@ extern const struct v4l2_dv_timings_cap vivid_dv_timings_cap; const struct vivid_fmt *vivid_get_format(struct vivid_dev *dev, u32 pixelformat); +struct vivid_dev *vivid_input_is_connected_to(struct vivid_dev *dev); +struct vivid_dev *vivid_output_is_connected_to(struct vivid_dev *dev); bool vivid_vid_can_loop(struct vivid_dev *dev); -void vivid_send_source_change(struct vivid_dev *dev, unsigned type); +void vivid_send_source_change(struct vivid_dev *dev, unsigned int type); +void vivid_send_input_source_change(struct vivid_dev *dev, unsigned int input_index); int vivid_vid_adjust_sel(unsigned flags, struct v4l2_rect *r); diff --git a/drivers/media/test-drivers/vivid/vivid-vid-out.c b/drivers/media/test-drivers/vivid/vivid-vid-out.c index 1653b2988f7e..60327f3612af 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-out.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-out.c @@ -63,14 +63,16 @@ static int vid_out_queue_setup(struct vb2_queue *vq, if (sizes[0] < size) return -EINVAL; for (p = 1; p < planes; p++) { - if (sizes[p] < dev->bytesperline_out[p] * h + - vfmt->data_offset[p]) + if (sizes[p] < dev->bytesperline_out[p] * h / + vfmt->vdownsampling[p] + + vfmt->data_offset[p]) return -EINVAL; } } else { for (p = 0; p < planes; p++) - sizes[p] = p ? dev->bytesperline_out[p] * h + - vfmt->data_offset[p] : size; + sizes[p] = p ? dev->bytesperline_out[p] * h / + vfmt->vdownsampling[p] + + vfmt->data_offset[p] : size; } *nplanes = planes; @@ -124,7 +126,7 @@ static int vid_out_buf_prepare(struct vb2_buffer *vb) for (p = 0; p < planes; p++) { if (p) - size = dev->bytesperline_out[p] * h; + size = dev->bytesperline_out[p] * h / vfmt->vdownsampling[p]; size += vb->planes[p].data_offset; if (vb2_get_plane_payload(vb, p) < size) { @@ -155,9 +157,6 @@ static int vid_out_start_streaming(struct vb2_queue *vq, unsigned count) struct vivid_dev *dev = vb2_get_drv_priv(vq); int err; - if (vb2_is_streaming(&dev->vb_vid_cap_q)) - dev->can_loop_video = vivid_vid_can_loop(dev); - dev->vid_out_seq_count = 0; dprintk(dev, 1, "%s\n", __func__); if (dev->start_streaming_error) { @@ -185,7 +184,6 @@ static void vid_out_stop_streaming(struct vb2_queue *vq) dprintk(dev, 1, "%s\n", __func__); vivid_stop_generating_vid_out(dev, &dev->vid_out_streaming); - dev->can_loop_video = false; } static void vid_out_buf_request_complete(struct vb2_buffer *vb) @@ -331,8 +329,8 @@ int vivid_g_fmt_vid_out(struct file *file, void *priv, for (p = 0; p < mp->num_planes; p++) { mp->plane_fmt[p].bytesperline = dev->bytesperline_out[p]; mp->plane_fmt[p].sizeimage = - mp->plane_fmt[p].bytesperline * mp->height + - fmt->data_offset[p]; + mp->plane_fmt[p].bytesperline * mp->height / + fmt->vdownsampling[p] + fmt->data_offset[p]; } for (p = fmt->buffers; p < fmt->planes; p++) { unsigned stride = dev->bytesperline_out[p]; @@ -562,9 +560,11 @@ set_colorspace: dev->xfer_func_out = mp->xfer_func; dev->ycbcr_enc_out = mp->ycbcr_enc; dev->quantization_out = mp->quantization; - if (dev->loop_video) { - vivid_send_source_change(dev, SVID); - vivid_send_source_change(dev, HDMI); + struct vivid_dev *in_dev = vivid_output_is_connected_to(dev); + + if (in_dev) { + vivid_send_source_change(in_dev, SVID); + vivid_send_source_change(in_dev, HDMI); } return 0; } @@ -964,16 +964,16 @@ int vidioc_enum_output(struct file *file, void *priv, out->type = V4L2_OUTPUT_TYPE_ANALOG; switch (dev->output_type[out->index]) { case SVID: - snprintf(out->name, sizeof(out->name), "S-Video %u", - dev->output_name_counter[out->index]); + snprintf(out->name, sizeof(out->name), "S-Video %03u-%u", + dev->inst, dev->output_name_counter[out->index]); out->std = V4L2_STD_ALL; if (dev->has_audio_outputs) out->audioset = (1 << ARRAY_SIZE(vivid_audio_outputs)) - 1; out->capabilities = V4L2_OUT_CAP_STD; break; case HDMI: - snprintf(out->name, sizeof(out->name), "HDMI %u", - dev->output_name_counter[out->index]); + snprintf(out->name, sizeof(out->name), "HDMI %03u-%u", + dev->inst, dev->output_name_counter[out->index]); out->capabilities = V4L2_OUT_CAP_DV_TIMINGS; break; } @@ -1014,11 +1014,6 @@ int vidioc_s_output(struct file *file, void *priv, unsigned o) dev->meta_out_dev.tvnorms = dev->vid_out_dev.tvnorms; vivid_update_format_out(dev); - v4l2_ctrl_activate(dev->ctrl_display_present, vivid_is_hdmi_out(dev)); - if (vivid_is_hdmi_out(dev)) - v4l2_ctrl_s_ctrl(dev->ctrl_display_present, - dev->display_present[dev->output]); - return 0; } diff --git a/drivers/media/tuners/tda9887.c b/drivers/media/tuners/tda9887.c index dca2fc776e44..b2f7054c1832 100644 --- a/drivers/media/tuners/tda9887.c +++ b/drivers/media/tuners/tda9887.c @@ -707,4 +707,5 @@ struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe, } EXPORT_SYMBOL_GPL(tda9887_attach); +MODULE_DESCRIPTION("NXP TDA9885/6/7 analog IF demodulator driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/tuners/tuner-types.c b/drivers/media/tuners/tuner-types.c index ff5a6c0acdd4..c26f1296e18f 100644 --- a/drivers/media/tuners/tuner-types.c +++ b/drivers/media/tuners/tuner-types.c @@ -1434,6 +1434,22 @@ static struct tuner_params tuner_sony_btf_pg463z_params[] = { }, }; +/* ------------- TUNER_TENA_TNF_931D_DFDR1 - NXP TDA6509A ------------- */ + +static struct tuner_range tuner_tena_tnf_931d_dfdr1_ranges[] = { + { 16 * 161.15 /*MHz*/, 0x8e, 0x01, }, + { 16 * 463.15 /*MHz*/, 0x8e, 0x02, }, + { 16 * 999.99 , 0x8e, 0x08, }, +}; + +static struct tuner_params tuner_tena_tnf_931d_dfdr1_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_tena_tnf_931d_dfdr1_ranges, + .count = ARRAY_SIZE(tuner_tena_tnf_931d_dfdr1_ranges), + }, +}; + /* --------------------------------------------------------------------- */ struct tunertype tuners[] = { @@ -1946,6 +1962,11 @@ struct tunertype tuners[] = { .name = "Silicon Labs Si2157 tuner", /* see si2157.c for details */ }, + [TUNER_TENA_TNF_931D_DFDR1] = { + .name = "Tena TNF931D-DFDR1", + .params = tuner_tena_tnf_931d_dfdr1_params, + .count = ARRAY_SIZE(tuner_tena_tnf_931d_dfdr1_params), + } }; EXPORT_SYMBOL(tuners); diff --git a/drivers/media/tuners/xc2028.c b/drivers/media/tuners/xc2028.c index 5a967edceca9..352b8a3679b7 100644 --- a/drivers/media/tuners/xc2028.c +++ b/drivers/media/tuners/xc2028.c @@ -1361,9 +1361,16 @@ static void load_firmware_cb(const struct firmware *fw, void *context) { struct dvb_frontend *fe = context; - struct xc2028_data *priv = fe->tuner_priv; + struct xc2028_data *priv; int rc; + if (!fe) { + pr_warn("xc2028: No frontend in %s\n", __func__); + return; + } + + priv = fe->tuner_priv; + tuner_dbg("request_firmware_nowait(): %s\n", fw ? "OK" : "error"); if (!fw) { tuner_err("Could not load firmware %s.\n", priv->fname); diff --git a/drivers/media/usb/b2c2/flexcop-usb.c b/drivers/media/usb/b2c2/flexcop-usb.c index 90f1aea99dac..8033622543f2 100644 --- a/drivers/media/usb/b2c2/flexcop-usb.c +++ b/drivers/media/usb/b2c2/flexcop-usb.c @@ -179,7 +179,7 @@ static int flexcop_usb_memory_req(struct flexcop_usb *fc_usb, flexcop_usb_request_t req, flexcop_usb_mem_page_t page_start, u32 addr, int extended, u8 *buf, u32 len) { - int i, ret = 0; + int ret = 0; u16 wMax; u32 pagechunk = 0; @@ -196,7 +196,7 @@ static int flexcop_usb_memory_req(struct flexcop_usb *fc_usb, default: return -EINVAL; } - for (i = 0; i < len;) { + while (len) { pagechunk = min(wMax, bytes_left_to_read_on_page(addr, len)); deb_info("%x\n", (addr & V8_MEMORY_PAGE_MASK) | @@ -206,11 +206,12 @@ static int flexcop_usb_memory_req(struct flexcop_usb *fc_usb, page_start + (addr / V8_MEMORY_PAGE_SIZE), (addr & V8_MEMORY_PAGE_MASK) | (V8_MEMORY_EXTENDED*extended), - &buf[i], pagechunk); + buf, pagechunk); if (ret < 0) return ret; addr += pagechunk; + buf += pagechunk; len -= pagechunk; } return 0; diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c index 343a4433ed24..abb967c8bd35 100644 --- a/drivers/media/usb/cx231xx/cx231xx-417.c +++ b/drivers/media/usb/cx231xx/cx231xx-417.c @@ -1453,7 +1453,7 @@ static void stop_streaming(struct vb2_queue *vq) return_all_buffers(dev, VB2_BUF_STATE_ERROR); } -static struct vb2_ops cx231xx_video_qops = { +static const struct vb2_ops cx231xx_video_qops = { .queue_setup = queue_setup, .buf_queue = buffer_queue, .start_streaming = start_streaming, diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index 8f347bbeeb32..435eb0b32cb1 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -795,7 +795,7 @@ static void stop_streaming(struct vb2_queue *vq) return_all_buffers(dev, VB2_BUF_STATE_ERROR); } -static struct vb2_ops cx231xx_video_qops = { +static const struct vb2_ops cx231xx_video_qops = { .queue_setup = queue_setup, .buf_queue = buffer_queue, .start_streaming = start_streaming, diff --git a/drivers/media/usb/dvb-usb/dibusb-common.c b/drivers/media/usb/dvb-usb/dibusb-common.c index aff60c10cb0b..20f1ef3393a5 100644 --- a/drivers/media/usb/dvb-usb/dibusb-common.c +++ b/drivers/media/usb/dvb-usb/dibusb-common.c @@ -14,6 +14,7 @@ static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "set debugging level (1=info (|-able))." DVB_USB_DEBUG_STATUS); +MODULE_DESCRIPTION("Common methods for dibusb-based receivers"); MODULE_LICENSE("GPL"); #define deb_info(args...) dprintk(debug,0x01,args) diff --git a/drivers/media/usb/dvb-usb/dibusb-mc-common.c b/drivers/media/usb/dvb-usb/dibusb-mc-common.c index b8cde4cded33..36bc7762acf4 100644 --- a/drivers/media/usb/dvb-usb/dibusb-mc-common.c +++ b/drivers/media/usb/dvb-usb/dibusb-mc-common.c @@ -8,6 +8,7 @@ #include "dibusb.h" +MODULE_DESCRIPTION("Common methods for DIB3000MC"); MODULE_LICENSE("GPL"); /* 3000MC/P stuff */ diff --git a/drivers/media/usb/dvb-usb/dvb-usb-init.c b/drivers/media/usb/dvb-usb/dvb-usb-init.c index fbf58012becd..22d83ac18eb7 100644 --- a/drivers/media/usb/dvb-usb/dvb-usb-init.c +++ b/drivers/media/usb/dvb-usb/dvb-usb-init.c @@ -23,11 +23,40 @@ static int dvb_usb_force_pid_filter_usage; module_param_named(force_pid_filter_usage, dvb_usb_force_pid_filter_usage, int, 0444); MODULE_PARM_DESC(force_pid_filter_usage, "force all dvb-usb-devices to use a PID filter, if any (default: 0)."); +static int dvb_usb_check_bulk_endpoint(struct dvb_usb_device *d, u8 endpoint) +{ + if (endpoint) { + int ret; + + ret = usb_pipe_type_check(d->udev, usb_sndbulkpipe(d->udev, endpoint)); + if (ret) + return ret; + ret = usb_pipe_type_check(d->udev, usb_rcvbulkpipe(d->udev, endpoint)); + if (ret) + return ret; + } + return 0; +} + +static void dvb_usb_clear_halt(struct dvb_usb_device *d, u8 endpoint) +{ + if (endpoint) { + usb_clear_halt(d->udev, usb_sndbulkpipe(d->udev, endpoint)); + usb_clear_halt(d->udev, usb_rcvbulkpipe(d->udev, endpoint)); + } +} + static int dvb_usb_adapter_init(struct dvb_usb_device *d, short *adapter_nrs) { struct dvb_usb_adapter *adap; int ret, n, o; + ret = dvb_usb_check_bulk_endpoint(d, d->props.generic_bulk_ctrl_endpoint); + if (ret) + return ret; + ret = dvb_usb_check_bulk_endpoint(d, d->props.generic_bulk_ctrl_endpoint_response); + if (ret) + return ret; for (n = 0; n < d->props.num_adapters; n++) { adap = &d->adapter[n]; adap->dev = d; @@ -103,10 +132,8 @@ static int dvb_usb_adapter_init(struct dvb_usb_device *d, short *adapter_nrs) * when reloading the driver w/o replugging the device * sometimes a timeout occurs, this helps */ - if (d->props.generic_bulk_ctrl_endpoint != 0) { - usb_clear_halt(d->udev, usb_sndbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); - usb_clear_halt(d->udev, usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); - } + dvb_usb_clear_halt(d, d->props.generic_bulk_ctrl_endpoint); + dvb_usb_clear_halt(d, d->props.generic_bulk_ctrl_endpoint_response); return 0; diff --git a/drivers/media/usb/dvb-usb/opera1.c b/drivers/media/usb/dvb-usb/opera1.c index d269f8bb2dee..268f05fc8691 100644 --- a/drivers/media/usb/dvb-usb/opera1.c +++ b/drivers/media/usb/dvb-usb/opera1.c @@ -32,10 +32,6 @@ struct opera1_state { u32 last_key_pressed; }; -struct rc_map_opera_table { - u32 keycode; - u32 event; -}; static int dvb_usb_opera1_debug; module_param_named(debug, dvb_usb_opera1_debug, int, 0644); diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index bae76023cf71..a51cbcf429e1 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -555,6 +555,30 @@ static struct em28xx_reg_seq hauppauge_usb_quadhd_atsc_reg_seq[] = { }; /* + * MyGica USB TV Box + * GPIO_1,0: 00=Composite audio + * 01=Tuner audio + * 10=Mute audio + * 11=FM radio? (if equipped) + * GPIO_2-6: Unused + * GPIO_7: ?? + */ +static const struct em28xx_reg_seq mygica_utv3_composite_audio_gpio[] = { + {EM2820_R08_GPIO_CTRL, 0xfc, 0xff, 0}, + { -1, -1, -1, -1}, +}; + +static const struct em28xx_reg_seq mygica_utv3_tuner_audio_gpio[] = { + {EM2820_R08_GPIO_CTRL, 0xfd, 0xff, 0}, + { -1, -1, -1, -1}, +}; + +static const struct em28xx_reg_seq mygica_utv3_suspend_gpio[] = { + {EM2820_R08_GPIO_CTRL, 0xfe, 0xff, 0}, + { -1, -1, -1, -1}, +}; + +/* * Button definitions */ static const struct em28xx_button std_snapshot_button[] = { @@ -2578,6 +2602,32 @@ const struct em28xx_board em28xx_boards[] = { .tuner_gpio = hauppauge_usb_quadhd_atsc_reg_seq, .leds = hauppauge_usb_quadhd_leds, }, + /* + * eb1a:2860 MyGica UTV3 Analog USB2.0 TV Box + * Empia EM2860, Philips SAA7113, NXP TDA9801T demod, + * Tena TNF931D-DFDR1 tuner (contains NXP TDA6509A), + * ST HCF4052 demux (switches audio to line out), + * no audio over USB + */ + [EM2860_BOARD_MYGICA_UTV3] = { + .name = "MyGica UTV3 Analog USB2.0 TV Box", + .xclk = EM28XX_XCLK_IR_RC5_MODE | EM28XX_XCLK_FREQUENCY_12MHZ, + .tuner_type = TUNER_TENA_TNF_931D_DFDR1, + .ir_codes = RC_MAP_MYGICA_UTV3, + .decoder = EM28XX_SAA711X, + .suspend_gpio = mygica_utv3_suspend_gpio, + .input = { { + .type = EM28XX_VMUX_COMPOSITE, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = mygica_utv3_composite_audio_gpio, + }, { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE2, + .amux = EM28XX_AMUX_VIDEO, + .gpio = mygica_utv3_tuner_audio_gpio, + } }, + }, }; EXPORT_SYMBOL_GPL(em28xx_boards); @@ -2819,6 +2869,7 @@ static const struct em28xx_hash_table em28xx_eeprom_hash[] = { {0x63f653bd, EM2870_BOARD_REDDO_DVB_C_USB_BOX, TUNER_ABSENT}, {0x4e913442, EM2882_BOARD_DIKOM_DK300, TUNER_XC2028}, {0x85dd871e, EM2882_BOARD_ZOLID_HYBRID_TV_STICK, TUNER_XC2028}, + {0x8f597549, EM2860_BOARD_MYGICA_UTV3, TUNER_TENA_TNF_931D_DFDR1}, }; /* I2C devicelist hash table for devices with generic USB IDs */ @@ -2831,6 +2882,7 @@ static const struct em28xx_hash_table em28xx_i2c_hash[] = { {0x4ba50080, EM2861_BOARD_GADMEI_UTV330PLUS, TUNER_TNF_5335MF}, {0x6b800080, EM2874_BOARD_LEADERSHIP_ISDBT, TUNER_ABSENT}, {0x27e10080, EM2882_BOARD_ZOLID_HYBRID_TV_STICK, TUNER_XC2028}, + {0x840d0484, EM2860_BOARD_MYGICA_UTV3, TUNER_TENA_TNF_931D_DFDR1}, }; /* NOTE: introduce a separate hash table for devices with 16 bit eeproms */ diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 61d7bf701d57..29a7f3f19b56 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -416,8 +416,9 @@ int em28xx_audio_analog_set(struct em28xx *dev) int ret, i; u8 xclk; + /* Set GPIOs here for boards without audio */ if (dev->int_audio_type == EM28XX_INT_AUDIO_NONE) - return 0; + return em28xx_gpio_set(dev, INPUT(dev->ctl_input)->gpio); /* * It is assumed that all devices use master volume for output. diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index db18dd814a67..f3449c240d21 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -143,6 +143,7 @@ #define EM28178_BOARD_PCTV_461E_V2 104 #define EM2860_BOARD_MYGICA_IGRABBER 105 #define EM2874_BOARD_HAUPPAUGE_USB_QUADHD 106 +#define EM2860_BOARD_MYGICA_UTV3 107 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 diff --git a/drivers/media/usb/go7007/go7007-driver.c b/drivers/media/usb/go7007/go7007-driver.c index eb03f98b2ef1..468406302cd5 100644 --- a/drivers/media/usb/go7007/go7007-driver.c +++ b/drivers/media/usb/go7007/go7007-driver.c @@ -736,4 +736,5 @@ void go7007_update_board(struct go7007 *go) } EXPORT_SYMBOL(go7007_update_board); +MODULE_DESCRIPTION("WIS GO7007 MPEG encoder support"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/usb/go7007/go7007-i2c.c b/drivers/media/usb/go7007/go7007-i2c.c index 2880370e45c8..f6ce28a4a768 100644 --- a/drivers/media/usb/go7007/go7007-i2c.c +++ b/drivers/media/usb/go7007/go7007-i2c.c @@ -33,7 +33,21 @@ /* There is only one I2C port on the TW2804 that feeds all four GO7007 VIPs * on the Adlink PCI-MPG24, so access is shared between all of them. */ -static DEFINE_MUTEX(adlink_mpg24_i2c_lock); +static DEFINE_MUTEX(adlink_mpg24_i2c_mutex); + +static inline void adlink_mpg24_i2c_lock(struct go7007 *go) +{ + /* Bridge the I2C port on this GO7007 to the shared bus */ + mutex_lock(&adlink_mpg24_i2c_mutex); + go7007_write_addr(go, 0x3c82, 0x0020); +} + +static inline void adlink_mpg24_i2c_unlock(struct go7007 *go) +{ + /* Isolate the I2C port on this GO7007 from the shared bus */ + go7007_write_addr(go, 0x3c82, 0x0000); + mutex_unlock(&adlink_mpg24_i2c_mutex); +} static int go7007_i2c_xfer(struct go7007 *go, u16 addr, int read, u16 command, int flags, u8 *data) @@ -56,11 +70,8 @@ static int go7007_i2c_xfer(struct go7007 *go, u16 addr, int read, mutex_lock(&go->hw_lock); - if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) { - /* Bridge the I2C port on this GO7007 to the shared bus */ - mutex_lock(&adlink_mpg24_i2c_lock); - go7007_write_addr(go, 0x3c82, 0x0020); - } + if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) + adlink_mpg24_i2c_lock(go); /* Wait for I2C adapter to be ready */ for (i = 0; i < 10; ++i) { @@ -116,11 +127,8 @@ static int go7007_i2c_xfer(struct go7007 *go, u16 addr, int read, ret = 0; i2c_done: - if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) { - /* Isolate the I2C port on this GO7007 from the shared bus */ - go7007_write_addr(go, 0x3c82, 0x0000); - mutex_unlock(&adlink_mpg24_i2c_lock); - } + if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) + adlink_mpg24_i2c_unlock(go); mutex_unlock(&go->hw_lock); return ret; } diff --git a/drivers/media/usb/go7007/go7007-usb.c b/drivers/media/usb/go7007/go7007-usb.c index 762c13e49bfa..334cdde81a5c 100644 --- a/drivers/media/usb/go7007/go7007-usb.c +++ b/drivers/media/usb/go7007/go7007-usb.c @@ -1352,4 +1352,5 @@ static struct usb_driver go7007_usb_driver = { }; module_usb_driver(go7007_usb_driver); +MODULE_DESCRIPTION("WIS GO7007 USB support"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/usb/pvrusb2/pvrusb2-debugifc.c b/drivers/media/usb/pvrusb2/pvrusb2-debugifc.c index 84cfb5ce8b8d..81d711269ab5 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-debugifc.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-debugifc.c @@ -9,11 +9,6 @@ #include "pvrusb2-hdw.h" #include "pvrusb2-debug.h" -struct debugifc_mask_item { - const char *name; - unsigned long msk; -}; - static unsigned int debugifc_count_whitespace(const char *buf, unsigned int count) diff --git a/drivers/media/usb/ttusb-dec/ttusb_dec.c b/drivers/media/usb/ttusb-dec/ttusb_dec.c index 79faa2560613..b4575fe89c95 100644 --- a/drivers/media/usb/ttusb-dec/ttusb_dec.c +++ b/drivers/media/usb/ttusb-dec/ttusb_dec.c @@ -19,6 +19,7 @@ #include <linux/input.h> #include <linux/mutex.h> +#include <linux/workqueue.h> #include <media/dmxdev.h> #include <media/dvb_demux.h> @@ -139,7 +140,7 @@ struct ttusb_dec { int v_pes_postbytes; struct list_head urb_frame_list; - struct tasklet_struct urb_tasklet; + struct work_struct urb_bh_work; spinlock_t urb_frame_list_lock; struct dvb_demux_filter *audio_filter; @@ -766,9 +767,9 @@ static void ttusb_dec_process_urb_frame(struct ttusb_dec *dec, u8 *b, } } -static void ttusb_dec_process_urb_frame_list(struct tasklet_struct *t) +static void ttusb_dec_process_urb_frame_list(struct work_struct *t) { - struct ttusb_dec *dec = from_tasklet(dec, t, urb_tasklet); + struct ttusb_dec *dec = from_work(dec, t, urb_bh_work); struct list_head *item; struct urb_frame *frame; unsigned long flags; @@ -822,7 +823,7 @@ static void ttusb_dec_process_urb(struct urb *urb) spin_unlock_irqrestore(&dec->urb_frame_list_lock, flags); - tasklet_schedule(&dec->urb_tasklet); + queue_work(system_bh_wq, &dec->urb_bh_work); } } } else { @@ -1198,11 +1199,11 @@ static int ttusb_dec_alloc_iso_urbs(struct ttusb_dec *dec) return 0; } -static void ttusb_dec_init_tasklet(struct ttusb_dec *dec) +static void ttusb_dec_init_bh_work(struct ttusb_dec *dec) { spin_lock_init(&dec->urb_frame_list_lock); INIT_LIST_HEAD(&dec->urb_frame_list); - tasklet_setup(&dec->urb_tasklet, ttusb_dec_process_urb_frame_list); + INIT_WORK(&dec->urb_bh_work, ttusb_dec_process_urb_frame_list); } static int ttusb_init_rc( struct ttusb_dec *dec) @@ -1588,12 +1589,12 @@ static void ttusb_dec_exit_usb(struct ttusb_dec *dec) ttusb_dec_free_iso_urbs(dec); } -static void ttusb_dec_exit_tasklet(struct ttusb_dec *dec) +static void ttusb_dec_exit_bh_work(struct ttusb_dec *dec) { struct list_head *item; struct urb_frame *frame; - tasklet_kill(&dec->urb_tasklet); + cancel_work_sync(&dec->urb_bh_work); while ((item = dec->urb_frame_list.next) != &dec->urb_frame_list) { frame = list_entry(item, struct urb_frame, urb_frame_list); @@ -1703,7 +1704,7 @@ static int ttusb_dec_probe(struct usb_interface *intf, ttusb_dec_init_v_pes(dec); ttusb_dec_init_filters(dec); - ttusb_dec_init_tasklet(dec); + ttusb_dec_init_bh_work(dec); dec->active = 1; @@ -1729,7 +1730,7 @@ static void ttusb_dec_disconnect(struct usb_interface *intf) dprintk("%s\n", __func__); if (dec->active) { - ttusb_dec_exit_tasklet(dec); + ttusb_dec_exit_bh_work(dec); ttusb_dec_exit_filters(dec); if(enable_rc) ttusb_dec_exit_rc(dec); diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index 4b685f883e4d..0136df5732ba 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -459,6 +459,94 @@ static void uvc_ctrl_set_rel_speed(struct uvc_control_mapping *mapping, data[first+1] = min_t(int, abs(value), 0xff); } +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_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_60HZ, + V4L2_CID_POWER_LINE_FREQUENCY_50HZ), +}; + +static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_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_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_60HZ, + V4L2_CID_POWER_LINE_FREQUENCY_DISABLED), +}; + +static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_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_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_AUTO, + V4L2_CID_POWER_LINE_FREQUENCY_DISABLED), +}; + +static const struct uvc_control_mapping *uvc_ctrl_filter_plf_mapping( + struct uvc_video_chain *chain, struct uvc_control *ctrl) +{ + const struct uvc_control_mapping *out_mapping = + &uvc_ctrl_power_line_mapping_uvc11; + u8 *buf __free(kfree) = NULL; + u8 init_val; + int ret; + + buf = kmalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) + return NULL; + + /* Save the current PLF value, so we can restore it. */ + ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, ctrl->entity->id, + chain->dev->intfnum, ctrl->info.selector, + buf, sizeof(*buf)); + /* If we cannot read the control skip it. */ + if (ret) + return NULL; + init_val = *buf; + + /* If PLF value cannot be set to off, it is limited. */ + *buf = V4L2_CID_POWER_LINE_FREQUENCY_DISABLED; + ret = uvc_query_ctrl(chain->dev, UVC_SET_CUR, ctrl->entity->id, + chain->dev->intfnum, ctrl->info.selector, + buf, sizeof(*buf)); + if (ret) + return &uvc_ctrl_power_line_mapping_limited; + + /* UVC 1.1 does not define auto, we can exit. */ + if (chain->dev->uvc_version < 0x150) + goto end; + + /* Check if the device supports auto. */ + *buf = V4L2_CID_POWER_LINE_FREQUENCY_AUTO; + ret = uvc_query_ctrl(chain->dev, UVC_SET_CUR, ctrl->entity->id, + chain->dev->intfnum, ctrl->info.selector, + buf, sizeof(*buf)); + if (!ret) + out_mapping = &uvc_ctrl_power_line_mapping_uvc15; + +end: + /* Restore initial value and add mapping. */ + *buf = init_val; + uvc_query_ctrl(chain->dev, UVC_SET_CUR, ctrl->entity->id, + chain->dev->intfnum, ctrl->info.selector, + buf, sizeof(*buf)); + + return out_mapping; +} + static const struct uvc_control_mapping uvc_ctrl_mappings[] = { { .id = V4L2_CID_BRIGHTNESS, @@ -748,52 +836,11 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = { .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, }, -}; - -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_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_60HZ, - V4L2_CID_POWER_LINE_FREQUENCY_50HZ), -}; - -const struct uvc_control_mapping uvc_ctrl_power_line_mapping_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_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_60HZ, - V4L2_CID_POWER_LINE_FREQUENCY_DISABLED), -}; - -static const struct uvc_control_mapping *uvc_ctrl_mappings_uvc11[] = { - &uvc_ctrl_power_line_mapping_uvc11, - NULL, /* Sentinel */ -}; - -static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_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_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_AUTO, - V4L2_CID_POWER_LINE_FREQUENCY_DISABLED), -}; - -static const struct uvc_control_mapping *uvc_ctrl_mappings_uvc15[] = { - &uvc_ctrl_power_line_mapping_uvc15, - NULL, /* Sentinel */ + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL, + .filter_mapping = uvc_ctrl_filter_plf_mapping, + }, }; /* ------------------------------------------------------------------------ @@ -2031,7 +2078,13 @@ static int uvc_ctrl_get_flags(struct uvc_device *dev, else ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, dev->intfnum, info->selector, data, 1); - if (!ret) + + if (!ret) { + info->flags &= ~(UVC_CTRL_FLAG_GET_CUR | + UVC_CTRL_FLAG_SET_CUR | + UVC_CTRL_FLAG_AUTO_UPDATE | + UVC_CTRL_FLAG_ASYNCHRONOUS); + info->flags |= (data[0] & UVC_CONTROL_CAP_GET ? UVC_CTRL_FLAG_GET_CUR : 0) | (data[0] & UVC_CONTROL_CAP_SET ? @@ -2040,6 +2093,7 @@ static int uvc_ctrl_get_flags(struct uvc_device *dev, UVC_CTRL_FLAG_AUTO_UPDATE : 0) | (data[0] & UVC_CONTROL_CAP_ASYNCHRONOUS ? UVC_CTRL_FLAG_ASYNCHRONOUS : 0); + } kfree(data); return ret; @@ -2591,7 +2645,6 @@ static void uvc_ctrl_prune_entity(struct uvc_device *dev, static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain, struct uvc_control *ctrl) { - const struct uvc_control_mapping **mappings; unsigned int i; /* @@ -2623,46 +2676,16 @@ 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; - - for (i = 0; chain->dev->info->mappings[i]; ++i) { - const struct uvc_control_mapping *mapping = - chain->dev->info->mappings[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. */ + /* Process common mappings. */ for (i = 0; i < ARRAY_SIZE(uvc_ctrl_mappings); ++i) { const struct uvc_control_mapping *mapping = &uvc_ctrl_mappings[i]; - 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. */ - mappings = chain->dev->uvc_version < 0x0150 - ? uvc_ctrl_mappings_uvc11 : uvc_ctrl_mappings_uvc15; - - for (i = 0; mappings[i]; ++i) { - const struct uvc_control_mapping *mapping = mappings[i]; + /* Let the device provide a custom mapping. */ + if (mapping->filter_mapping) { + mapping = mapping->filter_mapping(chain, ctrl); + if (!mapping) + continue; + } 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 8fe24c98087e..f0febdc08c2d 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -687,16 +687,26 @@ static int uvc_parse_streaming(struct uvc_device *dev, goto error; } - size = nformats * sizeof(*format) + nframes * sizeof(*frame) + /* + * Allocate memory for the formats, the frames and the intervals, + * plus any required padding to guarantee that everything has the + * correct alignment. + */ + size = nformats * sizeof(*format); + size = ALIGN(size, __alignof__(*frame)) + nframes * sizeof(*frame); + size = ALIGN(size, __alignof__(*interval)) + nintervals * sizeof(*interval); + format = kzalloc(size, GFP_KERNEL); - if (format == NULL) { + if (!format) { ret = -ENOMEM; goto error; } - frame = (struct uvc_frame *)&format[nformats]; - interval = (u32 *)&frame[nframes]; + frame = (void *)format + nformats * sizeof(*format); + frame = PTR_ALIGN(frame, __alignof__(*frame)); + interval = (void *)frame + nframes * sizeof(*frame); + interval = PTR_ALIGN(interval, __alignof__(*interval)); streaming->formats = format; streaming->nformats = 0; @@ -2390,20 +2400,6 @@ MODULE_PARM_DESC(timeout, "Streaming control requests timeout"); * Driver initialization and cleanup */ -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_ctrl_power_line_uvc11 = { - .mappings = (const struct uvc_control_mapping *[]) { - &uvc_ctrl_power_line_mapping_uvc11, - NULL, /* Sentinel */ - }, -}; - static const struct uvc_device_info uvc_quirk_probe_minmax = { .quirks = UVC_QUIRK_PROBE_MINMAX, }; @@ -2434,33 +2430,17 @@ 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 */ + /* Quanta ACER HD User Facing */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x0408, - .idProduct = 0x4034, + .idProduct = 0x4035, .bInterfaceClass = USB_CLASS_VIDEO, .bInterfaceSubClass = 1, .bInterfaceProtocol = UVC_PC_PROTOCOL_15, - .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, + .driver_info = (kernel_ulong_t)&(const struct uvc_device_info){ + .uvc_version = 0x010a, + } }, /* LogiLink Wireless Webcam */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -2580,7 +2560,17 @@ static const struct usb_device_id uvc_ids[] = { .bInterfaceClass = USB_CLASS_VIDEO, .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, - .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_RESTORE_CTRLS_ON_INIT) }, + .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_RESTORE_CTRLS_ON_INIT + | UVC_QUIRK_INVALID_DEVICE_SOF) }, + /* Logitech HD Pro Webcam C922 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x046d, + .idProduct = 0x085c, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_INVALID_DEVICE_SOF) }, /* Logitech Rally Bar Huddle */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -2617,42 +2607,6 @@ 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 Electronics Co., Ltd Integrated Camera */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x04f2, - .idProduct = 0xb67c, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = UVC_PC_PROTOCOL_15, - .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_uvc11 }, - /* 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, @@ -3037,15 +2991,6 @@ static const struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_FORCE_BPP) }, - /* SunplusIT Inc HD Camera */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x2b7e, - .idProduct = 0xb752, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = UVC_PC_PROTOCOL_15, - .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_uvc11 }, /* Insta360 Link */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -3055,51 +3000,6 @@ static const struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_DISABLE_AUTOSUSPEND) }, - /* Lenovo Integrated Camera */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x30c9, - .idProduct = 0x0093, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = UVC_PC_PROTOCOL_15, - .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_uvc11 }, - /* Sonix Technology USB 2.0 Camera */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x3277, - .idProduct = 0x0072, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, - /* Shine-Optics Integrated Camera */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x3277, - .idProduct = 0x009e, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = UVC_PC_PROTOCOL_15, - .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_uvc11 }, - /* 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 }, - /* Acer EasyCamera */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x1180, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, /* Intel D410/ASR depth camera */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 7cbf4692bd87..cd9c29532fb0 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -214,13 +214,13 @@ static void uvc_fixup_video_ctrl(struct uvc_streaming *stream, * 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 - * (assumed to be 12 bytes long). + * high- and super-speed devices) per second and add the UVC + * header size (assumed to be 12 bytes long). */ bandwidth = frame->wWidth * frame->wHeight / 8 * format->bpp; bandwidth *= 10000000 / interval + 1; bandwidth /= 1000; - if (stream->dev->udev->speed == USB_SPEED_HIGH) + if (stream->dev->udev->speed >= USB_SPEED_HIGH) bandwidth /= 8; bandwidth += 12; @@ -466,18 +466,49 @@ static inline ktime_t uvc_video_get_time(void) return ktime_get_real(); } +static void uvc_video_clock_add_sample(struct uvc_clock *clock, + const struct uvc_clock_sample *sample) +{ + unsigned long flags; + + /* + * If we write new data on the position where we had the last + * overflow, remove the overflow pointer. There is no SOF overflow + * in the whole circular buffer. + */ + if (clock->head == clock->last_sof_overflow) + clock->last_sof_overflow = -1; + + spin_lock_irqsave(&clock->lock, flags); + + if (clock->count > 0 && clock->last_sof > sample->dev_sof) { + /* + * Remove data from the circular buffer that is older than the + * last SOF overflow. We only support one SOF overflow per + * circular buffer. + */ + if (clock->last_sof_overflow != -1) + clock->count = (clock->head - clock->last_sof_overflow + + clock->size) % clock->size; + clock->last_sof_overflow = clock->head; + } + + /* Add sample. */ + clock->samples[clock->head] = *sample; + clock->head = (clock->head + 1) % clock->size; + clock->count = min(clock->count + 1, clock->size); + + spin_unlock_irqrestore(&clock->lock, flags); +} + static void uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, const u8 *data, int len) { - struct uvc_clock_sample *sample; + struct uvc_clock_sample sample; unsigned int header_size; bool has_pts = false; bool has_scr = false; - unsigned long flags; - ktime_t time; - u16 host_sof; - u16 dev_sof; switch (data[1] & (UVC_STREAM_PTS | UVC_STREAM_SCR)) { case UVC_STREAM_PTS | UVC_STREAM_SCR: @@ -522,14 +553,51 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, * all the data packets of the same frame contains the same SOF. In that * case only the first one will match the host_sof. */ - dev_sof = get_unaligned_le16(&data[header_size - 2]); - if (dev_sof == stream->clock.last_sof) + sample.dev_sof = get_unaligned_le16(&data[header_size - 2]); + if (sample.dev_sof == stream->clock.last_sof) return; - stream->clock.last_sof = dev_sof; + sample.dev_stc = get_unaligned_le32(&data[header_size - 6]); - host_sof = usb_get_current_frame_number(stream->dev->udev); - time = uvc_video_get_time(); + /* + * STC (Source Time Clock) is the clock used by the camera. The UVC 1.5 + * standard states that it "must be captured when the first video data + * of a video frame is put on the USB bus". This is generally understood + * as requiring devices to clear the payload header's SCR bit before + * the first packet containing video data. + * + * Most vendors follow that interpretation, but some (namely SunplusIT + * on some devices) always set the `UVC_STREAM_SCR` bit, fill the SCR + * field with 0's,and expect that the driver only processes the SCR if + * there is data in the packet. + * + * Ignore all the hardware timestamp information if we haven't received + * any data for this frame yet, the packet contains no data, and both + * STC and SOF are zero. This heuristics should be safe on compliant + * devices. This should be safe with compliant devices, as in the very + * unlikely case where a UVC 1.1 device would send timing information + * only before the first packet containing data, and both STC and SOF + * happen to be zero for a particular frame, we would only miss one + * clock sample from many and the clock recovery algorithm wouldn't + * suffer from this condition. + */ + if (buf && buf->bytesused == 0 && len == header_size && + sample.dev_stc == 0 && sample.dev_sof == 0) + return; + + sample.host_sof = usb_get_current_frame_number(stream->dev->udev); + + /* + * On some devices, like the Logitech C922, the device SOF does not run + * at a stable rate of 1kHz. For those devices use the host SOF instead. + * In the tests performed so far, this improves the timestamp precision. + * This is probably explained by a small packet handling jitter from the + * host, but the exact reason hasn't been fully determined. + */ + if (stream->dev->quirks & UVC_QUIRK_INVALID_DEVICE_SOF) + sample.dev_sof = sample.host_sof; + + sample.host_time = uvc_video_get_time(); /* * The UVC specification allows device implementations that can't obtain @@ -552,46 +620,29 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, * the 8 LSBs of the delta are kept. */ if (stream->clock.sof_offset == (u16)-1) { - u16 delta_sof = (host_sof - dev_sof) & 255; + u16 delta_sof = (sample.host_sof - sample.dev_sof) & 255; if (delta_sof >= 10) stream->clock.sof_offset = delta_sof; else stream->clock.sof_offset = 0; } - dev_sof = (dev_sof + stream->clock.sof_offset) & 2047; - - spin_lock_irqsave(&stream->clock.lock, flags); - - sample = &stream->clock.samples[stream->clock.head]; - sample->dev_stc = get_unaligned_le32(&data[header_size - 6]); - sample->dev_sof = dev_sof; - sample->host_sof = host_sof; - sample->host_time = time; - - /* Update the sliding window head and count. */ - stream->clock.head = (stream->clock.head + 1) % stream->clock.size; - - if (stream->clock.count < stream->clock.size) - stream->clock.count++; - - spin_unlock_irqrestore(&stream->clock.lock, flags); + sample.dev_sof = (sample.dev_sof + stream->clock.sof_offset) & 2047; + uvc_video_clock_add_sample(&stream->clock, &sample); + stream->clock.last_sof = sample.dev_sof; } -static void uvc_video_clock_reset(struct uvc_streaming *stream) +static void uvc_video_clock_reset(struct uvc_clock *clock) { - struct uvc_clock *clock = &stream->clock; - clock->head = 0; clock->count = 0; clock->last_sof = -1; + clock->last_sof_overflow = -1; clock->sof_offset = -1; } -static int uvc_video_clock_init(struct uvc_streaming *stream) +static int uvc_video_clock_init(struct uvc_clock *clock) { - struct uvc_clock *clock = &stream->clock; - spin_lock_init(&clock->lock); clock->size = 32; @@ -600,15 +651,15 @@ static int uvc_video_clock_init(struct uvc_streaming *stream) if (clock->samples == NULL) return -ENOMEM; - uvc_video_clock_reset(stream); + uvc_video_clock_reset(clock); return 0; } -static void uvc_video_clock_cleanup(struct uvc_streaming *stream) +static void uvc_video_clock_cleanup(struct uvc_clock *clock) { - kfree(stream->clock.samples); - stream->clock.samples = NULL; + kfree(clock->samples); + clock->samples = NULL; } /* @@ -709,11 +760,11 @@ void uvc_video_clock_update(struct uvc_streaming *stream, unsigned long flags; u64 timestamp; u32 delta_stc; - u32 y1, y2; + u32 y1; u32 x1, x2; u32 mean; u32 sof; - u64 y; + u64 y, y2; if (!uvc_hw_timestamps_param) return; @@ -728,11 +779,11 @@ void uvc_video_clock_update(struct uvc_streaming *stream, spin_lock_irqsave(&clock->lock, flags); - if (clock->count < clock->size) + if (clock->count < 2) goto done; - first = &clock->samples[clock->head]; - last = &clock->samples[(clock->head - 1) % clock->size]; + first = &clock->samples[(clock->head - clock->count + clock->size) % clock->size]; + last = &clock->samples[(clock->head - 1 + clock->size) % clock->size]; /* First step, PTS to SOF conversion. */ delta_stc = buf->pts - (1UL << 31); @@ -746,6 +797,18 @@ void uvc_video_clock_update(struct uvc_streaming *stream, if (y2 < y1) y2 += 2048 << 16; + /* + * Have at least 1/4 of a second of timestamps before we + * try to do any calculation. Otherwise we do not have enough + * precision. This value was determined by running Android CTS + * on different devices. + * + * dev_sof runs at 1KHz, and we have a fixed point precision of + * 16 bits. + */ + if ((y2 - y1) < ((1000 / 4) << 16)) + goto done; + y = (u64)(y2 - y1) * (1ULL << 31) + (u64)y1 * (u64)x2 - (u64)y2 * (u64)x1; y = div_u64(y, x2 - x1); @@ -753,7 +816,7 @@ void uvc_video_clock_update(struct uvc_streaming *stream, sof = y; uvc_dbg(stream->dev, CLOCK, - "%s: PTS %u y %llu.%06llu SOF %u.%06llu (x1 %u x2 %u y1 %u y2 %u SOF offset %u)\n", + "%s: PTS %u y %llu.%06llu SOF %u.%06llu (x1 %u x2 %u y1 %u y2 %llu SOF offset %u)\n", stream->dev->name, buf->pts, y >> 16, div_u64((y & 0xffff) * 1000000, 65536), sof >> 16, div_u64(((u64)sof & 0xffff) * 1000000LLU, 65536), @@ -768,7 +831,7 @@ void uvc_video_clock_update(struct uvc_streaming *stream, goto done; y1 = NSEC_PER_SEC; - y2 = (u32)ktime_to_ns(ktime_sub(last->host_time, first->host_time)) + y1; + y2 = ktime_to_ns(ktime_sub(last->host_time, first->host_time)) + y1; /* * Interpolated and host SOF timestamps can wrap around at slightly @@ -789,7 +852,7 @@ void uvc_video_clock_update(struct uvc_streaming *stream, timestamp = ktime_to_ns(first->host_time) + y - y1; uvc_dbg(stream->dev, CLOCK, - "%s: SOF %u.%06llu y %llu ts %llu buf ts %llu (x1 %u/%u/%u x2 %u/%u/%u y1 %u y2 %u)\n", + "%s: SOF %u.%06llu y %llu ts %llu buf ts %llu (x1 %u/%u/%u x2 %u/%u/%u y1 %u y2 %llu)\n", stream->dev->name, sof >> 16, div_u64(((u64)sof & 0xffff) * 1000000LLU, 65536), y, timestamp, vbuf->vb2_buf.timestamp, @@ -2071,7 +2134,7 @@ int uvc_video_resume(struct uvc_streaming *stream, int reset) stream->frozen = 0; - uvc_video_clock_reset(stream); + uvc_video_clock_reset(&stream->clock); if (!uvc_queue_streaming(&stream->queue)) return 0; @@ -2220,7 +2283,7 @@ int uvc_video_start_streaming(struct uvc_streaming *stream) { int ret; - ret = uvc_video_clock_init(stream); + ret = uvc_video_clock_init(&stream->clock); if (ret < 0) return ret; @@ -2238,7 +2301,7 @@ int uvc_video_start_streaming(struct uvc_streaming *stream) error_video: usb_set_interface(stream->dev->udev, stream->intfnum, 0); error_commit: - uvc_video_clock_cleanup(stream); + uvc_video_clock_cleanup(&stream->clock); return ret; } @@ -2266,5 +2329,5 @@ void uvc_video_stop_streaming(struct uvc_streaming *stream) usb_clear_halt(stream->dev->udev, pipe); } - uvc_video_clock_cleanup(stream); + uvc_video_clock_cleanup(&stream->clock); } diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index 3653b2c8a86c..b7d24a853ce4 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -75,6 +75,7 @@ #define UVC_QUIRK_WAKE_AUTOSUSPEND 0x00002000 #define UVC_QUIRK_NO_RESET_RESUME 0x00004000 #define UVC_QUIRK_DISABLE_AUTOSUSPEND 0x00008000 +#define UVC_QUIRK_INVALID_DEVICE_SOF 0x00010000 /* Format flags */ #define UVC_FMT_FLAG_COMPRESSED 0x00000001 @@ -86,7 +87,9 @@ struct gpio_desc; struct sg_table; +struct uvc_control; struct uvc_device; +struct uvc_video_chain; /* * TODO: Put the most frequently accessed fields at the beginning of @@ -125,6 +128,9 @@ struct uvc_control_mapping { s32 master_manual; u32 slave_ids[2]; + const struct uvc_control_mapping *(*filter_mapping) + (struct uvc_video_chain *chain, + struct uvc_control *ctrl); s32 (*get)(struct uvc_control_mapping *mapping, u8 query, const u8 *data); void (*set)(struct uvc_control_mapping *mapping, s32 value, @@ -500,6 +506,7 @@ struct uvc_streaming { unsigned int head; unsigned int count; unsigned int size; + unsigned int last_sof_overflow; u16 last_sof; u16 sof_offset; @@ -524,7 +531,6 @@ struct uvc_device_info { u32 quirks; u32 meta_format; u16 uvc_version; - const struct uvc_control_mapping **mappings; }; struct uvc_status_streaming { @@ -750,8 +756,6 @@ int uvc_status_start(struct uvc_device *dev, gfp_t flags); void uvc_status_stop(struct uvc_device *dev); /* Controls */ -extern const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited; -extern const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc11; extern const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops; int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 222f01665f7c..ee884a8221fb 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -323,6 +323,13 @@ static int v4l2_async_create_ancillary_links(struct v4l2_async_notifier *n, sd->entity.function != MEDIA_ENT_F_FLASH) return 0; + if (!n->sd) { + dev_warn(notifier_dev(n), + "not a sub-device notifier, not creating an ancillary link for %s!\n", + dev_name(sd->dev)); + return 0; + } + link = media_create_ancillary_link(&n->sd->entity, &sd->entity); return IS_ERR(link) ? PTR_ERR(link) : 0; @@ -965,4 +972,5 @@ module_exit(v4l2_async_exit); MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>"); MODULE_AUTHOR("Ezequiel Garcia <ezequiel@collabora.com>"); +MODULE_DESCRIPTION("V4L2 asynchronous subdevice registration API"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/v4l2-core/v4l2-cci.c b/drivers/media/v4l2-core/v4l2-cci.c index ee3475bed37f..1ff94affbaf3 100644 --- a/drivers/media/v4l2-core/v4l2-cci.c +++ b/drivers/media/v4l2-core/v4l2-cci.c @@ -23,6 +23,15 @@ int cci_read(struct regmap *map, u32 reg, u64 *val, int *err) u8 buf[8]; int ret; + /* + * TODO: Fix smatch. Assign *val to 0 here in order to avoid + * failing a smatch check on caller when the caller proceeds to + * read *val without initialising it on caller's side. *val is set + * to a valid value whenever this function returns 0 but smatch + * can't figure that out currently. + */ + *val = 0; + if (err && *err) return *err; diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index 4165c815faef..0a2f4f0d0a07 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -253,6 +253,8 @@ const struct v4l2_format_info *v4l2_format_info(u32 format) { .format = V4L2_PIX_FMT_RGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_BGR666, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_BGR48_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGR48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_ABGR64_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGBA1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGBX1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, diff --git a/drivers/media/v4l2-core/v4l2-ctrls-defs.c b/drivers/media/v4l2-core/v4l2-ctrls-defs.c index 8696eb1cdd61..1ea52011247a 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-defs.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-defs.c @@ -970,6 +970,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_LTR_COUNT: return "LTR Count"; case V4L2_CID_MPEG_VIDEO_FRAME_LTR_INDEX: return "Frame LTR Index"; case V4L2_CID_MPEG_VIDEO_USE_LTR_FRAMES: return "Use LTR Frames"; + case V4L2_CID_MPEG_VIDEO_AVERAGE_QP: return "Average QP Value"; case V4L2_CID_FWHT_I_FRAME_QP: return "FWHT I-Frame QP Value"; case V4L2_CID_FWHT_P_FRAME_QP: return "FWHT P-Frame QP Value"; @@ -1507,6 +1508,10 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, *max = 0xffffffffffffLL; *step = 1; break; + case V4L2_CID_MPEG_VIDEO_AVERAGE_QP: + *type = V4L2_CTRL_TYPE_INTEGER; + *flags |= V4L2_CTRL_FLAG_READ_ONLY; + break; case V4L2_CID_PIXEL_RATE: *type = V4L2_CTRL_TYPE_INTEGER64; *flags |= V4L2_CTRL_FLAG_READ_ONLY; diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index 89c7192148df..f19c8adf2c61 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -1251,6 +1251,7 @@ out_cleanup: } EXPORT_SYMBOL_GPL(v4l2_async_register_subdev_sensor); +MODULE_DESCRIPTION("V4L2 fwnode binding parsing library"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>"); MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 4c76d17b4629..5eb4d797d259 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1307,6 +1307,8 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_RGBX1010102: descr = "32-bit RGBX 10-10-10-2"; break; case V4L2_PIX_FMT_RGBA1010102: descr = "32-bit RGBA 10-10-10-2"; break; case V4L2_PIX_FMT_ARGB2101010: descr = "32-bit ARGB 2-10-10-10"; break; + case V4L2_PIX_FMT_BGR48: descr = "48-bit BGR 16-16-16"; break; + case V4L2_PIX_FMT_RGB48: descr = "48-bit RGB 16-16-16"; break; case V4L2_PIX_FMT_BGR48_12: descr = "12-bit Depth BGR"; break; case V4L2_PIX_FMT_ABGR64_12: descr = "12-bit Depth BGRA"; break; case V4L2_PIX_FMT_GREY: descr = "8-bit Greyscale"; break; @@ -1463,6 +1465,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_Y210: descr = "10-bit YUYV Packed"; break; case V4L2_PIX_FMT_Y212: descr = "12-bit YUYV Packed"; break; case V4L2_PIX_FMT_Y216: descr = "16-bit YUYV Packed"; break; + case V4L2_META_FMT_RPI_BE_CFG: descr = "RPi PiSP BE Config format"; break; case V4L2_META_FMT_GENERIC_8: descr = "8-bit Generic Metadata"; break; case V4L2_META_FMT_GENERIC_CSI2_10: descr = "8-bit Generic Meta, 10b CSI-2"; break; case V4L2_META_FMT_GENERIC_CSI2_12: descr = "8-bit Generic Meta, 12b CSI-2"; break; @@ -1529,6 +1532,16 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_MT2110T: descr = "Mediatek 10bit Tile Mode"; break; case V4L2_PIX_FMT_MT2110R: descr = "Mediatek 10bit Raster Mode"; break; case V4L2_PIX_FMT_HEXTILE: descr = "Hextile Compressed Format"; break; + case V4L2_PIX_FMT_PISP_COMP1_RGGB: descr = "PiSP 8b RGRG/GBGB mode1 compr"; break; + case V4L2_PIX_FMT_PISP_COMP1_GRBG: descr = "PiSP 8b GRGR/BGBG mode1 compr"; break; + case V4L2_PIX_FMT_PISP_COMP1_GBRG: descr = "PiSP 8b GBGB/RGRG mode1 compr"; break; + case V4L2_PIX_FMT_PISP_COMP1_BGGR: descr = "PiSP 8b BGBG/GRGR mode1 compr"; break; + case V4L2_PIX_FMT_PISP_COMP1_MONO: descr = "PiSP 8b monochrome mode1 compr"; break; + case V4L2_PIX_FMT_PISP_COMP2_RGGB: descr = "PiSP 8b RGRG/GBGB mode2 compr"; break; + case V4L2_PIX_FMT_PISP_COMP2_GRBG: descr = "PiSP 8b GRGR/BGBG mode2 compr"; break; + case V4L2_PIX_FMT_PISP_COMP2_GBRG: descr = "PiSP 8b GBGB/RGRG mode2 compr"; break; + case V4L2_PIX_FMT_PISP_COMP2_BGGR: descr = "PiSP 8b BGBG/GRGR mode2 compr"; break; + case V4L2_PIX_FMT_PISP_COMP2_MONO: descr = "PiSP 8b monochrome mode2 compr"; break; default: if (fmt->description[0]) return; diff --git a/drivers/media/v4l2-core/v4l2-jpeg.c b/drivers/media/v4l2-core/v4l2-jpeg.c index 94435a7b6816..b8bece739d07 100644 --- a/drivers/media/v4l2-core/v4l2-jpeg.c +++ b/drivers/media/v4l2-core/v4l2-jpeg.c @@ -52,6 +52,122 @@ MODULE_LICENSE("GPL"); #define COM 0xfffe /* comment */ #define TEM 0xff01 /* temporary */ +/* Luma and chroma qp tables to achieve 50% compression quality + * This is as per example in Annex K.1 of ITU-T.81 + */ +const u8 v4l2_jpeg_ref_table_luma_qt[V4L2_JPEG_PIXELS_IN_BLOCK] = { + 16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 56, 68, 109, 103, 77, + 24, 35, 55, 64, 81, 104, 113, 92, + 49, 64, 78, 87, 103, 121, 120, 101, + 72, 92, 95, 98, 112, 100, 103, 99 +}; +EXPORT_SYMBOL_GPL(v4l2_jpeg_ref_table_luma_qt); + +const u8 v4l2_jpeg_ref_table_chroma_qt[V4L2_JPEG_PIXELS_IN_BLOCK] = { + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 +}; +EXPORT_SYMBOL_GPL(v4l2_jpeg_ref_table_chroma_qt); + +/* Zigzag scan pattern indexes */ +const u8 v4l2_jpeg_zigzag_scan_index[V4L2_JPEG_PIXELS_IN_BLOCK] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63 +}; +EXPORT_SYMBOL_GPL(v4l2_jpeg_zigzag_scan_index); + +/* + * Contains the data that needs to be sent in the marker segment of an + * interchange format JPEG stream or an abbreviated format table specification + * data stream. Specifies the huffman table used for encoding the luminance DC + * coefficient differences. The table represents Table K.3 of ITU-T.81 + */ +const u8 v4l2_jpeg_ref_table_luma_dc_ht[V4L2_JPEG_REF_HT_DC_LEN] = { + 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B +}; +EXPORT_SYMBOL_GPL(v4l2_jpeg_ref_table_luma_dc_ht); + +/* + * Contains the data that needs to be sent in the marker segment of an + * interchange format JPEG stream or an abbreviated format table specification + * data stream. Specifies the huffman table used for encoding the luminance AC + * coefficients. The table represents Table K.5 of ITU-T.81 + */ +const u8 v4l2_jpeg_ref_table_luma_ac_ht[V4L2_JPEG_REF_HT_AC_LEN] = { + 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, + 0x00, 0x00, 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, + 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, + 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, + 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, + 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, + 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, + 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, + 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, + 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, + 0xD9, 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, + 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA +}; +EXPORT_SYMBOL_GPL(v4l2_jpeg_ref_table_luma_ac_ht); + +/* + * Contains the data that needs to be sent in the marker segment of an interchange format JPEG + * stream or an abbreviated format table specification data stream. + * Specifies the huffman table used for encoding the chrominance DC coefficient differences. + * The table represents Table K.4 of ITU-T.81 + */ +const u8 v4l2_jpeg_ref_table_chroma_dc_ht[V4L2_JPEG_REF_HT_DC_LEN] = { + 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B +}; +EXPORT_SYMBOL_GPL(v4l2_jpeg_ref_table_chroma_dc_ht); + +/* + * Contains the data that needs to be sent in the marker segment of an + * interchange format JPEG stream or an abbreviated format table specification + * data stream. Specifies the huffman table used for encoding the chrominance + * AC coefficients. The table represents Table K.6 of ITU-T.81 + */ +const u8 v4l2_jpeg_ref_table_chroma_ac_ht[V4L2_JPEG_REF_HT_AC_LEN] = { + 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, + 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, + 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, + 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, + 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, + 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, + 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, + 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, + 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, + 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, + 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, + 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA +}; +EXPORT_SYMBOL_GPL(v4l2_jpeg_ref_table_chroma_ac_ht); + /** * struct jpeg_stream - JPEG byte stream * @curr: current position in stream diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 8470d6eda9a3..7c5812d55315 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -148,6 +148,23 @@ static int subdev_close(struct file *file) } #endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */ +static void v4l2_subdev_enable_privacy_led(struct v4l2_subdev *sd) +{ +#if IS_REACHABLE(CONFIG_LEDS_CLASS) + if (!IS_ERR_OR_NULL(sd->privacy_led)) + led_set_brightness(sd->privacy_led, + sd->privacy_led->max_brightness); +#endif +} + +static void v4l2_subdev_disable_privacy_led(struct v4l2_subdev *sd) +{ +#if IS_REACHABLE(CONFIG_LEDS_CLASS) + if (!IS_ERR_OR_NULL(sd->privacy_led)) + led_set_brightness(sd->privacy_led, 0); +#endif +} + static inline int check_which(u32 which) { if (which != V4L2_SUBDEV_FORMAT_TRY && @@ -434,12 +451,8 @@ static int call_s_stream(struct v4l2_subdev *sd, int enable) * The .s_stream() operation must never be called to start or stop an * already started or stopped subdev. Catch offenders but don't return * an error yet to avoid regressions. - * - * As .s_stream() is mutually exclusive with the .enable_streams() and - * .disable_streams() operation, we can use the enabled_streams field - * to store the subdev streaming state. */ - if (WARN_ON(!!sd->enabled_streams == !!enable)) + if (WARN_ON(sd->s_stream_enabled == !!enable)) return 0; ret = sd->ops->video->s_stream(sd, enable); @@ -450,17 +463,12 @@ static int call_s_stream(struct v4l2_subdev *sd, int enable) } if (!ret) { - sd->enabled_streams = enable ? BIT(0) : 0; + sd->s_stream_enabled = enable; -#if IS_REACHABLE(CONFIG_LEDS_CLASS) - if (!IS_ERR_OR_NULL(sd->privacy_led)) { - if (enable) - led_set_brightness(sd->privacy_led, - sd->privacy_led->max_brightness); - else - led_set_brightness(sd->privacy_led, 0); - } -#endif + if (enable) + v4l2_subdev_enable_privacy_led(sd); + else + v4l2_subdev_disable_privacy_led(sd); } return ret; @@ -1261,14 +1269,6 @@ v4l2_subdev_link_validate_get_format(struct media_pad *pad, u32 stream, struct v4l2_subdev *sd; int ret; - if (!is_media_entity_v4l2_subdev(pad->entity)) { - WARN(pad->entity->function != MEDIA_ENT_F_IO_V4L, - "Driver bug! Wrong media entity type 0x%08x, entity %s\n", - pad->entity->function, pad->entity->name); - - return -EINVAL; - } - sd = media_entity_to_v4l2_subdev(pad->entity); fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; @@ -1576,6 +1576,33 @@ int __v4l2_subdev_init_finalize(struct v4l2_subdev *sd, const char *name, struct lock_class_key *key) { struct v4l2_subdev_state *state; + struct device *dev = sd->dev; + bool has_disable_streams; + bool has_enable_streams; + bool has_s_stream; + + /* Check that the subdevice implements the required features */ + + has_s_stream = v4l2_subdev_has_op(sd, video, s_stream); + has_enable_streams = v4l2_subdev_has_op(sd, pad, enable_streams); + has_disable_streams = v4l2_subdev_has_op(sd, pad, disable_streams); + + if (has_enable_streams != has_disable_streams) { + dev_err(dev, + "subdev '%s' must implement both or neither of .enable_streams() and .disable_streams()\n", + sd->name); + return -EINVAL; + } + + if (sd->flags & V4L2_SUBDEV_FL_STREAMS) { + if (has_s_stream && !has_enable_streams) { + dev_err(dev, + "subdev '%s' must implement .enable/disable_streams()\n", + sd->name); + + return -EINVAL; + } + } state = __v4l2_subdev_state_alloc(sd, name, key); if (IS_ERR(state)) @@ -2120,43 +2147,55 @@ out: } EXPORT_SYMBOL_GPL(v4l2_subdev_routing_validate); -static int v4l2_subdev_enable_streams_fallback(struct v4l2_subdev *sd, u32 pad, - u64 streams_mask) +static void v4l2_subdev_collect_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask, + u64 *found_streams, + u64 *enabled_streams) { - struct device *dev = sd->entity.graph_obj.mdev->dev; - unsigned int i; - int ret; + if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) { + *found_streams = BIT_ULL(0); + *enabled_streams = + (sd->enabled_pads & BIT_ULL(pad)) ? BIT_ULL(0) : 0; + return; + } - /* - * The subdev doesn't implement pad-based stream enable, fall back - * on the .s_stream() operation. This can only be done for subdevs that - * have a single source pad, as sd->enabled_streams is global to the - * subdev. - */ - if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)) - return -EOPNOTSUPP; + *found_streams = 0; + *enabled_streams = 0; - for (i = 0; i < sd->entity.num_pads; ++i) { - if (i != pad && sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE) - return -EOPNOTSUPP; - } + for (unsigned int i = 0; i < state->stream_configs.num_configs; ++i) { + const struct v4l2_subdev_stream_config *cfg = + &state->stream_configs.configs[i]; - if (sd->enabled_streams & streams_mask) { - dev_dbg(dev, "set of streams %#llx already enabled on %s:%u\n", - streams_mask, sd->entity.name, pad); - return -EALREADY; + if (cfg->pad != pad || !(streams_mask & BIT_ULL(cfg->stream))) + continue; + + *found_streams |= BIT_ULL(cfg->stream); + if (cfg->enabled) + *enabled_streams |= BIT_ULL(cfg->stream); } +} - /* Start streaming when the first streams are enabled. */ - if (!sd->enabled_streams) { - ret = v4l2_subdev_call(sd, video, s_stream, 1); - if (ret) - return ret; +static void v4l2_subdev_set_streams_enabled(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask, + bool enabled) +{ + if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) { + if (enabled) + sd->enabled_pads |= BIT_ULL(pad); + else + sd->enabled_pads &= ~BIT_ULL(pad); + return; } - sd->enabled_streams |= streams_mask; + for (unsigned int i = 0; i < state->stream_configs.num_configs; ++i) { + struct v4l2_subdev_stream_config *cfg = + &state->stream_configs.configs[i]; - return 0; + if (cfg->pad == pad && (streams_mask & BIT_ULL(cfg->stream))) + cfg->enabled = enabled; + } } int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, @@ -2164,44 +2203,44 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, { struct device *dev = sd->entity.graph_obj.mdev->dev; struct v4l2_subdev_state *state; - u64 found_streams = 0; - unsigned int i; + bool already_streaming; + u64 enabled_streams; + u64 found_streams; + bool use_s_stream; int ret; /* A few basic sanity checks first. */ if (pad >= sd->entity.num_pads) return -EINVAL; + if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)) + return -EOPNOTSUPP; + + /* + * We use a 64-bit bitmask for tracking enabled pads, so only subdevices + * with 64 pads or less can be supported. + */ + if (pad >= sizeof(sd->enabled_pads) * BITS_PER_BYTE) + return -EOPNOTSUPP; + if (!streams_mask) return 0; /* Fallback on .s_stream() if .enable_streams() isn't available. */ - if (!sd->ops->pad || !sd->ops->pad->enable_streams) - return v4l2_subdev_enable_streams_fallback(sd, pad, - streams_mask); + use_s_stream = !v4l2_subdev_has_op(sd, pad, enable_streams); - state = v4l2_subdev_lock_and_get_active_state(sd); + if (!use_s_stream) + state = v4l2_subdev_lock_and_get_active_state(sd); + else + state = NULL; /* * Verify that the requested streams exist and that they are not * already enabled. */ - for (i = 0; i < state->stream_configs.num_configs; ++i) { - struct v4l2_subdev_stream_config *cfg = - &state->stream_configs.configs[i]; - - if (cfg->pad != pad || !(streams_mask & BIT_ULL(cfg->stream))) - continue; - found_streams |= BIT_ULL(cfg->stream); - - if (cfg->enabled) { - dev_dbg(dev, "stream %u already enabled on %s:%u\n", - cfg->stream, sd->entity.name, pad); - ret = -EALREADY; - goto done; - } - } + v4l2_subdev_collect_streams(sd, state, pad, streams_mask, + &found_streams, &enabled_streams); if (found_streams != streams_mask) { dev_dbg(dev, "streams 0x%llx not found on %s:%u\n", @@ -2210,11 +2249,29 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, goto done; } + if (enabled_streams) { + dev_dbg(dev, "streams 0x%llx already enabled on %s:%u\n", + enabled_streams, sd->entity.name, pad); + ret = -EALREADY; + goto done; + } + dev_dbg(dev, "enable streams %u:%#llx\n", pad, streams_mask); - /* Call the .enable_streams() operation. */ - ret = v4l2_subdev_call(sd, pad, enable_streams, state, pad, - streams_mask); + already_streaming = v4l2_subdev_is_streaming(sd); + + if (!use_s_stream) { + /* Call the .enable_streams() operation. */ + ret = v4l2_subdev_call(sd, pad, enable_streams, state, pad, + streams_mask); + } else { + /* Start streaming when the first pad is enabled. */ + if (!already_streaming) + ret = v4l2_subdev_call(sd, video, s_stream, 1); + else + ret = 0; + } + if (ret) { dev_dbg(dev, "enable streams %u:%#llx failed: %d\n", pad, streams_mask, ret); @@ -2222,103 +2279,68 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, } /* Mark the streams as enabled. */ - for (i = 0; i < state->stream_configs.num_configs; ++i) { - struct v4l2_subdev_stream_config *cfg = - &state->stream_configs.configs[i]; + v4l2_subdev_set_streams_enabled(sd, state, pad, streams_mask, true); - if (cfg->pad == pad && (streams_mask & BIT_ULL(cfg->stream))) - cfg->enabled = true; - } + /* + * TODO: When all the drivers have been changed to use + * v4l2_subdev_enable_streams() and v4l2_subdev_disable_streams(), + * instead of calling .s_stream() operation directly, we can remove + * the privacy LED handling from call_s_stream() and do it here + * for all cases. + */ + if (!use_s_stream && !already_streaming) + v4l2_subdev_enable_privacy_led(sd); done: - v4l2_subdev_unlock_state(state); + if (!use_s_stream) + v4l2_subdev_unlock_state(state); return ret; } EXPORT_SYMBOL_GPL(v4l2_subdev_enable_streams); -static int v4l2_subdev_disable_streams_fallback(struct v4l2_subdev *sd, u32 pad, - u64 streams_mask) -{ - struct device *dev = sd->entity.graph_obj.mdev->dev; - unsigned int i; - int ret; - - /* - * If the subdev doesn't implement pad-based stream enable, fall back - * on the .s_stream() operation. This can only be done for subdevs that - * have a single source pad, as sd->enabled_streams is global to the - * subdev. - */ - if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)) - return -EOPNOTSUPP; - - for (i = 0; i < sd->entity.num_pads; ++i) { - if (i != pad && sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE) - return -EOPNOTSUPP; - } - - if ((sd->enabled_streams & streams_mask) != streams_mask) { - dev_dbg(dev, "set of streams %#llx already disabled on %s:%u\n", - streams_mask, sd->entity.name, pad); - return -EALREADY; - } - - /* Stop streaming when the last streams are disabled. */ - if (!(sd->enabled_streams & ~streams_mask)) { - ret = v4l2_subdev_call(sd, video, s_stream, 0); - if (ret) - return ret; - } - - sd->enabled_streams &= ~streams_mask; - - return 0; -} - int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad, u64 streams_mask) { struct device *dev = sd->entity.graph_obj.mdev->dev; struct v4l2_subdev_state *state; - u64 found_streams = 0; - unsigned int i; + u64 enabled_streams; + u64 found_streams; + bool use_s_stream; int ret; /* A few basic sanity checks first. */ if (pad >= sd->entity.num_pads) return -EINVAL; + if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)) + return -EOPNOTSUPP; + + /* + * We use a 64-bit bitmask for tracking enabled pads, so only subdevices + * with 64 pads or less can be supported. + */ + if (pad >= sizeof(sd->enabled_pads) * BITS_PER_BYTE) + return -EOPNOTSUPP; + if (!streams_mask) return 0; /* Fallback on .s_stream() if .disable_streams() isn't available. */ - if (!sd->ops->pad || !sd->ops->pad->disable_streams) - return v4l2_subdev_disable_streams_fallback(sd, pad, - streams_mask); + use_s_stream = !v4l2_subdev_has_op(sd, pad, disable_streams); - state = v4l2_subdev_lock_and_get_active_state(sd); + if (!use_s_stream) + state = v4l2_subdev_lock_and_get_active_state(sd); + else + state = NULL; /* * Verify that the requested streams exist and that they are not * already disabled. */ - for (i = 0; i < state->stream_configs.num_configs; ++i) { - struct v4l2_subdev_stream_config *cfg = - &state->stream_configs.configs[i]; - if (cfg->pad != pad || !(streams_mask & BIT_ULL(cfg->stream))) - continue; - - found_streams |= BIT_ULL(cfg->stream); - - if (!cfg->enabled) { - dev_dbg(dev, "stream %u already disabled on %s:%u\n", - cfg->stream, sd->entity.name, pad); - ret = -EALREADY; - goto done; - } - } + v4l2_subdev_collect_streams(sd, state, pad, streams_mask, + &found_streams, &enabled_streams); if (found_streams != streams_mask) { dev_dbg(dev, "streams 0x%llx not found on %s:%u\n", @@ -2327,28 +2349,43 @@ int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad, goto done; } + if (enabled_streams != streams_mask) { + dev_dbg(dev, "streams 0x%llx already disabled on %s:%u\n", + streams_mask & ~enabled_streams, sd->entity.name, pad); + ret = -EALREADY; + goto done; + } + dev_dbg(dev, "disable streams %u:%#llx\n", pad, streams_mask); - /* Call the .disable_streams() operation. */ - ret = v4l2_subdev_call(sd, pad, disable_streams, state, pad, - streams_mask); + if (!use_s_stream) { + /* Call the .disable_streams() operation. */ + ret = v4l2_subdev_call(sd, pad, disable_streams, state, pad, + streams_mask); + } else { + /* Stop streaming when the last streams are disabled. */ + + if (!(sd->enabled_pads & ~BIT_ULL(pad))) + ret = v4l2_subdev_call(sd, video, s_stream, 0); + else + ret = 0; + } + if (ret) { dev_dbg(dev, "disable streams %u:%#llx failed: %d\n", pad, streams_mask, ret); goto done; } - /* Mark the streams as disabled. */ - for (i = 0; i < state->stream_configs.num_configs; ++i) { - struct v4l2_subdev_stream_config *cfg = - &state->stream_configs.configs[i]; - - if (cfg->pad == pad && (streams_mask & BIT_ULL(cfg->stream))) - cfg->enabled = false; - } + v4l2_subdev_set_streams_enabled(sd, state, pad, streams_mask, false); done: - v4l2_subdev_unlock_state(state); + if (!use_s_stream) { + if (!v4l2_subdev_is_streaming(sd)) + v4l2_subdev_disable_privacy_led(sd); + + v4l2_subdev_unlock_state(state); + } return ret; } @@ -2377,15 +2414,24 @@ int v4l2_subdev_s_stream_helper(struct v4l2_subdev *sd, int enable) if (WARN_ON(pad_index == -1)) return -EINVAL; - /* - * As there's a single source pad, just collect all the source streams. - */ - state = v4l2_subdev_lock_and_get_active_state(sd); + if (sd->flags & V4L2_SUBDEV_FL_STREAMS) { + /* + * As there's a single source pad, just collect all the source + * streams. + */ + state = v4l2_subdev_lock_and_get_active_state(sd); - for_each_active_route(&state->routing, route) - source_mask |= BIT_ULL(route->source_stream); + for_each_active_route(&state->routing, route) + source_mask |= BIT_ULL(route->source_stream); - v4l2_subdev_unlock_state(state); + v4l2_subdev_unlock_state(state); + } else { + /* + * For non-streams subdevices, there's a single implicit stream + * per pad. + */ + source_mask = BIT_ULL(0); + } if (enable) return v4l2_subdev_enable_streams(sd, pad_index, source_mask); @@ -2427,6 +2473,31 @@ void v4l2_subdev_notify_event(struct v4l2_subdev *sd, } EXPORT_SYMBOL_GPL(v4l2_subdev_notify_event); +bool v4l2_subdev_is_streaming(struct v4l2_subdev *sd) +{ + struct v4l2_subdev_state *state; + + if (!v4l2_subdev_has_op(sd, pad, enable_streams)) + return sd->s_stream_enabled; + + if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) + return !!sd->enabled_pads; + + state = v4l2_subdev_get_locked_active_state(sd); + + for (unsigned int i = 0; i < state->stream_configs.num_configs; ++i) { + const struct v4l2_subdev_stream_config *cfg; + + cfg = &state->stream_configs.configs[i]; + + if (cfg->enabled) + return true; + } + + return false; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_is_streaming); + int v4l2_subdev_get_privacy_led(struct v4l2_subdev *sd) { #if IS_REACHABLE(CONFIG_LEDS_CLASS) |