diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2025-01-25 15:59:46 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2025-01-25 15:59:46 -0800 |
commit | 184a0997fb77f4a9527fc867fcd16806776c27ce (patch) | |
tree | 1775a93287d59f002f0f4720187dcef32133ca42 /drivers | |
parent | 405057718a1f9074133979a9f2ff0c9fa4a19948 (diff) | |
parent | c4b7779abc6633677e6edb79e2809f4f61fde157 (diff) |
Merge tag 'media/v6.14-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab:
- Sensor driver fixes
- remove dead TI wl128x FM radio driver
- Add support for the imx462 sensor at the IMX290 binding
- V4L2 pixel data transmitter and receiver documentation improvements
- Add support for MIPI Discovery and Configuration for C-PHY line
orders
- imx8-isi fixes and improvements
- stm32: dcmipp: add core support for the stm32mp25
- qcom: camss: Add sc7280 support
- Various fixes and enhancements
* tag 'media/v6.14-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (152 commits)
media: nuvoton: Fix an error check in npcm_video_ece_init()
media: dvb-usb-v2: af9035: fix ISO C90 compilation error on af9035_i2c_master_xfer
media: platform: rzg2l-cru: rzg2l-video: Fix the comment in rzg2l_cru_start_streaming_vq()
media: fix secfeed undefined when filter alloc fail
media: dt-bindings: trivial white-space and example cleanup
MAINTAINERS: repair file entry in MEDIA DRIVERS FOR STM32 - CSI
media: solo6x10: Use const 'struct bin_attribute' callback
media: saa7164: Remove unused values
staging: media: imx: fix OF node leak in imx_media_add_of_subdevs()
media: platform: exynos4-is: Remove unused __is_get_frame_size
media: vidtv: Fix a null-ptr-deref in vidtv_mux_stop_thread
media: mmp: Bring back registration of the device
media: cec: include linux/debugfs.h and linux/seq_file.h where needed
Revert "media: qcom: camss: Restructure camss_link_entities"
media: venus: Remove unused hfi_core_ping()
media: dt-bindings: qcom-venus: Deprecate video-decoder and video-encoder where applicable
media: venus: Populate video encoder/decoder nodename entries
media: venus: Add support for static video encoder/decoder declarations
media: venus: match instance creation and destruction order
media: venus: destroy hfi session after m2m_ctx release
...
Diffstat (limited to 'drivers')
129 files changed, 3833 insertions, 6128 deletions
diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c index c7d36010c890..ba6828ef540e 100644 --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -7,12 +7,13 @@ #include <linux/errno.h> #include <linux/init.h> -#include <linux/module.h> #include <linux/kernel.h> #include <linux/kmod.h> #include <linux/ktime.h> -#include <linux/slab.h> #include <linux/mm.h> +#include <linux/module.h> +#include <linux/seq_file.h> +#include <linux/slab.h> #include <linux/string.h> #include <linux/types.h> diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c index ca0db8d457b4..e10bd588a586 100644 --- a/drivers/media/cec/core/cec-core.c +++ b/drivers/media/cec/core/cec-core.c @@ -5,13 +5,14 @@ * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved. */ +#include <linux/debugfs.h> #include <linux/errno.h> #include <linux/init.h> -#include <linux/module.h> #include <linux/kernel.h> #include <linux/kmod.h> -#include <linux/slab.h> #include <linux/mm.h> +#include <linux/module.h> +#include <linux/slab.h> #include <linux/string.h> #include <linux/types.h> diff --git a/drivers/media/cec/core/cec-pin-error-inj.c b/drivers/media/cec/core/cec-pin-error-inj.c index fc0968b9d40e..6e61a04b8168 100644 --- a/drivers/media/cec/core/cec-pin-error-inj.c +++ b/drivers/media/cec/core/cec-pin-error-inj.c @@ -4,8 +4,9 @@ */ #include <linux/delay.h> -#include <linux/slab.h> #include <linux/sched/types.h> +#include <linux/seq_file.h> +#include <linux/slab.h> #include <media/cec-pin.h> #include "cec-pin-priv.h" diff --git a/drivers/media/cec/core/cec-pin.c b/drivers/media/cec/core/cec-pin.c index 330d5d5d86ab..a70451d99ebc 100644 --- a/drivers/media/cec/core/cec-pin.c +++ b/drivers/media/cec/core/cec-pin.c @@ -4,8 +4,9 @@ */ #include <linux/delay.h> -#include <linux/slab.h> #include <linux/sched/types.h> +#include <linux/seq_file.h> +#include <linux/slab.h> #include <media/cec-pin.h> #include "cec-pin-priv.h" diff --git a/drivers/media/cec/platform/cec-gpio/cec-gpio.c b/drivers/media/cec/platform/cec-gpio/cec-gpio.c index cf64e8871fe5..50cdc557c943 100644 --- a/drivers/media/cec/platform/cec-gpio/cec-gpio.c +++ b/drivers/media/cec/platform/cec-gpio/cec-gpio.c @@ -3,11 +3,12 @@ * Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved. */ -#include <linux/module.h> -#include <linux/interrupt.h> #include <linux/delay.h> -#include <linux/platform_device.h> #include <linux/gpio/consumer.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/seq_file.h> #include <media/cec-notifier.h> #include <media/cec-pin.h> diff --git a/drivers/media/common/b2c2/flexcop-common.h b/drivers/media/common/b2c2/flexcop-common.h index f944c59cf495..a468ea7e77a1 100644 --- a/drivers/media/common/b2c2/flexcop-common.h +++ b/drivers/media/common/b2c2/flexcop-common.h @@ -125,8 +125,6 @@ void flexcop_dma_free(struct flexcop_dma *dma); int flexcop_dma_control_timer_irq(struct flexcop_device *fc, flexcop_dma_index_t no, int onoff); -int flexcop_dma_control_size_irq(struct flexcop_device *fc, - flexcop_dma_index_t no, int onoff); int flexcop_dma_config(struct flexcop_device *fc, struct flexcop_dma *dma, flexcop_dma_index_t dma_idx); int flexcop_dma_xfer_control(struct flexcop_device *fc, @@ -170,8 +168,6 @@ int flexcop_sram_init(struct flexcop_device *fc); void flexcop_determine_revision(struct flexcop_device *fc); void flexcop_device_name(struct flexcop_device *fc, const char *prefix, const char *suffix); -void flexcop_dump_reg(struct flexcop_device *fc, - flexcop_ibi_register reg, int num); /* from flexcop-hw-filter.c */ int flexcop_pid_feed_control(struct flexcop_device *fc, diff --git a/drivers/media/common/b2c2/flexcop-misc.c b/drivers/media/common/b2c2/flexcop-misc.c index 83d01d3a81cc..251c4f731ed1 100644 --- a/drivers/media/common/b2c2/flexcop-misc.c +++ b/drivers/media/common/b2c2/flexcop-misc.c @@ -70,16 +70,3 @@ void flexcop_device_name(struct flexcop_device *fc, flexcop_bus_names[fc->bus_type], flexcop_revision_names[fc->rev], suffix); } - -void flexcop_dump_reg(struct flexcop_device *fc, - flexcop_ibi_register reg, int num) -{ - flexcop_ibi_value v; - int i; - for (i = 0; i < num; i++) { - v = fc->read_ibi_reg(fc, reg+4*i); - deb_rdump("0x%03x: %08x, ", reg+4*i, v.raw); - } - deb_rdump("\n"); -} -EXPORT_SYMBOL(flexcop_dump_reg); diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c index 9ce5f010de3f..6063782e937a 100644 --- a/drivers/media/dvb-core/dmxdev.c +++ b/drivers/media/dvb-core/dmxdev.c @@ -731,7 +731,7 @@ static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter) ret = (*secfeed)->allocate_filter(*secfeed, secfilter); if (ret < 0) { dvb_dmxdev_feed_restart(filter); - filter->feed.sec->start_filtering(*secfeed); + *secfeed = NULL; dprintk("could not get filter\n"); return ret; } diff --git a/drivers/media/dvb-frontends/cxd2841er.c b/drivers/media/dvb-frontends/cxd2841er.c index d925ca24183b..415f1f91cc30 100644 --- a/drivers/media/dvb-frontends/cxd2841er.c +++ b/drivers/media/dvb-frontends/cxd2841er.c @@ -311,12 +311,8 @@ static int cxd2841er_set_reg_bits(struct cxd2841er_priv *priv, static u32 cxd2841er_calc_iffreq_xtal(enum cxd2841er_xtal xtal, u32 ifhz) { - u64 tmp; - - tmp = (u64) ifhz * 16777216; - do_div(tmp, ((xtal == SONY_XTAL_24000) ? 48000000 : 41000000)); - - return (u32) tmp; + return div_u64(ifhz * 16777216ull, + (xtal == SONY_XTAL_24000) ? 48000000 : 41000000); } static u32 cxd2841er_calc_iffreq(u32 ifhz) diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index e1ae0f9fad43..2cdab2f3d9dc 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -3335,9 +3335,11 @@ static int ccs_probe(struct i2c_client *client) rval = request_firmware(&fw, filename, &client->dev); if (!rval) { - ccs_data_parse(&sensor->sdata, fw->data, fw->size, &client->dev, - true); + rval = ccs_data_parse(&sensor->sdata, fw->data, fw->size, + &client->dev, true); release_firmware(fw); + if (rval) + goto out_power_off; } if (!(ccsdev->flags & CCS_DEVICE_FLAG_IS_SMIA) || @@ -3351,9 +3353,11 @@ static int ccs_probe(struct i2c_client *client) rval = request_firmware(&fw, filename, &client->dev); if (!rval) { - ccs_data_parse(&sensor->mdata, fw->data, fw->size, - &client->dev, true); + rval = ccs_data_parse(&sensor->mdata, fw->data, + fw->size, &client->dev, true); release_firmware(fw); + if (rval) + goto out_release_sdata; } } @@ -3566,15 +3570,15 @@ out_disable_runtime_pm: out_cleanup: ccs_cleanup(sensor); +out_free_ccs_limits: + kfree(sensor->ccs_limits); + out_release_mdata: kvfree(sensor->mdata.backing); out_release_sdata: kvfree(sensor->sdata.backing); -out_free_ccs_limits: - kfree(sensor->ccs_limits); - out_power_off: ccs_power_off(&client->dev); mutex_destroy(&sensor->mutex); diff --git a/drivers/media/i2c/ccs/ccs-data.c b/drivers/media/i2c/ccs/ccs-data.c index 08400edf77ce..f469afcea680 100644 --- a/drivers/media/i2c/ccs/ccs-data.c +++ b/drivers/media/i2c/ccs/ccs-data.c @@ -10,6 +10,7 @@ #include <linux/limits.h> #include <linux/mm.h> #include <linux/slab.h> +#include <linux/string.h> #include "ccs-data-defs.h" @@ -97,7 +98,7 @@ ccs_data_parse_length_specifier(const struct __ccs_data_length_specifier *__len, plen = ((size_t) (__len3->length[0] & ((1 << CCS_DATA_LENGTH_SPECIFIER_SIZE_SHIFT) - 1)) - << 16) + (__len3->length[0] << 8) + __len3->length[1]; + << 16) + (__len3->length[1] << 8) + __len3->length[2]; break; } default: @@ -948,15 +949,15 @@ int ccs_data_parse(struct ccs_data_container *ccsdata, const void *data, rval = __ccs_data_parse(&bin, ccsdata, data, len, dev, verbose); if (rval) - return rval; + goto out_cleanup; rval = bin_backing_alloc(&bin); if (rval) - return rval; + goto out_cleanup; rval = __ccs_data_parse(&bin, ccsdata, data, len, dev, false); if (rval) - goto out_free; + goto out_cleanup; if (verbose && ccsdata->version) print_ccs_data_version(dev, ccsdata->version); @@ -965,15 +966,17 @@ int ccs_data_parse(struct ccs_data_container *ccsdata, const void *data, rval = -EPROTO; dev_dbg(dev, "parsing mismatch; base %p; now %p; end %p\n", bin.base, bin.now, bin.end); - goto out_free; + goto out_cleanup; } ccsdata->backing = bin.base; return 0; -out_free: +out_cleanup: kvfree(bin.base); + memset(ccsdata, 0, sizeof(*ccsdata)); + dev_warn(dev, "failed to parse CCS static data: %d\n", rval); return rval; } diff --git a/drivers/media/i2c/ds90ub913.c b/drivers/media/i2c/ds90ub913.c index 79bddfee2e2e..fd2d2d5272bf 100644 --- a/drivers/media/i2c/ds90ub913.c +++ b/drivers/media/i2c/ds90ub913.c @@ -8,6 +8,7 @@ * Copyright (c) 2023 Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> */ +#include <linux/bitfield.h> #include <linux/clk-provider.h> #include <linux/clk.h> #include <linux/delay.h> @@ -146,6 +147,19 @@ static int ub913_write(const struct ub913_data *priv, u8 reg, u8 val) return ret; } +static int ub913_update_bits(const struct ub913_data *priv, u8 reg, u8 mask, + u8 val) +{ + int ret; + + ret = regmap_update_bits(priv->regmap, reg, mask, val); + if (ret < 0) + dev_err(&priv->client->dev, + "Cannot update register 0x%02x %d!\n", reg, ret); + + return ret; +} + /* * GPIO chip */ @@ -733,10 +747,13 @@ static int ub913_hw_init(struct ub913_data *priv) if (ret) return dev_err_probe(dev, ret, "i2c master init failed\n"); - ub913_read(priv, UB913_REG_GENERAL_CFG, &v); - v &= ~UB913_REG_GENERAL_CFG_PCLK_RISING; - v |= priv->pclk_polarity_rising ? UB913_REG_GENERAL_CFG_PCLK_RISING : 0; - ub913_write(priv, UB913_REG_GENERAL_CFG, v); + ret = ub913_update_bits(priv, UB913_REG_GENERAL_CFG, + UB913_REG_GENERAL_CFG_PCLK_RISING, + FIELD_PREP(UB913_REG_GENERAL_CFG_PCLK_RISING, + priv->pclk_polarity_rising)); + + if (ret) + return ret; return 0; } @@ -793,7 +810,6 @@ static void ub913_subdev_uninit(struct ub913_data *priv) v4l2_async_unregister_subdev(&priv->sd); ub913_v4l2_nf_unregister(priv); v4l2_subdev_cleanup(&priv->sd); - fwnode_handle_put(priv->sd.fwnode); media_entity_cleanup(&priv->sd.entity); } diff --git a/drivers/media/i2c/ds90ub953.c b/drivers/media/i2c/ds90ub953.c index 725589b3e1c5..46569381b332 100644 --- a/drivers/media/i2c/ds90ub953.c +++ b/drivers/media/i2c/ds90ub953.c @@ -65,6 +65,9 @@ #define UB953_REG_GPIO_INPUT_CTRL_OUT_EN(n) BIT(4 + (n)) #define UB953_REG_GPIO_INPUT_CTRL_INPUT_EN(n) BIT(0 + (n)) +#define UB953_REG_BC_CTRL 0x49 +#define UB953_REG_BC_CTRL_CRC_ERR_CLR BIT(3) + #define UB953_REG_REV_MASK_ID 0x50 #define UB953_REG_GENERAL_STATUS 0x52 @@ -397,8 +400,13 @@ static int ub953_gpiochip_probe(struct ub953_data *priv) int ret; /* Set all GPIOs to local input mode */ - ub953_write(priv, UB953_REG_LOCAL_GPIO_DATA, 0); - ub953_write(priv, UB953_REG_GPIO_INPUT_CTRL, 0xf); + ret = ub953_write(priv, UB953_REG_LOCAL_GPIO_DATA, 0); + if (ret) + return ret; + + ret = ub953_write(priv, UB953_REG_GPIO_INPUT_CTRL, 0xf); + if (ret) + return ret; gc->label = dev_name(dev); gc->parent = dev; @@ -618,6 +626,12 @@ static int ub953_log_status(struct v4l2_subdev *sd) ub953_read(priv, UB953_REG_CRC_ERR_CNT2, &v2); dev_info(dev, "CRC error count %u\n", v1 | (v2 << 8)); + /* Clear CRC error counter */ + if (v1 || v2) + regmap_update_bits(priv->regmap, UB953_REG_BC_CTRL, + UB953_REG_BC_CTRL_CRC_ERR_CLR, + UB953_REG_BC_CTRL_CRC_ERR_CLR); + ub953_read(priv, UB953_REG_CSI_ERR_CNT, &v); dev_info(dev, "CSI error count %u\n", v); @@ -958,10 +972,11 @@ static void ub953_calc_clkout_params(struct ub953_data *priv, clkout_data->rate = clkout_rate; } -static void ub953_write_clkout_regs(struct ub953_data *priv, - const struct ub953_clkout_data *clkout_data) +static int ub953_write_clkout_regs(struct ub953_data *priv, + const struct ub953_clkout_data *clkout_data) { u8 clkout_ctrl0, clkout_ctrl1; + int ret; if (priv->hw_data->is_ub971) clkout_ctrl0 = clkout_data->m; @@ -971,8 +986,15 @@ static void ub953_write_clkout_regs(struct ub953_data *priv, clkout_ctrl1 = clkout_data->n; - ub953_write(priv, UB953_REG_CLKOUT_CTRL0, clkout_ctrl0); - ub953_write(priv, UB953_REG_CLKOUT_CTRL1, clkout_ctrl1); + ret = ub953_write(priv, UB953_REG_CLKOUT_CTRL0, clkout_ctrl0); + if (ret) + return ret; + + ret = ub953_write(priv, UB953_REG_CLKOUT_CTRL1, clkout_ctrl1); + if (ret) + return ret; + + return 0; } static unsigned long ub953_clkout_recalc_rate(struct clk_hw *hw, @@ -1052,9 +1074,7 @@ static int ub953_clkout_set_rate(struct clk_hw *hw, unsigned long rate, dev_dbg(&priv->client->dev, "%s %lu (requested %lu)\n", __func__, clkout_data.rate, rate); - ub953_write_clkout_regs(priv, &clkout_data); - - return 0; + return ub953_write_clkout_regs(priv, &clkout_data); } static const struct clk_ops ub953_clkout_ops = { @@ -1079,7 +1099,9 @@ static int ub953_register_clkout(struct ub953_data *priv) /* Initialize clkout to 25MHz by default */ ub953_calc_clkout_params(priv, UB953_DEFAULT_CLKOUT_RATE, &clkout_data); - ub953_write_clkout_regs(priv, &clkout_data); + ret = ub953_write_clkout_regs(priv, &clkout_data); + if (ret) + return ret; priv->clkout_clk_hw.init = &init; @@ -1226,10 +1248,15 @@ static int ub953_hw_init(struct ub953_data *priv) if (ret) return dev_err_probe(dev, ret, "i2c init failed\n"); - ub953_write(priv, UB953_REG_GENERAL_CFG, - (priv->non_continous_clk ? 0 : UB953_REG_GENERAL_CFG_CONT_CLK) | - ((priv->num_data_lanes - 1) << UB953_REG_GENERAL_CFG_CSI_LANE_SEL_SHIFT) | - UB953_REG_GENERAL_CFG_CRC_TX_GEN_ENABLE); + v = 0; + v |= priv->non_continous_clk ? 0 : UB953_REG_GENERAL_CFG_CONT_CLK; + v |= (priv->num_data_lanes - 1) << + UB953_REG_GENERAL_CFG_CSI_LANE_SEL_SHIFT; + v |= UB953_REG_GENERAL_CFG_CRC_TX_GEN_ENABLE; + + ret = ub953_write(priv, UB953_REG_GENERAL_CFG, v); + if (ret) + return ret; return 0; } @@ -1288,7 +1315,6 @@ static void ub953_subdev_uninit(struct ub953_data *priv) v4l2_async_unregister_subdev(&priv->sd); ub953_v4l2_notifier_unregister(priv); v4l2_subdev_cleanup(&priv->sd); - fwnode_handle_put(priv->sd.fwnode); media_entity_cleanup(&priv->sd.entity); } diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c index 1b1ff7f7505b..5dde8452739b 100644 --- a/drivers/media/i2c/ds90ub960.c +++ b/drivers/media/i2c/ds90ub960.c @@ -43,6 +43,7 @@ #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> +#include <linux/units.h> #include <linux/workqueue.h> #include <media/i2c/ds90ub9xx.h> @@ -51,7 +52,16 @@ #include <media/v4l2-fwnode.h> #include <media/v4l2-subdev.h> -#define MHZ(v) ((u32)((v) * 1000000U)) +#define MHZ(v) ((u32)((v) * HZ_PER_MHZ)) + +/* + * If this is defined, the i2c addresses from UB960_DEBUG_I2C_RX_ID to + * UB960_DEBUG_I2C_RX_ID + 3 can be used to access the paged RX port registers + * directly. + * + * Only for debug purposes. + */ +/* #define UB960_DEBUG_I2C_RX_ID 0x40 */ #define UB960_POLL_TIME_MS 500 @@ -349,12 +359,13 @@ #define UB960_SR_FPD3_RX_ID(n) (0xf0 + (n)) #define UB960_SR_FPD3_RX_ID_LEN 6 -#define UB960_SR_I2C_RX_ID(n) (0xf8 + (n)) /* < UB960_FPD_RX_NPORTS */ +#define UB960_SR_I2C_RX_ID(n) (0xf8 + (n)) + +#define UB9702_SR_REFCLK_FREQ 0x3d /* Indirect register blocks */ #define UB960_IND_TARGET_PAT_GEN 0x00 #define UB960_IND_TARGET_RX_ANA(n) (0x01 + (n)) -#define UB960_IND_TARGET_CSI_CSIPLL_REG_1 0x92 /* UB9702 */ #define UB960_IND_TARGET_CSI_ANA 0x07 /* UB960_IR_PGEN_*: Indirect Registers for Test Pattern Generator */ @@ -568,11 +579,23 @@ struct ub960_format_info { }; static const struct ub960_format_info ub960_formats[] = { + { .code = MEDIA_BUS_FMT_RGB888_1X24, .bpp = 24, .datatype = MIPI_CSI2_DT_RGB888, }, + { .code = MEDIA_BUS_FMT_YUYV8_1X16, .bpp = 16, .datatype = MIPI_CSI2_DT_YUV422_8B, }, { .code = MEDIA_BUS_FMT_UYVY8_1X16, .bpp = 16, .datatype = MIPI_CSI2_DT_YUV422_8B, }, { .code = MEDIA_BUS_FMT_VYUY8_1X16, .bpp = 16, .datatype = MIPI_CSI2_DT_YUV422_8B, }, { .code = MEDIA_BUS_FMT_YVYU8_1X16, .bpp = 16, .datatype = MIPI_CSI2_DT_YUV422_8B, }, + { .code = MEDIA_BUS_FMT_SBGGR8_1X8, .bpp = 8, .datatype = MIPI_CSI2_DT_RAW8, }, + { .code = MEDIA_BUS_FMT_SGBRG8_1X8, .bpp = 8, .datatype = MIPI_CSI2_DT_RAW8, }, + { .code = MEDIA_BUS_FMT_SGRBG8_1X8, .bpp = 8, .datatype = MIPI_CSI2_DT_RAW8, }, + { .code = MEDIA_BUS_FMT_SRGGB8_1X8, .bpp = 8, .datatype = MIPI_CSI2_DT_RAW8, }, + + { .code = MEDIA_BUS_FMT_SBGGR10_1X10, .bpp = 10, .datatype = MIPI_CSI2_DT_RAW10, }, + { .code = MEDIA_BUS_FMT_SGBRG10_1X10, .bpp = 10, .datatype = MIPI_CSI2_DT_RAW10, }, + { .code = MEDIA_BUS_FMT_SGRBG10_1X10, .bpp = 10, .datatype = MIPI_CSI2_DT_RAW10, }, + { .code = MEDIA_BUS_FMT_SRGGB10_1X10, .bpp = 10, .datatype = MIPI_CSI2_DT_RAW10, }, + { .code = MEDIA_BUS_FMT_SBGGR12_1X12, .bpp = 12, .datatype = MIPI_CSI2_DT_RAW12, }, { .code = MEDIA_BUS_FMT_SGBRG12_1X12, .bpp = 12, .datatype = MIPI_CSI2_DT_RAW12, }, { .code = MEDIA_BUS_FMT_SGRBG12_1X12, .bpp = 12, .datatype = MIPI_CSI2_DT_RAW12, }, @@ -1552,7 +1575,12 @@ static int ub960_rxport_wait_locks(struct ub960_data *priv, if (missing == 0) break; - msleep(50); + /* + * The sleep time of 10 ms was found by testing to give a lock + * with a few iterations. It can be decreased if on some setups + * the lock can be achieved much faster. + */ + fsleep(10 * USEC_PER_MSEC); } if (lock_mask) @@ -1574,16 +1602,24 @@ static int ub960_rxport_wait_locks(struct ub960_data *priv, ub960_rxport_read16(priv, nport, UB960_RR_RX_FREQ_HIGH, &v); - ret = ub960_rxport_get_strobe_pos(priv, nport, &strobe_pos); - if (ret) - return ret; + if (priv->hw_data->is_ub9702) { + dev_dbg(dev, "\trx%u: locked, freq %llu Hz\n", + nport, ((u64)v * HZ_PER_MHZ) >> 8); + } else { + ret = ub960_rxport_get_strobe_pos(priv, nport, + &strobe_pos); + if (ret) + return ret; - ret = ub960_rxport_get_eq_level(priv, nport, &eq_level); - if (ret) - return ret; + ret = ub960_rxport_get_eq_level(priv, nport, &eq_level); + if (ret) + return ret; - dev_dbg(dev, "\trx%u: locked, SP: %d, EQ: %u, freq %llu Hz\n", - nport, strobe_pos, eq_level, (v * 1000000ULL) >> 8); + dev_dbg(dev, + "\trx%u: locked, SP: %d, EQ: %u, freq %llu Hz\n", + nport, strobe_pos, eq_level, + ((u64)v * HZ_PER_MHZ) >> 8); + } } return 0; @@ -2412,7 +2448,6 @@ static int ub960_configure_ports_for_streaming(struct ub960_data *priv, } rx_data[UB960_MAX_RX_NPORTS] = {}; u8 vc_map[UB960_MAX_RX_NPORTS] = {}; struct v4l2_subdev_route *route; - unsigned int nport; int ret; ret = ub960_validate_stream_vcs(priv); @@ -2482,7 +2517,8 @@ static int ub960_configure_ports_for_streaming(struct ub960_data *priv, */ fwd_ctl = GENMASK(7, 4); - for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + for (unsigned int nport = 0; nport < priv->hw_data->num_rxports; + nport++) { struct ub960_rxport *rxport = priv->rxports[nport]; u8 vc = vc_map[nport]; @@ -2522,7 +2558,7 @@ static int ub960_configure_ports_for_streaming(struct ub960_data *priv, for (i = 0; i < 8; i++) ub960_rxport_write(priv, nport, UB960_RR_VC_ID_MAP(i), - nport); + (nport << 4) | nport); } break; @@ -2939,20 +2975,78 @@ static const struct v4l2_subdev_pad_ops ub960_pad_ops = { .set_fmt = ub960_set_fmt, }; +static void ub960_log_status_ub960_sp_eq(struct ub960_data *priv, + unsigned int nport) +{ + struct device *dev = &priv->client->dev; + u8 eq_level; + s8 strobe_pos; + int ret; + u8 v; + + /* Strobe */ + + ret = ub960_read(priv, UB960_XR_AEQ_CTL1, &v); + if (ret) + return; + + dev_info(dev, "\t%s strobe\n", + (v & UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN) ? "Adaptive" : + "Manual"); + + if (v & UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN) { + ret = ub960_read(priv, UB960_XR_SFILTER_CFG, &v); + if (ret) + return; + + dev_info(dev, "\tStrobe range [%d, %d]\n", + ((v >> UB960_XR_SFILTER_CFG_SFILTER_MIN_SHIFT) & 0xf) - 7, + ((v >> UB960_XR_SFILTER_CFG_SFILTER_MAX_SHIFT) & 0xf) - 7); + } + + ret = ub960_rxport_get_strobe_pos(priv, nport, &strobe_pos); + if (ret) + return; + + dev_info(dev, "\tStrobe pos %d\n", strobe_pos); + + /* EQ */ + + ret = ub960_rxport_read(priv, nport, UB960_RR_AEQ_BYPASS, &v); + if (ret) + return; + + dev_info(dev, "\t%s EQ\n", + (v & UB960_RR_AEQ_BYPASS_ENABLE) ? "Manual" : + "Adaptive"); + + if (!(v & UB960_RR_AEQ_BYPASS_ENABLE)) { + ret = ub960_rxport_read(priv, nport, UB960_RR_AEQ_MIN_MAX, &v); + if (ret) + return; + + dev_info(dev, "\tEQ range [%u, %u]\n", + (v >> UB960_RR_AEQ_MIN_MAX_AEQ_FLOOR_SHIFT) & 0xf, + (v >> UB960_RR_AEQ_MIN_MAX_AEQ_MAX_SHIFT) & 0xf); + } + + if (ub960_rxport_get_eq_level(priv, nport, &eq_level) == 0) + dev_info(dev, "\tEQ level %u\n", eq_level); +} + static int ub960_log_status(struct v4l2_subdev *sd) { struct ub960_data *priv = sd_to_ub960(sd); struct device *dev = &priv->client->dev; struct v4l2_subdev_state *state; unsigned int nport; - unsigned int i; u16 v16 = 0; u8 v = 0; u8 id[UB960_SR_FPD3_RX_ID_LEN]; state = v4l2_subdev_lock_and_get_active_state(sd); - for (i = 0; i < sizeof(id); i++) + for (unsigned int i = 0; i < sizeof(id); i++) ub960_read(priv, UB960_SR_FPD3_RX_ID(i), &id[i]); dev_info(dev, "ID '%.*s'\n", (int)sizeof(id), id); @@ -2986,9 +3080,6 @@ static int ub960_log_status(struct v4l2_subdev *sd) for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { struct ub960_rxport *rxport = priv->rxports[nport]; - u8 eq_level; - s8 strobe_pos; - unsigned int i; dev_info(dev, "RX %u\n", nport); @@ -3009,7 +3100,7 @@ static int ub960_log_status(struct v4l2_subdev *sd) dev_info(dev, "\trx_port_sts2 %#02x\n", v); ub960_rxport_read16(priv, nport, UB960_RR_RX_FREQ_HIGH, &v16); - dev_info(dev, "\tlink freq %llu Hz\n", (v16 * 1000000ULL) >> 8); + dev_info(dev, "\tlink freq %llu Hz\n", ((u64)v16 * HZ_PER_MHZ) >> 8); ub960_rxport_read16(priv, nport, UB960_RR_RX_PAR_ERR_HI, &v16); dev_info(dev, "\tparity errors %u\n", v16); @@ -3023,47 +3114,11 @@ static int ub960_log_status(struct v4l2_subdev *sd) ub960_rxport_read(priv, nport, UB960_RR_CSI_ERR_COUNTER, &v); dev_info(dev, "\tcsi_err_counter %u\n", v); - /* Strobe */ - - ub960_read(priv, UB960_XR_AEQ_CTL1, &v); - - dev_info(dev, "\t%s strobe\n", - (v & UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN) ? "Adaptive" : - "Manual"); - - if (v & UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN) { - ub960_read(priv, UB960_XR_SFILTER_CFG, &v); - - dev_info(dev, "\tStrobe range [%d, %d]\n", - ((v >> UB960_XR_SFILTER_CFG_SFILTER_MIN_SHIFT) & 0xf) - 7, - ((v >> UB960_XR_SFILTER_CFG_SFILTER_MAX_SHIFT) & 0xf) - 7); - } - - ub960_rxport_get_strobe_pos(priv, nport, &strobe_pos); - - dev_info(dev, "\tStrobe pos %d\n", strobe_pos); - - /* EQ */ - - ub960_rxport_read(priv, nport, UB960_RR_AEQ_BYPASS, &v); - - dev_info(dev, "\t%s EQ\n", - (v & UB960_RR_AEQ_BYPASS_ENABLE) ? "Manual" : - "Adaptive"); - - if (!(v & UB960_RR_AEQ_BYPASS_ENABLE)) { - ub960_rxport_read(priv, nport, UB960_RR_AEQ_MIN_MAX, &v); - - dev_info(dev, "\tEQ range [%u, %u]\n", - (v >> UB960_RR_AEQ_MIN_MAX_AEQ_FLOOR_SHIFT) & 0xf, - (v >> UB960_RR_AEQ_MIN_MAX_AEQ_MAX_SHIFT) & 0xf); - } - - if (ub960_rxport_get_eq_level(priv, nport, &eq_level) == 0) - dev_info(dev, "\tEQ level %u\n", eq_level); + if (!priv->hw_data->is_ub9702) + ub960_log_status_ub960_sp_eq(priv, nport); /* GPIOs */ - for (i = 0; i < UB960_NUM_BC_GPIOS; i++) { + for (unsigned int i = 0; i < UB960_NUM_BC_GPIOS; i++) { u8 ctl_reg; u8 ctl_shift; @@ -3834,13 +3889,16 @@ static int ub960_enable_core_hw(struct ub960_data *priv) if (ret) goto err_pd_gpio; - ret = ub960_read(priv, UB960_XR_REFCLK_FREQ, &refclk_freq); + if (priv->hw_data->is_ub9702) + ret = ub960_read(priv, UB9702_SR_REFCLK_FREQ, &refclk_freq); + else + ret = ub960_read(priv, UB960_XR_REFCLK_FREQ, &refclk_freq); if (ret) goto err_pd_gpio; dev_dbg(dev, "refclk valid %u freq %u MHz (clk fw freq %lu MHz)\n", !!(dev_sts & BIT(4)), refclk_freq, - clk_get_rate(priv->refclk) / 1000000); + clk_get_rate(priv->refclk) / HZ_PER_MHZ); /* Disable all RX ports by default */ ret = ub960_write(priv, UB960_SR_RX_PORT_CTL, 0); @@ -3974,6 +4032,12 @@ static int ub960_probe(struct i2c_client *client) schedule_delayed_work(&priv->poll_work, msecs_to_jiffies(UB960_POLL_TIME_MS)); +#ifdef UB960_DEBUG_I2C_RX_ID + for (unsigned int i = 0; i < priv->hw_data->num_rxports; i++) + ub960_write(priv, UB960_SR_I2C_RX_ID(i), + (UB960_DEBUG_I2C_RX_ID + i) << 1); +#endif + return 0; err_free_sers: diff --git a/drivers/media/i2c/imx208.c b/drivers/media/i2c/imx208.c index 2184c90f7864..2b5a6ce7b1ae 100644 --- a/drivers/media/i2c/imx208.c +++ b/drivers/media/i2c/imx208.c @@ -814,7 +814,7 @@ out_unlock: } static ssize_t otp_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct i2c_client *client = to_i2c_client(kobj_to_dev(kobj)); diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index f5ee6bd3b52d..fbf7eba3d71d 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -170,12 +170,15 @@ enum imx290_model { IMX290_MODEL_IMX290LQR, IMX290_MODEL_IMX290LLR, IMX290_MODEL_IMX327LQR, + IMX290_MODEL_IMX462LQR, + IMX290_MODEL_IMX462LLR, }; struct imx290_model_info { enum imx290_colour_variant colour_variant; const struct cci_reg_sequence *init_regs; size_t init_regs_num; + unsigned int max_analog_gain; const char *name; }; @@ -267,7 +270,6 @@ static const struct cci_reg_sequence imx290_global_init_settings[] = { { IMX290_WINWV, 1097 }, { IMX290_XSOUTSEL, IMX290_XSOUTSEL_XVSOUTSEL_VSYNC | IMX290_XSOUTSEL_XHSOUTSEL_HSYNC }, - { CCI_REG8(0x3011), 0x02 }, { CCI_REG8(0x3012), 0x64 }, { CCI_REG8(0x3013), 0x00 }, }; @@ -275,6 +277,51 @@ static const struct cci_reg_sequence imx290_global_init_settings[] = { static const struct cci_reg_sequence imx290_global_init_settings_290[] = { { CCI_REG8(0x300f), 0x00 }, { CCI_REG8(0x3010), 0x21 }, + { CCI_REG8(0x3011), 0x00 }, + { CCI_REG8(0x3016), 0x09 }, + { CCI_REG8(0x3070), 0x02 }, + { CCI_REG8(0x3071), 0x11 }, + { CCI_REG8(0x309b), 0x10 }, + { CCI_REG8(0x309c), 0x22 }, + { CCI_REG8(0x30a2), 0x02 }, + { CCI_REG8(0x30a6), 0x20 }, + { CCI_REG8(0x30a8), 0x20 }, + { CCI_REG8(0x30aa), 0x20 }, + { CCI_REG8(0x30ac), 0x20 }, + { CCI_REG8(0x30b0), 0x43 }, + { CCI_REG8(0x3119), 0x9e }, + { CCI_REG8(0x311c), 0x1e }, + { CCI_REG8(0x311e), 0x08 }, + { CCI_REG8(0x3128), 0x05 }, + { CCI_REG8(0x313d), 0x83 }, + { CCI_REG8(0x3150), 0x03 }, + { CCI_REG8(0x317e), 0x00 }, + { CCI_REG8(0x32b8), 0x50 }, + { CCI_REG8(0x32b9), 0x10 }, + { CCI_REG8(0x32ba), 0x00 }, + { CCI_REG8(0x32bb), 0x04 }, + { CCI_REG8(0x32c8), 0x50 }, + { CCI_REG8(0x32c9), 0x10 }, + { CCI_REG8(0x32ca), 0x00 }, + { CCI_REG8(0x32cb), 0x04 }, + { CCI_REG8(0x332c), 0xd3 }, + { CCI_REG8(0x332d), 0x10 }, + { CCI_REG8(0x332e), 0x0d }, + { CCI_REG8(0x3358), 0x06 }, + { CCI_REG8(0x3359), 0xe1 }, + { CCI_REG8(0x335a), 0x11 }, + { CCI_REG8(0x3360), 0x1e }, + { CCI_REG8(0x3361), 0x61 }, + { CCI_REG8(0x3362), 0x10 }, + { CCI_REG8(0x33b0), 0x50 }, + { CCI_REG8(0x33b2), 0x1a }, + { CCI_REG8(0x33b3), 0x04 }, +}; + +static const struct cci_reg_sequence imx290_global_init_settings_462[] = { + { CCI_REG8(0x300f), 0x00 }, + { CCI_REG8(0x3010), 0x21 }, + { CCI_REG8(0x3011), 0x02 }, { CCI_REG8(0x3016), 0x09 }, { CCI_REG8(0x3070), 0x02 }, { CCI_REG8(0x3071), 0x11 }, @@ -328,6 +375,7 @@ static const struct cci_reg_sequence xclk_regs[][IMX290_NUM_CLK_REGS] = { }; static const struct cci_reg_sequence imx290_global_init_settings_327[] = { + { CCI_REG8(0x3011), 0x02 }, { CCI_REG8(0x309e), 0x4A }, { CCI_REG8(0x309f), 0x4A }, { CCI_REG8(0x313b), 0x61 }, @@ -876,14 +924,10 @@ static int imx290_ctrl_init(struct imx290 *imx290) * up to 72.0dB (240) add further digital gain. Limit the range to * analog gain only, support for digital gain can be added separately * if needed. - * - * The IMX327 and IMX462 are largely compatible with the IMX290, but - * have an analog gain range of 0.0dB to 29.4dB and 42dB of digital - * gain. When support for those sensors gets added to the driver, the - * gain control should be adjusted accordingly. */ v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, - V4L2_CID_ANALOGUE_GAIN, 0, 100, 1, 0); + V4L2_CID_ANALOGUE_GAIN, 0, + imx290->model->max_analog_gain, 1, 0); /* * Correct range will be determined through imx290_ctrl_update setting @@ -1441,20 +1485,37 @@ static const struct imx290_model_info imx290_models[] = { .colour_variant = IMX290_VARIANT_COLOUR, .init_regs = imx290_global_init_settings_290, .init_regs_num = ARRAY_SIZE(imx290_global_init_settings_290), + .max_analog_gain = 100, .name = "imx290", }, [IMX290_MODEL_IMX290LLR] = { .colour_variant = IMX290_VARIANT_MONO, .init_regs = imx290_global_init_settings_290, .init_regs_num = ARRAY_SIZE(imx290_global_init_settings_290), + .max_analog_gain = 100, .name = "imx290", }, [IMX290_MODEL_IMX327LQR] = { .colour_variant = IMX290_VARIANT_COLOUR, .init_regs = imx290_global_init_settings_327, .init_regs_num = ARRAY_SIZE(imx290_global_init_settings_327), + .max_analog_gain = 98, .name = "imx327", }, + [IMX290_MODEL_IMX462LQR] = { + .colour_variant = IMX290_VARIANT_COLOUR, + .init_regs = imx290_global_init_settings_462, + .init_regs_num = ARRAY_SIZE(imx290_global_init_settings_462), + .max_analog_gain = 98, + .name = "imx462", + }, + [IMX290_MODEL_IMX462LLR] = { + .colour_variant = IMX290_VARIANT_MONO, + .init_regs = imx290_global_init_settings_462, + .init_regs_num = ARRAY_SIZE(imx290_global_init_settings_462), + .max_analog_gain = 98, + .name = "imx462", + }, }; static int imx290_parse_dt(struct imx290 *imx290) @@ -1653,6 +1714,12 @@ static const struct of_device_id imx290_of_match[] = { }, { .compatible = "sony,imx327lqr", .data = &imx290_models[IMX290_MODEL_IMX327LQR], + }, { + .compatible = "sony,imx462lqr", + .data = &imx290_models[IMX290_MODEL_IMX462LQR], + }, { + .compatible = "sony,imx462llr", + .data = &imx290_models[IMX290_MODEL_IMX462LLR], }, { /* sentinel */ }, }; diff --git a/drivers/media/i2c/imx296.c b/drivers/media/i2c/imx296.c index 83149fa729c4..f3bec16b527c 100644 --- a/drivers/media/i2c/imx296.c +++ b/drivers/media/i2c/imx296.c @@ -954,6 +954,8 @@ static int imx296_identify_model(struct imx296 *sensor) return ret; } + usleep_range(2000, 5000); + ret = imx296_read(sensor, IMX296_SENSOR_INFO); if (ret < 0) { dev_err(sensor->dev, "failed to read sensor information (%d)\n", diff --git a/drivers/media/i2c/imx412.c b/drivers/media/i2c/imx412.c index 0bfe3046fcc8..c74097a59c42 100644 --- a/drivers/media/i2c/imx412.c +++ b/drivers/media/i2c/imx412.c @@ -547,7 +547,7 @@ static int imx412_update_exp_gain(struct imx412 *imx412, u32 exposure, u32 gain) lpfr = imx412->vblank + imx412->cur_mode->height; - dev_dbg(imx412->dev, "Set exp %u, analog gain %u, lpfr %u", + dev_dbg(imx412->dev, "Set exp %u, analog gain %u, lpfr %u\n", exposure, gain, lpfr); ret = imx412_write_reg(imx412, IMX412_REG_HOLD, 1, 1); @@ -594,7 +594,7 @@ static int imx412_set_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_VBLANK: imx412->vblank = imx412->vblank_ctrl->val; - dev_dbg(imx412->dev, "Received vblank %u, new lpfr %u", + dev_dbg(imx412->dev, "Received vblank %u, new lpfr %u\n", imx412->vblank, imx412->vblank + imx412->cur_mode->height); @@ -613,7 +613,7 @@ static int imx412_set_ctrl(struct v4l2_ctrl *ctrl) exposure = ctrl->val; analog_gain = imx412->again_ctrl->val; - dev_dbg(imx412->dev, "Received exp %u, analog gain %u", + dev_dbg(imx412->dev, "Received exp %u, analog gain %u\n", exposure, analog_gain); ret = imx412_update_exp_gain(imx412, exposure, analog_gain); @@ -622,7 +622,7 @@ static int imx412_set_ctrl(struct v4l2_ctrl *ctrl) break; default: - dev_err(imx412->dev, "Invalid control %d", ctrl->id); + dev_err(imx412->dev, "Invalid control %d\n", ctrl->id); ret = -EINVAL; } @@ -803,14 +803,14 @@ static int imx412_start_streaming(struct imx412 *imx412) ret = imx412_write_regs(imx412, reg_list->regs, reg_list->num_of_regs); if (ret) { - dev_err(imx412->dev, "fail to write initial registers"); + dev_err(imx412->dev, "fail to write initial registers\n"); return ret; } /* Setup handler will write actual exposure and gain */ ret = __v4l2_ctrl_handler_setup(imx412->sd.ctrl_handler); if (ret) { - dev_err(imx412->dev, "fail to setup handler"); + dev_err(imx412->dev, "fail to setup handler\n"); return ret; } @@ -821,7 +821,7 @@ static int imx412_start_streaming(struct imx412 *imx412) ret = imx412_write_reg(imx412, IMX412_REG_MODE_SELECT, 1, IMX412_MODE_STREAMING); if (ret) { - dev_err(imx412->dev, "fail to start streaming"); + dev_err(imx412->dev, "fail to start streaming\n"); return ret; } @@ -895,7 +895,7 @@ static int imx412_detect(struct imx412 *imx412) return ret; if (val != IMX412_ID) { - dev_err(imx412->dev, "chip id mismatch: %x!=%x", + dev_err(imx412->dev, "chip id mismatch: %x!=%x\n", IMX412_ID, val); return -ENXIO; } @@ -927,7 +927,7 @@ static int imx412_parse_hw_config(struct imx412 *imx412) imx412->reset_gpio = devm_gpiod_get_optional(imx412->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(imx412->reset_gpio)) { - dev_err(imx412->dev, "failed to get reset gpio %ld", + dev_err(imx412->dev, "failed to get reset gpio %ld\n", PTR_ERR(imx412->reset_gpio)); return PTR_ERR(imx412->reset_gpio); } @@ -935,13 +935,13 @@ static int imx412_parse_hw_config(struct imx412 *imx412) /* Get sensor input clock */ imx412->inclk = devm_clk_get(imx412->dev, NULL); if (IS_ERR(imx412->inclk)) { - dev_err(imx412->dev, "could not get inclk"); + dev_err(imx412->dev, "could not get inclk\n"); return PTR_ERR(imx412->inclk); } rate = clk_get_rate(imx412->inclk); if (rate != IMX412_INCLK_RATE) { - dev_err(imx412->dev, "inclk frequency mismatch"); + dev_err(imx412->dev, "inclk frequency mismatch\n"); return -EINVAL; } @@ -966,14 +966,14 @@ static int imx412_parse_hw_config(struct imx412 *imx412) if (bus_cfg.bus.mipi_csi2.num_data_lanes != IMX412_NUM_DATA_LANES) { dev_err(imx412->dev, - "number of CSI2 data lanes %d is not supported", + "number of CSI2 data lanes %d is not supported\n", bus_cfg.bus.mipi_csi2.num_data_lanes); ret = -EINVAL; goto done_endpoint_free; } if (!bus_cfg.nr_of_link_frequencies) { - dev_err(imx412->dev, "no link frequencies defined"); + dev_err(imx412->dev, "no link frequencies defined\n"); ret = -EINVAL; goto done_endpoint_free; } @@ -1034,7 +1034,7 @@ static int imx412_power_on(struct device *dev) ret = clk_prepare_enable(imx412->inclk); if (ret) { - dev_err(imx412->dev, "fail to enable inclk"); + dev_err(imx412->dev, "fail to enable inclk\n"); goto error_reset; } @@ -1145,7 +1145,7 @@ static int imx412_init_controls(struct imx412 *imx412) imx412->hblank_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; if (ctrl_hdlr->error) { - dev_err(imx412->dev, "control init failed: %d", + dev_err(imx412->dev, "control init failed: %d\n", ctrl_hdlr->error); v4l2_ctrl_handler_free(ctrl_hdlr); return ctrl_hdlr->error; @@ -1183,7 +1183,7 @@ static int imx412_probe(struct i2c_client *client) ret = imx412_parse_hw_config(imx412); if (ret) { - dev_err(imx412->dev, "HW configuration is not supported"); + dev_err(imx412->dev, "HW configuration is not supported\n"); return ret; } @@ -1191,14 +1191,14 @@ static int imx412_probe(struct i2c_client *client) ret = imx412_power_on(imx412->dev); if (ret) { - dev_err(imx412->dev, "failed to power-on the sensor"); + dev_err(imx412->dev, "failed to power-on the sensor\n"); goto error_mutex_destroy; } /* Check module identity */ ret = imx412_detect(imx412); if (ret) { - dev_err(imx412->dev, "failed to find sensor: %d", ret); + dev_err(imx412->dev, "failed to find sensor: %d\n", ret); goto error_power_off; } @@ -1208,7 +1208,7 @@ static int imx412_probe(struct i2c_client *client) ret = imx412_init_controls(imx412); if (ret) { - dev_err(imx412->dev, "failed to init controls: %d", ret); + dev_err(imx412->dev, "failed to init controls: %d\n", ret); goto error_power_off; } @@ -1222,14 +1222,14 @@ static int imx412_probe(struct i2c_client *client) imx412->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&imx412->sd.entity, 1, &imx412->pad); if (ret) { - dev_err(imx412->dev, "failed to init entity pads: %d", ret); + dev_err(imx412->dev, "failed to init entity pads: %d\n", ret); goto error_handler_free; } ret = v4l2_async_register_subdev_sensor(&imx412->sd); if (ret < 0) { dev_err(imx412->dev, - "failed to register async subdev: %d", ret); + "failed to register async subdev: %d\n", ret); goto error_media_entity; } diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c index c484b753a718..9a5d118b87b0 100644 --- a/drivers/media/i2c/ov2740.c +++ b/drivers/media/i2c/ov2740.c @@ -11,6 +11,7 @@ #include <linux/pm_runtime.h> #include <linux/nvmem-provider.h> #include <linux/regmap.h> +#include <linux/regulator/consumer.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-fwnode.h> @@ -76,6 +77,14 @@ /* OTP registers from sensor */ #define OV2740_REG_OTP_CUSTOMER 0x7010 +static const char * const ov2740_supply_name[] = { + "AVDD", + "DOVDD", + "DVDD", +}; + +#define OV2740_NUM_SUPPLIES ARRAY_SIZE(ov2740_supply_name) + struct nvm_data { struct nvmem_device *nvmem; struct regmap *regmap; @@ -523,9 +532,11 @@ struct ov2740 { struct v4l2_ctrl *hblank; struct v4l2_ctrl *exposure; - /* GPIOs, clocks */ + /* GPIOs, clocks, regulators */ struct gpio_desc *reset_gpio; + struct gpio_desc *powerdown_gpio; struct clk *clk; + struct regulator_bulk_data supplies[OV2740_NUM_SUPPLIES]; /* Current mode */ const struct ov2740_mode *cur_mode; @@ -644,6 +655,8 @@ static int ov2740_identify_module(struct ov2740 *ov2740) return -ENXIO; } + dev_dbg(&client->dev, "chip id: %x\n", val); + ov2740->identified = true; return 0; @@ -753,15 +766,17 @@ static const struct v4l2_ctrl_ops ov2740_ctrl_ops = { static int ov2740_init_controls(struct ov2740 *ov2740) { + struct i2c_client *client = v4l2_get_subdevdata(&ov2740->sd); struct v4l2_ctrl_handler *ctrl_hdlr; const struct ov2740_mode *cur_mode; s64 exposure_max, h_blank, pixel_rate; u32 vblank_min, vblank_max, vblank_default; + struct v4l2_fwnode_device_properties props; int size; int ret; ctrl_hdlr = &ov2740->ctrl_handler; - ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8); + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10); if (ret) return ret; @@ -811,6 +826,13 @@ static int ov2740_init_controls(struct ov2740 *ov2740) V4L2_CID_TEST_PATTERN, ARRAY_SIZE(ov2740_test_pattern_menu) - 1, 0, 0, ov2740_test_pattern_menu); + + ret = v4l2_fwnode_device_parse(&client->dev, &props); + if (ret) + return ret; + + v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &ov2740_ctrl_ops, &props); + if (ctrl_hdlr->error) { v4l2_ctrl_handler_free(ctrl_hdlr); return ctrl_hdlr->error; @@ -1295,7 +1317,9 @@ static int ov2740_suspend(struct device *dev) struct ov2740 *ov2740 = to_ov2740(sd); gpiod_set_value_cansleep(ov2740->reset_gpio, 1); + gpiod_set_value_cansleep(ov2740->powerdown_gpio, 1); clk_disable_unprepare(ov2740->clk); + regulator_bulk_disable(OV2740_NUM_SUPPLIES, ov2740->supplies); return 0; } @@ -1305,10 +1329,17 @@ static int ov2740_resume(struct device *dev) struct ov2740 *ov2740 = to_ov2740(sd); int ret; - ret = clk_prepare_enable(ov2740->clk); + ret = regulator_bulk_enable(OV2740_NUM_SUPPLIES, ov2740->supplies); if (ret) return ret; + ret = clk_prepare_enable(ov2740->clk); + if (ret) { + regulator_bulk_disable(OV2740_NUM_SUPPLIES, ov2740->supplies); + return ret; + } + + gpiod_set_value_cansleep(ov2740->powerdown_gpio, 0); gpiod_set_value_cansleep(ov2740->reset_gpio, 0); msleep(20); @@ -1320,7 +1351,7 @@ static int ov2740_probe(struct i2c_client *client) struct device *dev = &client->dev; struct ov2740 *ov2740; bool full_power; - int ret; + int i, ret; ov2740 = devm_kzalloc(&client->dev, sizeof(*ov2740), GFP_KERNEL); if (!ov2740) @@ -1337,9 +1368,17 @@ static int ov2740_probe(struct i2c_client *client) if (IS_ERR(ov2740->reset_gpio)) { return dev_err_probe(dev, PTR_ERR(ov2740->reset_gpio), "failed to get reset GPIO\n"); - } else if (ov2740->reset_gpio) { + } + + ov2740->powerdown_gpio = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); + if (IS_ERR(ov2740->powerdown_gpio)) { + return dev_err_probe(dev, PTR_ERR(ov2740->powerdown_gpio), + "failed to get powerdown GPIO\n"); + } + + if (ov2740->reset_gpio || ov2740->powerdown_gpio) { /* - * Ensure reset is asserted for at least 20 ms before + * Ensure reset/powerdown is asserted for at least 20 ms before * ov2740_resume() deasserts it. */ msleep(20); @@ -1350,6 +1389,13 @@ static int ov2740_probe(struct i2c_client *client) return dev_err_probe(dev, PTR_ERR(ov2740->clk), "failed to get clock\n"); + for (i = 0; i < OV2740_NUM_SUPPLIES; i++) + ov2740->supplies[i].supply = ov2740_supply_name[i]; + + ret = devm_regulator_bulk_get(dev, OV2740_NUM_SUPPLIES, ov2740->supplies); + if (ret) + return dev_err_probe(dev, ret, "failed to get regulators\n"); + full_power = acpi_dev_state_d0(&client->dev); if (full_power) { /* ACPI does not always clear the reset GPIO / enable the clock */ diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index da5cb5f45a4f..0dae0438aa80 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -1982,6 +1982,7 @@ static int ov5640_get_light_freq(struct ov5640_dev *sensor) light_freq = 50; } else { /* 60Hz */ + light_freq = 60; } } diff --git a/drivers/media/i2c/ov9282.c b/drivers/media/i2c/ov9282.c index 9f52af6f047f..87e5d7ce5a47 100644 --- a/drivers/media/i2c/ov9282.c +++ b/drivers/media/i2c/ov9282.c @@ -40,7 +40,7 @@ /* Exposure control */ #define OV9282_REG_EXPOSURE 0x3500 #define OV9282_EXPOSURE_MIN 1 -#define OV9282_EXPOSURE_OFFSET 12 +#define OV9282_EXPOSURE_OFFSET 25 #define OV9282_EXPOSURE_STEP 1 #define OV9282_EXPOSURE_DEFAULT 0x0282 diff --git a/drivers/media/pci/b2c2/flexcop-dma.c b/drivers/media/pci/b2c2/flexcop-dma.c index ff8058568240..2ef97be4dc54 100644 --- a/drivers/media/pci/b2c2/flexcop-dma.c +++ b/drivers/media/pci/b2c2/flexcop-dma.c @@ -123,23 +123,6 @@ static int flexcop_dma_remap(struct flexcop_device *fc, return 0; } -int flexcop_dma_control_size_irq(struct flexcop_device *fc, - flexcop_dma_index_t no, - int onoff) -{ - flexcop_ibi_value v = fc->read_ibi_reg(fc, ctrl_208); - - if (no & FC_DMA_1) - v.ctrl_208.DMA1_IRQ_Enable_sig = onoff; - - if (no & FC_DMA_2) - v.ctrl_208.DMA2_IRQ_Enable_sig = onoff; - - fc->write_ibi_reg(fc, ctrl_208, v); - return 0; -} -EXPORT_SYMBOL(flexcop_dma_control_size_irq); - int flexcop_dma_control_timer_irq(struct flexcop_device *fc, flexcop_dma_index_t no, int onoff) diff --git a/drivers/media/pci/cx18/cx18-gpio.c b/drivers/media/pci/cx18/cx18-gpio.c index c85eb8d25837..485a6cbeb15a 100644 --- a/drivers/media/pci/cx18/cx18-gpio.c +++ b/drivers/media/pci/cx18/cx18-gpio.c @@ -305,21 +305,6 @@ int cx18_gpio_register(struct cx18 *cx, u32 hw) return v4l2_device_register_subdev(&cx->v4l2_dev, sd); } -void cx18_reset_ir_gpio(void *data) -{ - struct cx18 *cx = to_cx18(data); - - if (cx->card->gpio_i2c_slave_reset.ir_reset_mask == 0) - return; - - CX18_DEBUG_INFO("Resetting IR microcontroller\n"); - - v4l2_subdev_call(&cx->sd_resetctrl, - core, reset, CX18_GPIO_RESET_Z8F0811); -} -EXPORT_SYMBOL(cx18_reset_ir_gpio); -/* This symbol is exported for use by lirc_pvr150 for the IR-blaster */ - /* Xceive tuner reset function */ int cx18_reset_tuner_gpio(void *dev, int component, int cmd, int value) { diff --git a/drivers/media/pci/cx18/cx18-gpio.h b/drivers/media/pci/cx18/cx18-gpio.h index 0fa4c7ad2286..8d5797dea7f5 100644 --- a/drivers/media/pci/cx18/cx18-gpio.h +++ b/drivers/media/pci/cx18/cx18-gpio.h @@ -17,5 +17,4 @@ enum cx18_gpio_reset_type { CX18_GPIO_RESET_XC2028 = 2, }; -void cx18_reset_ir_gpio(void *data); int cx18_reset_tuner_gpio(void *dev, int component, int cmd, int value); diff --git a/drivers/media/pci/intel/ipu6/ipu6-buttress.c b/drivers/media/pci/intel/ipu6/ipu6-buttress.c index e898902e83f3..d8db5aa5d528 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-buttress.c +++ b/drivers/media/pci/intel/ipu6/ipu6-buttress.c @@ -847,10 +847,10 @@ int ipu6_buttress_init(struct ipu6_device *isp) INIT_LIST_HEAD(&b->constraints); isp->secure_mode = ipu6_buttress_get_secure_mode(isp); - dev_info(&isp->pdev->dev, "IPU6 in %s mode touch 0x%x mask 0x%x\n", - isp->secure_mode ? "secure" : "non-secure", - readl(isp->base + BUTTRESS_REG_SECURITY_TOUCH), - readl(isp->base + BUTTRESS_REG_CAMERA_MASK)); + dev_dbg(&isp->pdev->dev, "IPU6 in %s mode touch 0x%x mask 0x%x\n", + isp->secure_mode ? "secure" : "non-secure", + readl(isp->base + BUTTRESS_REG_SECURITY_TOUCH), + readl(isp->base + BUTTRESS_REG_CAMERA_MASK)); b->wdt_cached_value = readl(isp->base + BUTTRESS_REG_WDT); writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_CLEAR); diff --git a/drivers/media/pci/intel/ipu6/ipu6-cpd.c b/drivers/media/pci/intel/ipu6/ipu6-cpd.c index 8b8142bcb2d5..b7013f6524ec 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-cpd.c +++ b/drivers/media/pci/intel/ipu6/ipu6-cpd.c @@ -275,7 +275,7 @@ static int ipu6_cpd_validate_moduledata(struct ipu6_device *isp, return -EINVAL; } - dev_info(&isp->pdev->dev, "FW version: %x\n", mod_hdr->fw_pkg_date); + dev_dbg(&isp->pdev->dev, "FW version: %x\n", mod_hdr->fw_pkg_date); ret = ipu6_cpd_validate_cpd(isp, moduledata + mod_hdr->hdr_len, moduledata_size - mod_hdr->hdr_len, moduledata_size); diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys.c b/drivers/media/pci/intel/ipu6/ipu6-isys.c index 77f9c7319868..8df1d83a74b5 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys.c @@ -1133,6 +1133,7 @@ static int isys_probe(struct auxiliary_device *auxdev, free_fw_msg_bufs: free_fw_msg_bufs(isys); out_remove_pkg_dir_shared_buffer: + cpu_latency_qos_remove_request(&isys->pm_qos); if (!isp->secure_mode) ipu6_cpd_free_pkg_dir(adev); remove_shared_buffer: diff --git a/drivers/media/pci/mgb4/mgb4_core.c b/drivers/media/pci/mgb4/mgb4_core.c index bc63dc81bcae..8ceaed5c1453 100644 --- a/drivers/media/pci/mgb4/mgb4_core.c +++ b/drivers/media/pci/mgb4/mgb4_core.c @@ -40,7 +40,9 @@ #include "mgb4_trigger.h" #include "mgb4_core.h" -#define MGB4_USER_IRQS 16 +#define MGB4_USER_IRQS 16 +#define MGB4_MGB4_BAR_ID 0 +#define MGB4_XDMA_BAR_ID 1 #define DIGITEQ_VID 0x1ed8 #define T100_DID 0x0101 diff --git a/drivers/media/pci/mgb4/mgb4_core.h b/drivers/media/pci/mgb4/mgb4_core.h index 9aec62514c0b..e86742d7b6c4 100644 --- a/drivers/media/pci/mgb4/mgb4_core.h +++ b/drivers/media/pci/mgb4/mgb4_core.h @@ -18,9 +18,6 @@ #define MGB4_VIN_DEVICES 2 #define MGB4_VOUT_DEVICES 2 -#define MGB4_MGB4_BAR_ID 0 -#define MGB4_XDMA_BAR_ID 1 - #define MGB4_IS_GMSL(mgbdev) \ ((mgbdev)->module_version >> 4 == 2) #define MGB4_IS_FPDL3(mgbdev) \ diff --git a/drivers/media/pci/mgb4/mgb4_sysfs_in.c b/drivers/media/pci/mgb4/mgb4_sysfs_in.c index 0ba66a2cf145..9626fa59e3d3 100644 --- a/drivers/media/pci/mgb4/mgb4_sysfs_in.c +++ b/drivers/media/pci/mgb4/mgb4_sysfs_in.c @@ -333,7 +333,7 @@ static ssize_t hsync_width_show(struct device *dev, struct video_device *vdev = to_video_device(dev); struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); u32 sig = mgb4_read_reg(&vindev->mgbdev->video, - vindev->config->regs.signal); + vindev->config->regs.hsync); return sprintf(buf, "%u\n", (sig & 0x00FF0000) >> 16); } @@ -344,7 +344,7 @@ static ssize_t vsync_width_show(struct device *dev, struct video_device *vdev = to_video_device(dev); struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); u32 sig = mgb4_read_reg(&vindev->mgbdev->video, - vindev->config->regs.signal2); + vindev->config->regs.vsync); return sprintf(buf, "%u\n", (sig & 0x00FF0000) >> 16); } @@ -355,7 +355,7 @@ static ssize_t hback_porch_show(struct device *dev, struct video_device *vdev = to_video_device(dev); struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); u32 sig = mgb4_read_reg(&vindev->mgbdev->video, - vindev->config->regs.signal); + vindev->config->regs.hsync); return sprintf(buf, "%u\n", (sig & 0x0000FF00) >> 8); } @@ -366,7 +366,7 @@ static ssize_t hfront_porch_show(struct device *dev, struct video_device *vdev = to_video_device(dev); struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); u32 sig = mgb4_read_reg(&vindev->mgbdev->video, - vindev->config->regs.signal); + vindev->config->regs.hsync); return sprintf(buf, "%u\n", (sig & 0x000000FF)); } @@ -377,7 +377,7 @@ static ssize_t vback_porch_show(struct device *dev, struct video_device *vdev = to_video_device(dev); struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); u32 sig = mgb4_read_reg(&vindev->mgbdev->video, - vindev->config->regs.signal2); + vindev->config->regs.vsync); return sprintf(buf, "%u\n", (sig & 0x0000FF00) >> 8); } @@ -388,7 +388,7 @@ static ssize_t vfront_porch_show(struct device *dev, struct video_device *vdev = to_video_device(dev); struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); u32 sig = mgb4_read_reg(&vindev->mgbdev->video, - vindev->config->regs.signal2); + vindev->config->regs.vsync); return sprintf(buf, "%u\n", (sig & 0x000000FF)); } diff --git a/drivers/media/pci/mgb4/mgb4_vin.c b/drivers/media/pci/mgb4/mgb4_vin.c index 3f171c624b40..434eaf0440e2 100644 --- a/drivers/media/pci/mgb4/mgb4_vin.c +++ b/drivers/media/pci/mgb4/mgb4_vin.c @@ -143,8 +143,8 @@ static int get_timings(struct mgb4_vin_dev *vindev, u32 status = mgb4_read_reg(video, regs->status); u32 pclk = mgb4_read_reg(video, regs->pclk); - u32 signal = mgb4_read_reg(video, regs->signal); - u32 signal2 = mgb4_read_reg(video, regs->signal2); + u32 hsync = mgb4_read_reg(video, regs->hsync); + u32 vsync = mgb4_read_reg(video, regs->vsync); u32 resolution = mgb4_read_reg(video, regs->resolution); if (!(status & (1U << 2))) @@ -161,12 +161,12 @@ static int get_timings(struct mgb4_vin_dev *vindev, if (status & (1U << 13)) timings->bt.polarities |= V4L2_DV_VSYNC_POS_POL; timings->bt.pixelclock = pclk * 1000; - timings->bt.hsync = (signal & 0x00FF0000) >> 16; - timings->bt.vsync = (signal2 & 0x00FF0000) >> 16; - timings->bt.hbackporch = (signal & 0x0000FF00) >> 8; - timings->bt.hfrontporch = signal & 0x000000FF; - timings->bt.vbackporch = (signal2 & 0x0000FF00) >> 8; - timings->bt.vfrontporch = signal2 & 0x000000FF; + timings->bt.hsync = (hsync & 0x00FF0000) >> 16; + timings->bt.vsync = (vsync & 0x00FF0000) >> 16; + timings->bt.hbackporch = (hsync & 0x0000FF00) >> 8; + timings->bt.hfrontporch = hsync & 0x000000FF; + timings->bt.vbackporch = (vsync & 0x0000FF00) >> 8; + timings->bt.vfrontporch = vsync & 0x000000FF; return 0; } @@ -864,9 +864,9 @@ static void create_debugfs(struct mgb4_vin_dev *vindev) vindev->regs[5].name = "PCLK_FREQUENCY"; vindev->regs[5].offset = vindev->config->regs.pclk; vindev->regs[6].name = "VIDEO_PARAMS_1"; - vindev->regs[6].offset = vindev->config->regs.signal; + vindev->regs[6].offset = vindev->config->regs.hsync; vindev->regs[7].name = "VIDEO_PARAMS_2"; - vindev->regs[7].offset = vindev->config->regs.signal2; + vindev->regs[7].offset = vindev->config->regs.vsync; vindev->regs[8].name = "PADDING_PIXELS"; vindev->regs[8].offset = vindev->config->regs.padding; if (has_timeperframe(video)) { diff --git a/drivers/media/pci/mgb4/mgb4_vin.h b/drivers/media/pci/mgb4/mgb4_vin.h index 8fd10c0a5554..2a2c829914ce 100644 --- a/drivers/media/pci/mgb4/mgb4_vin.h +++ b/drivers/media/pci/mgb4/mgb4_vin.h @@ -22,8 +22,8 @@ struct mgb4_vin_regs { u32 frame_period; u32 sync; u32 pclk; - u32 signal; - u32 signal2; + u32 hsync; + u32 vsync; u32 padding; u32 timer; }; diff --git a/drivers/media/pci/mgb4/mgb4_vout.c b/drivers/media/pci/mgb4/mgb4_vout.c index 6b2791e29de1..14c5725bd4d8 100644 --- a/drivers/media/pci/mgb4/mgb4_vout.c +++ b/drivers/media/pci/mgb4/mgb4_vout.c @@ -24,10 +24,6 @@ #include "mgb4_cmt.h" #include "mgb4_vout.h" -#define DEFAULT_WIDTH 1280 -#define DEFAULT_HEIGHT 640 -#define DEFAULT_PERIOD (MGB4_HW_FREQ / 60) - ATTRIBUTE_GROUPS(mgb4_fpdl3_out); ATTRIBUTE_GROUPS(mgb4_gmsl_out); @@ -180,7 +176,10 @@ static void stop_streaming(struct vb2_queue *vq) xdma_disable_user_irq(mgbdev->xdev, irq); cancel_work_sync(&voutdev->dma_work); + mgb4_mask_reg(&mgbdev->video, voutdev->config->regs.config, 0x2, 0x0); + mgb4_write_reg(&mgbdev->video, voutdev->config->regs.padding, 0); + return_all_buffers(voutdev, VB2_BUF_STATE_ERROR); } @@ -196,6 +195,7 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) int rv; u32 addr; + mgb4_write_reg(video, config->regs.padding, voutdev->padding); mgb4_mask_reg(video, config->regs.config, 0x2, 0x2); addr = mgb4_read_reg(video, config->regs.address); @@ -359,7 +359,6 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) voutdev->padding = (f->fmt.pix.bytesperline - (f->fmt.pix.width * pixelsize)) / pixelsize; - mgb4_write_reg(video, voutdev->config->regs.padding, voutdev->padding); return 0; } @@ -661,11 +660,10 @@ static void fpga_init(struct mgb4_vout_dev *voutdev) const struct mgb4_vout_regs *regs = &voutdev->config->regs; mgb4_write_reg(video, regs->config, 0x00000011); - mgb4_write_reg(video, regs->resolution, - (DEFAULT_WIDTH << 16) | DEFAULT_HEIGHT); + mgb4_write_reg(video, regs->resolution, (1280 << 16) | 640); mgb4_write_reg(video, regs->hsync, 0x00283232); mgb4_write_reg(video, regs->vsync, 0x40141F1E); - mgb4_write_reg(video, regs->frame_limit, DEFAULT_PERIOD); + mgb4_write_reg(video, regs->frame_limit, MGB4_HW_FREQ / 60); mgb4_write_reg(video, regs->padding, 0x00000000); voutdev->freq = mgb4_cmt_set_vout_freq(voutdev, 61150 >> 1) << 1; diff --git a/drivers/media/pci/saa7164/saa7164-vbi.c b/drivers/media/pci/saa7164/saa7164-vbi.c index a6738baab688..ac958a5fca78 100644 --- a/drivers/media/pci/saa7164/saa7164-vbi.c +++ b/drivers/media/pci/saa7164/saa7164-vbi.c @@ -77,9 +77,7 @@ static int saa7164_vbi_buffers_alloc(struct saa7164_port *port) /* TODO: NTSC SPECIFIC */ /* Init and establish defaults */ params->samplesperline = 1440; - params->numberoflines = 12; params->numberoflines = 18; - params->pitch = 1600; params->pitch = 1440; params->numpagetables = 2 + ((params->numberoflines * params->pitch) / PAGE_SIZE); diff --git a/drivers/media/pci/solo6x10/solo6x10-core.c b/drivers/media/pci/solo6x10/solo6x10-core.c index 1a9e2bccc413..6ec1480a6d18 100644 --- a/drivers/media/pci/solo6x10/solo6x10-core.c +++ b/drivers/media/pci/solo6x10/solo6x10-core.c @@ -362,7 +362,7 @@ static ssize_t sdram_offsets_show(struct device *dev, } static ssize_t sdram_show(struct file *file, struct kobject *kobj, - struct bin_attribute *a, char *buf, + const struct bin_attribute *a, char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); @@ -432,7 +432,7 @@ static int solo_sysfs_init(struct solo_dev *solo_dev) sysfs_attr_init(&sdram_attr->attr); sdram_attr->attr.name = "sdram"; sdram_attr->attr.mode = 0440; - sdram_attr->read = sdram_show; + sdram_attr->read_new = sdram_show; sdram_attr->size = solo_dev->sdram_size; if (device_create_bin_file(dev, sdram_attr)) { diff --git a/drivers/media/platform/broadcom/bcm2835-unicam.c b/drivers/media/platform/broadcom/bcm2835-unicam.c index 3aed0e493c81..f10064107d54 100644 --- a/drivers/media/platform/broadcom/bcm2835-unicam.c +++ b/drivers/media/platform/broadcom/bcm2835-unicam.c @@ -199,6 +199,7 @@ struct unicam_device { /* subdevice async notifier */ struct v4l2_async_notifier notifier; unsigned int sequence; + bool frame_started; /* Sensor node */ struct { @@ -546,7 +547,8 @@ unicam_find_format_by_fourcc(u32 fourcc, u32 pad) } for (i = 0; i < num_formats; ++i) { - if (formats[i].fourcc == fourcc) + if (formats[i].fourcc == fourcc || + formats[i].unpacked_fourcc == fourcc) return &formats[i]; } @@ -638,7 +640,14 @@ static inline void unicam_reg_write_field(struct unicam_device *unicam, u32 offs static void unicam_wr_dma_addr(struct unicam_node *node, struct unicam_buffer *buf) { - dma_addr_t endaddr = buf->dma_addr + buf->size; + /* + * Due to a HW bug causing buffer overruns in circular buffer mode under + * certain (not yet fully known) conditions, the dummy buffer allocation + * is set to a a single page size, but the hardware gets programmed with + * a buffer size of 0. + */ + dma_addr_t endaddr = buf->dma_addr + + (buf != &node->dummy_buf ? buf->size : 0); if (node->id == UNICAM_IMAGE_NODE) { unicam_reg_write(node->dev, UNICAM_IBSA0, buf->dma_addr); @@ -742,6 +751,8 @@ static irqreturn_t unicam_isr(int irq, void *dev) * buffer forever. */ if (fe) { + bool inc_seq = unicam->frame_started; + /* * Ensure we have swapped buffers already as we can't * stop the peripheral. If no buffer is available, use a @@ -761,11 +772,24 @@ static irqreturn_t unicam_isr(int irq, void *dev) * + FS + LS). In this case, we cannot signal the buffer * as complete, as the HW will reuse that buffer. */ - if (node->cur_frm && node->cur_frm != node->next_frm) + if (node->cur_frm && node->cur_frm != node->next_frm) { unicam_process_buffer_complete(node, sequence); + inc_seq = true; + } node->cur_frm = node->next_frm; } - unicam->sequence++; + + /* + * Increment the sequence number conditionally on either a FS + * having already occurred, or in the FE + FS condition as + * caught in the FE handler above. This ensures the sequence + * number corresponds to the frames generated by the sensor, not + * the frames dequeued to userland. + */ + if (inc_seq) { + unicam->sequence++; + unicam->frame_started = false; + } } if (ista & UNICAM_FSI) { @@ -795,6 +819,7 @@ static irqreturn_t unicam_isr(int irq, void *dev) } unicam_queue_event_sof(unicam); + unicam->frame_started = true; } /* @@ -816,11 +841,6 @@ static irqreturn_t unicam_isr(int irq, void *dev) } } - if (unicam_reg_read(unicam, UNICAM_ICTL) & UNICAM_FCM) { - /* Switch out of trigger mode if selected */ - unicam_reg_write_field(unicam, UNICAM_ICTL, 1, UNICAM_TFC); - unicam_reg_write_field(unicam, UNICAM_ICTL, 0, UNICAM_FCM); - } return IRQ_HANDLED; } @@ -984,8 +1004,7 @@ static void unicam_start_rx(struct unicam_device *unicam, unicam_reg_write_field(unicam, UNICAM_ANA, 0, UNICAM_DDL); - /* Always start in trigger frame capture mode (UNICAM_FCM set) */ - val = UNICAM_FSIE | UNICAM_FEIE | UNICAM_FCM | UNICAM_IBOB; + val = UNICAM_FSIE | UNICAM_FEIE | UNICAM_IBOB; line_int_freq = max(fmt->height >> 2, 128); unicam_set_field(&val, line_int_freq, UNICAM_LCIE_MASK); unicam_reg_write(unicam, UNICAM_ICTL, val); @@ -1413,6 +1432,7 @@ static int unicam_sd_enable_streams(struct v4l2_subdev *sd, if (unicam->pipe.nodes & BIT(UNICAM_METADATA_NODE)) unicam_start_metadata(unicam); + unicam->frame_started = false; unicam_start_rx(unicam, state); } diff --git a/drivers/media/platform/marvell/mcam-core.c b/drivers/media/platform/marvell/mcam-core.c index 9ec01228f907..b8360d37000a 100644 --- a/drivers/media/platform/marvell/mcam-core.c +++ b/drivers/media/platform/marvell/mcam-core.c @@ -935,7 +935,12 @@ static int mclk_enable(struct clk_hw *hw) ret = pm_runtime_resume_and_get(cam->dev); if (ret < 0) return ret; - clk_enable(cam->clk[0]); + ret = clk_enable(cam->clk[0]); + if (ret) { + pm_runtime_put(cam->dev); + return ret; + } + mcam_reg_write(cam, REG_CLKCTRL, (mclk_src << 29) | mclk_div); mcam_ctlr_power_up(cam); diff --git a/drivers/media/platform/marvell/mmp-driver.c b/drivers/media/platform/marvell/mmp-driver.c index 3fd4fc1b9c48..d3da7ebb4a2b 100644 --- a/drivers/media/platform/marvell/mmp-driver.c +++ b/drivers/media/platform/marvell/mmp-driver.c @@ -232,12 +232,22 @@ static int mmpcam_probe(struct platform_device *pdev) mcam_init_clk(mcam); /* + * Register with V4L. + */ + + ret = v4l2_device_register(mcam->dev, &mcam->v4l2_dev); + if (ret) + return ret; + + /* * Create a match of the sensor against its OF node. */ ep = fwnode_graph_get_next_endpoint(of_fwnode_handle(pdev->dev.of_node), NULL); - if (!ep) - return -ENODEV; + if (!ep) { + ret = -ENODEV; + goto out_v4l2_device_unregister; + } v4l2_async_nf_init(&mcam->notifier, &mcam->v4l2_dev); @@ -246,7 +256,7 @@ static int mmpcam_probe(struct platform_device *pdev) fwnode_handle_put(ep); if (IS_ERR(asd)) { ret = PTR_ERR(asd); - goto out; + goto out_v4l2_device_unregister; } /* @@ -254,7 +264,7 @@ static int mmpcam_probe(struct platform_device *pdev) */ ret = mccic_register(mcam); if (ret) - goto out; + goto out_v4l2_device_unregister; /* * Add OF clock provider. @@ -283,6 +293,8 @@ static int mmpcam_probe(struct platform_device *pdev) return 0; out: mccic_shutdown(mcam); +out_v4l2_device_unregister: + v4l2_device_unregister(&mcam->v4l2_dev); return ret; } @@ -293,6 +305,7 @@ static void mmpcam_remove(struct platform_device *pdev) struct mcam_camera *mcam = &cam->mcam; mccic_shutdown(mcam); + v4l2_device_unregister(&mcam->v4l2_dev); pm_runtime_force_suspend(mcam->dev); } diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c index ea2ea119dd2a..e5ccf673e152 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c @@ -114,19 +114,15 @@ static struct img_config *__get_config_offset(struct mdp_dev *mdp, if (pp_idx >= mdp->mdp_data->pp_used) goto err_param; - if (CFG_CHECK(MT8183, p_id)) + if (CFG_CHECK(MT8183, p_id)) { cfg_c = CFG_OFST(MT8183, param->config, pp_idx); - else if (CFG_CHECK(MT8195, p_id)) - cfg_c = CFG_OFST(MT8195, param->config, pp_idx); - else - goto err_param; - - if (CFG_CHECK(MT8183, p_id)) cfg_n = CFG_OFST(MT8183, param->config, pp_idx + 1); - else if (CFG_CHECK(MT8195, p_id)) + } else if (CFG_CHECK(MT8195, p_id)) { + cfg_c = CFG_OFST(MT8195, param->config, pp_idx); cfg_n = CFG_OFST(MT8195, param->config, pp_idx + 1); - else + } else { goto err_param; + } if ((long)cfg_n - (long)mdp->vpu.config > bound) { dev_err(dev, "config offset %ld OOB %ld\n", (long)cfg_n, bound); @@ -325,8 +321,7 @@ static int mdp_path_config_subfrm(struct mdp_cmdq_cmd *cmd, /* Enable mux settings */ for (index = 0; index < ctrl->num_sets; index++) { set = &ctrl->sets[index]; - cmdq_pkt_write_mask(&cmd->pkt, set->subsys_id, set->reg, - set->value, 0xFFFFFFFF); + cmdq_pkt_write(&cmd->pkt, set->subsys_id, set->reg, set->value); } /* Config sub-frame information */ for (index = (num_comp - 1); index >= 0; index--) { @@ -381,8 +376,7 @@ static int mdp_path_config_subfrm(struct mdp_cmdq_cmd *cmd, /* Disable mux settings */ for (index = 0; index < ctrl->num_sets; index++) { set = &ctrl->sets[index]; - cmdq_pkt_write_mask(&cmd->pkt, set->subsys_id, set->reg, - 0, 0xFFFFFFFF); + cmdq_pkt_write(&cmd->pkt, set->subsys_id, set->reg, 0); } return 0; @@ -471,43 +465,6 @@ static int mdp_path_config(struct mdp_dev *mdp, struct mdp_cmdq_cmd *cmd, return 0; } -static int mdp_cmdq_pkt_create(struct cmdq_client *client, struct cmdq_pkt *pkt, - size_t size) -{ - struct device *dev; - dma_addr_t dma_addr; - - pkt->va_base = kzalloc(size, GFP_KERNEL); - if (!pkt->va_base) - return -ENOMEM; - - pkt->buf_size = size; - pkt->cl = (void *)client; - - dev = client->chan->mbox->dev; - dma_addr = dma_map_single(dev, pkt->va_base, pkt->buf_size, - DMA_TO_DEVICE); - if (dma_mapping_error(dev, dma_addr)) { - dev_err(dev, "dma map failed, size=%u\n", (u32)(u64)size); - kfree(pkt->va_base); - return -ENOMEM; - } - - pkt->pa_base = dma_addr; - - return 0; -} - -static void mdp_cmdq_pkt_destroy(struct cmdq_pkt *pkt) -{ - struct cmdq_client *client = (struct cmdq_client *)pkt->cl; - - dma_unmap_single(client->chan->mbox->dev, pkt->pa_base, pkt->buf_size, - DMA_TO_DEVICE); - kfree(pkt->va_base); - pkt->va_base = NULL; -} - static void mdp_auto_release_work(struct work_struct *work) { struct mdp_cmdq_cmd *cmd; @@ -538,7 +495,7 @@ static void mdp_auto_release_work(struct work_struct *work) wake_up(&mdp->callback_wq); } - mdp_cmdq_pkt_destroy(&cmd->pkt); + cmdq_pkt_destroy(mdp->cmdq_clt[cmd->pp_idx], &cmd->pkt); kfree(cmd->comps); cmd->comps = NULL; kfree(cmd); @@ -578,7 +535,7 @@ static void mdp_handle_cmdq_callback(struct mbox_client *cl, void *mssg) if (refcount_dec_and_test(&mdp->job_count)) wake_up(&mdp->callback_wq); - mdp_cmdq_pkt_destroy(&cmd->pkt); + cmdq_pkt_destroy(mdp->cmdq_clt[cmd->pp_idx], &cmd->pkt); kfree(cmd->comps); cmd->comps = NULL; kfree(cmd); @@ -607,20 +564,13 @@ static struct mdp_cmdq_cmd *mdp_cmdq_prepare(struct mdp_dev *mdp, goto err_uninit; } - if (CFG_CHECK(MT8183, p_id)) - num_comp = CFG_GET(MT8183, config, num_components); - else if (CFG_CHECK(MT8195, p_id)) - num_comp = CFG_GET(MT8195, config, num_components); - else - goto err_uninit; - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto err_uninit; } - ret = mdp_cmdq_pkt_create(mdp->cmdq_clt[pp_idx], &cmd->pkt, SZ_16K); + ret = cmdq_pkt_create(mdp->cmdq_clt[pp_idx], &cmd->pkt, SZ_16K); if (ret) goto err_free_cmd; @@ -632,6 +582,7 @@ static struct mdp_cmdq_cmd *mdp_cmdq_prepare(struct mdp_dev *mdp, ret = -EINVAL; goto err_destroy_pkt; } + comps = kcalloc(num_comp, sizeof(*comps), GFP_KERNEL); if (!comps) { ret = -ENOMEM; @@ -676,7 +627,8 @@ static struct mdp_cmdq_cmd *mdp_cmdq_prepare(struct mdp_dev *mdp, dev_err(dev, "mdp_path_config error %d\n", pp_idx); goto err_free_path; } - cmdq_pkt_finalize(&cmd->pkt); + cmdq_pkt_eoc(&cmd->pkt); + cmdq_pkt_jump_rel(&cmd->pkt, CMDQ_INST_SIZE, mdp->cmdq_shift_pa[pp_idx]); for (i = 0; i < num_comp; i++) { s32 inner_id = MDP_COMP_NONE; @@ -699,6 +651,7 @@ static struct mdp_cmdq_cmd *mdp_cmdq_prepare(struct mdp_dev *mdp, cmd->comps = comps; cmd->num_comps = num_comp; cmd->mdp_ctx = param->mdp_ctx; + cmd->pp_idx = pp_idx; kfree(path); return cmd; @@ -710,7 +663,7 @@ err_free_path: err_free_comps: kfree(comps); err_destroy_pkt: - mdp_cmdq_pkt_destroy(&cmd->pkt); + cmdq_pkt_destroy(mdp->cmdq_clt[pp_idx], &cmd->pkt); err_free_cmd: kfree(cmd); err_uninit: diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.h b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.h index 53a30ad7e0b0..935ae9825728 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.h +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.h @@ -35,6 +35,7 @@ struct mdp_cmdq_cmd { struct mdp_comp *comps; void *mdp_ctx; u8 num_comps; + u8 pp_idx; }; struct mdp_dev; diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c index 8f62fb167156..683c066ed975 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c @@ -72,14 +72,14 @@ static int init_rdma(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) /* Disable RSZ1 */ if (ctx->comp->inner_id == rdma0 && prz1) - MM_REG_WRITE(cmd, subsys_id, prz1->reg_base, PRZ_ENABLE, - 0x0, BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, prz1->reg_base, + PRZ_ENABLE, 0x0, BIT(0)); } /* Reset RDMA */ - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_RESET, BIT(0), BIT(0)); - MM_REG_POLL(cmd, subsys_id, base, MDP_RDMA_MON_STA_1, BIT(8), BIT(8)); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_RESET, 0x0, BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_RESET, BIT(0), BIT(0)); + MM_REG_POLL_MASK(cmd, subsys_id, base, MDP_RDMA_MON_STA_1, BIT(8), BIT(8)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_RESET, 0x0, BIT(0)); return 0; } @@ -98,26 +98,25 @@ static int config_rdma_frame(struct mdp_comp_ctx *ctx, if (mdp_cfg && mdp_cfg->rdma_support_10bit) { if (block10bit) - MM_REG_WRITE(cmd, subsys_id, base, - MDP_RDMA_RESV_DUMMY_0, 0x7, 0x7); + MM_REG_WRITE_MASK(cmd, subsys_id, base, + MDP_RDMA_RESV_DUMMY_0, 0x7, 0x7); else - MM_REG_WRITE(cmd, subsys_id, base, - MDP_RDMA_RESV_DUMMY_0, 0x0, 0x7); + MM_REG_WRITE_MASK(cmd, subsys_id, base, + MDP_RDMA_RESV_DUMMY_0, 0x0, 0x7); } /* Setup smi control */ - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_GMCIF_CON, - (7 << 4) + //burst type to 8 - (1 << 16), //enable pre-ultra - 0x00030071); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_GMCIF_CON, + (7 << 4) + //burst type to 8 + (1 << 16), //enable pre-ultra + 0x00030071); /* Setup source frame info */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rdma.src_ctrl); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.src_ctrl); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_CON, reg, - 0x03C8FE0F); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_SRC_CON, reg, 0x03C8FE0F); if (mdp_cfg) if (mdp_cfg->rdma_support_10bit && en_ufo) { @@ -126,17 +125,15 @@ static int config_rdma_frame(struct mdp_comp_ctx *ctx, reg = CFG_COMP(MT8183, ctx->param, rdma.ufo_dec_y); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.ufo_dec_y); - MM_REG_WRITE(cmd, subsys_id, - base, MDP_RDMA_UFO_DEC_LENGTH_BASE_Y, - reg, 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, + MDP_RDMA_UFO_DEC_LENGTH_BASE_Y, reg); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rdma.ufo_dec_c); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.ufo_dec_c); - MM_REG_WRITE(cmd, subsys_id, - base, MDP_RDMA_UFO_DEC_LENGTH_BASE_C, - reg, 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, + MDP_RDMA_UFO_DEC_LENGTH_BASE_C, reg); /* Set 10bit source frame pitch */ if (block10bit) { @@ -144,9 +141,9 @@ static int config_rdma_frame(struct mdp_comp_ctx *ctx, reg = CFG_COMP(MT8183, ctx->param, rdma.mf_bkgd_in_pxl); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.mf_bkgd_in_pxl); - MM_REG_WRITE(cmd, subsys_id, - base, MDP_RDMA_MF_BKGD_SIZE_IN_PXL, - reg, 0x001FFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, + MDP_RDMA_MF_BKGD_SIZE_IN_PXL, + reg, 0x001FFFFF); } } @@ -157,128 +154,121 @@ static int config_rdma_frame(struct mdp_comp_ctx *ctx, reg = CFG_COMP(MT8195, ctx->param, rdma.control); rdma_con_mask = 0x1130; } - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_CON, reg, - rdma_con_mask); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_CON, reg, rdma_con_mask); /* Setup source buffer base */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rdma.iova[0]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.iova[0]); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_BASE_0, reg, - 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_BASE_0, reg); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rdma.iova[1]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.iova[1]); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_BASE_1, reg, - 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_BASE_1, reg); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rdma.iova[2]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.iova[2]); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_BASE_2, reg, - 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_BASE_2, reg); /* Setup source buffer end */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rdma.iova_end[0]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.iova_end[0]); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_END_0, - reg, 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_END_0, reg); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rdma.iova_end[1]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.iova_end[1]); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_END_1, - reg, 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_END_1, reg); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rdma.iova_end[2]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.iova_end[2]); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_END_2, - reg, 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_END_2, reg); /* Setup source frame pitch */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rdma.mf_bkgd); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.mf_bkgd); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_MF_BKGD_SIZE_IN_BYTE, - reg, 0x001FFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_MF_BKGD_SIZE_IN_BYTE, + reg, 0x001FFFFF); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rdma.sf_bkgd); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.sf_bkgd); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SF_BKGD_SIZE_IN_BYTE, - reg, 0x001FFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_SF_BKGD_SIZE_IN_BYTE, + reg, 0x001FFFFF); /* Setup color transform */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rdma.transform); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.transform); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_TRANSFORM_0, - reg, 0x0F110000); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_TRANSFORM_0, + reg, 0x0F110000); if (!mdp_cfg || !mdp_cfg->rdma_esl_setting) goto rdma_config_done; if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.dmabuf_con0); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_DMABUF_CON_0, - reg, 0x0FFF00FF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_DMABUF_CON_0, + reg, 0x0FFF00FF); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.ultra_th_high_con0); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_HIGH_CON_0, - reg, 0x3FFFFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_HIGH_CON_0, + reg, 0x3FFFFFFF); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.ultra_th_low_con0); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_LOW_CON_0, - reg, 0x3FFFFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_LOW_CON_0, + reg, 0x3FFFFFFF); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.dmabuf_con1); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_DMABUF_CON_1, - reg, 0x0F7F007F); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_DMABUF_CON_1, + reg, 0x0F7F007F); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.ultra_th_high_con1); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_HIGH_CON_1, - reg, 0x3FFFFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_HIGH_CON_1, + reg, 0x3FFFFFFF); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.ultra_th_low_con1); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_LOW_CON_1, - reg, 0x3FFFFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_LOW_CON_1, + reg, 0x3FFFFFFF); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.dmabuf_con2); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_DMABUF_CON_2, - reg, 0x0F3F003F); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_DMABUF_CON_2, + reg, 0x0F3F003F); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.ultra_th_high_con2); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_HIGH_CON_2, - reg, 0x3FFFFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_HIGH_CON_2, + reg, 0x3FFFFFFF); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.ultra_th_low_con2); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_LOW_CON_2, - reg, 0x3FFFFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_LOW_CON_2, + reg, 0x3FFFFFFF); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.dmabuf_con3); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_DMABUF_CON_3, - reg, 0x0F3F003F); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_DMABUF_CON_3, + reg, 0x0F3F003F); rdma_config_done: return 0; @@ -297,15 +287,14 @@ static int config_rdma_subfrm(struct mdp_comp_ctx *ctx, u32 reg = 0; /* Enable RDMA */ - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_EN, BIT(0), BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_EN, BIT(0), BIT(0)); /* Set Y pixel offset */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rdma.subfrms[index].offset[0]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.subfrms[index].offset[0]); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_OFFSET_0, - reg, 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_OFFSET_0, reg); /* Set 10bit UFO mode */ if (mdp_cfg) { @@ -315,8 +304,7 @@ static int config_rdma_subfrm(struct mdp_comp_ctx *ctx, else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.subfrms[index].offset_0_p); MM_REG_WRITE(cmd, subsys_id, base, - MDP_RDMA_SRC_OFFSET_0_P, - reg, 0xFFFFFFFF); + MDP_RDMA_SRC_OFFSET_0_P, reg); } } @@ -325,40 +313,38 @@ static int config_rdma_subfrm(struct mdp_comp_ctx *ctx, reg = CFG_COMP(MT8183, ctx->param, rdma.subfrms[index].offset[1]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.subfrms[index].offset[1]); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_OFFSET_1, - reg, 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_OFFSET_1, reg); /* Set V pixel offset */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rdma.subfrms[index].offset[2]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.subfrms[index].offset[2]); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_OFFSET_2, - reg, 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_OFFSET_2, reg); /* Set source size */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rdma.subfrms[index].src); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.subfrms[index].src); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_MF_SRC_SIZE, reg, - 0x1FFF1FFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_MF_SRC_SIZE, reg, + 0x1FFF1FFF); /* Set target size */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rdma.subfrms[index].clip); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.subfrms[index].clip); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_MF_CLIP_SIZE, - reg, 0x1FFF1FFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_MF_CLIP_SIZE, + reg, 0x1FFF1FFF); /* Set crop offset */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rdma.subfrms[index].clip_ofst); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rdma.subfrms[index].clip_ofst); - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_MF_OFFSET_1, - reg, 0x003F001F); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_MF_OFFSET_1, + reg, 0x003F001F); if (CFG_CHECK(MT8183, p_id)) { csf_l = CFG_COMP(MT8183, ctx->param, subfrms[index].in.left); @@ -369,8 +355,8 @@ static int config_rdma_subfrm(struct mdp_comp_ctx *ctx, } if (mdp_cfg && mdp_cfg->rdma_upsample_repeat_only) if ((csf_r - csf_l + 1) > 320) - MM_REG_WRITE(cmd, subsys_id, base, - MDP_RDMA_RESV_DUMMY_0, BIT(2), BIT(2)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, + MDP_RDMA_RESV_DUMMY_0, BIT(2), BIT(2)); return 0; } @@ -393,7 +379,7 @@ static int wait_rdma_event(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) MM_REG_WAIT(cmd, ctx->comp->gce_event[MDP_GCE_EVENT_EOF]); /* Disable RDMA */ - MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_EN, 0x0, BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_EN, 0x0, BIT(0)); return 0; } @@ -411,10 +397,10 @@ static int init_rsz(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) u8 subsys_id = ctx->comp->subsys_id; /* Reset RSZ */ - MM_REG_WRITE(cmd, subsys_id, base, PRZ_ENABLE, 0x10000, BIT(16)); - MM_REG_WRITE(cmd, subsys_id, base, PRZ_ENABLE, 0x0, BIT(16)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_ENABLE, 0x10000, BIT(16)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_ENABLE, 0x0, BIT(16)); /* Enable RSZ */ - MM_REG_WRITE(cmd, subsys_id, base, PRZ_ENABLE, BIT(0), BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_ENABLE, BIT(0), BIT(0)); if (CFG_CHECK(MT8195, p_id)) { struct device *dev; @@ -437,7 +423,7 @@ static int config_rsz_frame(struct mdp_comp_ctx *ctx, u32 reg = 0; if (mdp_cfg && mdp_cfg->rsz_etc_control) - MM_REG_WRITE(cmd, subsys_id, base, RSZ_ETC_CONTROL, 0x0, 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, RSZ_ETC_CONTROL, 0x0); if (CFG_CHECK(MT8183, p_id)) bypass = CFG_COMP(MT8183, ctx->param, frame.bypass); @@ -446,7 +432,7 @@ static int config_rsz_frame(struct mdp_comp_ctx *ctx, if (bypass) { /* Disable RSZ */ - MM_REG_WRITE(cmd, subsys_id, base, PRZ_ENABLE, 0x0, BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_ENABLE, 0x0, BIT(0)); return 0; } @@ -454,29 +440,27 @@ static int config_rsz_frame(struct mdp_comp_ctx *ctx, reg = CFG_COMP(MT8183, ctx->param, rsz.control1); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rsz.control1); - MM_REG_WRITE(cmd, subsys_id, base, PRZ_CONTROL_1, reg, - 0x03FFFDF3); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_CONTROL_1, reg, 0x03FFFDF3); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rsz.control2); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rsz.control2); - MM_REG_WRITE(cmd, subsys_id, base, PRZ_CONTROL_2, reg, - 0x0FFFC290); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_CONTROL_2, reg, 0x0FFFC290); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rsz.coeff_step_x); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rsz.coeff_step_x); - MM_REG_WRITE(cmd, subsys_id, base, PRZ_HORIZONTAL_COEFF_STEP, - reg, 0x007FFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_HORIZONTAL_COEFF_STEP, reg, + 0x007FFFFF); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rsz.coeff_step_y); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rsz.coeff_step_y); - MM_REG_WRITE(cmd, subsys_id, base, PRZ_VERTICAL_COEFF_STEP, - reg, 0x007FFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_VERTICAL_COEFF_STEP, reg, + 0x007FFFFF); return 0; } @@ -495,15 +479,13 @@ static int config_rsz_subfrm(struct mdp_comp_ctx *ctx, reg = CFG_COMP(MT8183, ctx->param, rsz.subfrms[index].control2); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rsz.subfrms[index].control2); - MM_REG_WRITE(cmd, subsys_id, base, PRZ_CONTROL_2, reg, - 0x00003800); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_CONTROL_2, reg, 0x00003800); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rsz.subfrms[index].src); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rsz.subfrms[index].src); - MM_REG_WRITE(cmd, subsys_id, base, PRZ_INPUT_IMAGE, reg, - 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, PRZ_INPUT_IMAGE, reg); if (CFG_CHECK(MT8183, p_id)) { csf_l = CFG_COMP(MT8183, ctx->param, subfrms[index].in.left); @@ -514,60 +496,56 @@ static int config_rsz_subfrm(struct mdp_comp_ctx *ctx, } if (mdp_cfg && mdp_cfg->rsz_disable_dcm_small_sample) if ((csf_r - csf_l + 1) <= 16) - MM_REG_WRITE(cmd, subsys_id, base, PRZ_CONTROL_1, - BIT(27), BIT(27)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_CONTROL_1, + BIT(27), BIT(27)); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, subfrms[index].luma.left); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, subfrms[index].luma.left); - MM_REG_WRITE(cmd, subsys_id, base, PRZ_LUMA_HORIZONTAL_INTEGER_OFFSET, - reg, 0xFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_LUMA_HORIZONTAL_INTEGER_OFFSET, + reg, 0xFFFF); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, subfrms[index].luma.left_subpix); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, subfrms[index].luma.left_subpix); - MM_REG_WRITE(cmd, subsys_id, - base, PRZ_LUMA_HORIZONTAL_SUBPIXEL_OFFSET, - reg, 0x1FFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_LUMA_HORIZONTAL_SUBPIXEL_OFFSET, + reg, 0x1FFFFF); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, subfrms[index].luma.top); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, subfrms[index].luma.top); - MM_REG_WRITE(cmd, subsys_id, base, PRZ_LUMA_VERTICAL_INTEGER_OFFSET, - reg, 0xFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_LUMA_VERTICAL_INTEGER_OFFSET, + reg, 0xFFFF); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, subfrms[index].luma.top_subpix); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, subfrms[index].luma.top_subpix); - MM_REG_WRITE(cmd, subsys_id, base, PRZ_LUMA_VERTICAL_SUBPIXEL_OFFSET, - reg, 0x1FFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_LUMA_VERTICAL_SUBPIXEL_OFFSET, + reg, 0x1FFFFF); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, subfrms[index].chroma.left); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, subfrms[index].chroma.left); - MM_REG_WRITE(cmd, subsys_id, - base, PRZ_CHROMA_HORIZONTAL_INTEGER_OFFSET, - reg, 0xFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_CHROMA_HORIZONTAL_INTEGER_OFFSET, + reg, 0xFFFF); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, subfrms[index].chroma.left_subpix); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, subfrms[index].chroma.left_subpix); - MM_REG_WRITE(cmd, subsys_id, - base, PRZ_CHROMA_HORIZONTAL_SUBPIXEL_OFFSET, - reg, 0x1FFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_CHROMA_HORIZONTAL_SUBPIXEL_OFFSET, + reg, 0x1FFFFF); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, rsz.subfrms[index].clip); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rsz.subfrms[index].clip); - MM_REG_WRITE(cmd, subsys_id, base, PRZ_OUTPUT_IMAGE, reg, - 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, PRZ_OUTPUT_IMAGE, reg); if (CFG_CHECK(MT8195, p_id)) { struct device *dev; @@ -596,19 +574,19 @@ static int config_rsz_subfrm(struct mdp_comp_ctx *ctx, if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, rsz.subfrms[index].merge_cfg); MM_REG_WRITE(cmd, merge->subsys_id, merge->reg_base, - MDP_MERGE_CFG_0, reg, 0xFFFFFFFF); + MDP_MERGE_CFG_0, reg); MM_REG_WRITE(cmd, merge->subsys_id, merge->reg_base, - MDP_MERGE_CFG_4, reg, 0xFFFFFFFF); + MDP_MERGE_CFG_4, reg); MM_REG_WRITE(cmd, merge->subsys_id, merge->reg_base, - MDP_MERGE_CFG_24, reg, 0xFFFFFFFF); + MDP_MERGE_CFG_24, reg); MM_REG_WRITE(cmd, merge->subsys_id, merge->reg_base, - MDP_MERGE_CFG_25, reg, 0xFFFFFFFF); + MDP_MERGE_CFG_25, reg); /* Bypass mode */ MM_REG_WRITE(cmd, merge->subsys_id, merge->reg_base, - MDP_MERGE_CFG_12, BIT(0), 0xFFFFFFFF); + MDP_MERGE_CFG_12, BIT(0)); MM_REG_WRITE(cmd, merge->subsys_id, merge->reg_base, - MDP_MERGE_ENABLE, BIT(0), 0xFFFFFFFF); + MDP_MERGE_ENABLE, BIT(0)); } rsz_subfrm_done: @@ -634,8 +612,8 @@ static int advance_rsz_subfrm(struct mdp_comp_ctx *ctx, } if ((csf_r - csf_l + 1) <= 16) - MM_REG_WRITE(cmd, subsys_id, base, PRZ_CONTROL_1, 0x0, - BIT(27)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_CONTROL_1, 0x0, + BIT(27)); } return 0; @@ -655,15 +633,15 @@ static int init_wrot(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) u8 subsys_id = ctx->comp->subsys_id; /* Reset WROT */ - MM_REG_WRITE(cmd, subsys_id, base, VIDO_SOFT_RST, BIT(0), BIT(0)); - MM_REG_POLL(cmd, subsys_id, base, VIDO_SOFT_RST_STAT, BIT(0), BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_SOFT_RST, BIT(0), BIT(0)); + MM_REG_POLL_MASK(cmd, subsys_id, base, VIDO_SOFT_RST_STAT, BIT(0), BIT(0)); /* Reset setting */ if (CFG_CHECK(MT8195, p_id)) - MM_REG_WRITE(cmd, subsys_id, base, VIDO_CTRL, 0x0, 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, VIDO_CTRL, 0x0); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_SOFT_RST, 0x0, BIT(0)); - MM_REG_POLL(cmd, subsys_id, base, VIDO_SOFT_RST_STAT, 0x0, BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_SOFT_RST, 0x0, BIT(0)); + MM_REG_POLL_MASK(cmd, subsys_id, base, VIDO_SOFT_RST_STAT, 0x0, BIT(0)); return 0; } @@ -681,39 +659,36 @@ static int config_wrot_frame(struct mdp_comp_ctx *ctx, reg = CFG_COMP(MT8183, ctx->param, wrot.iova[0]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.iova[0]); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_BASE_ADDR, reg, - 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, VIDO_BASE_ADDR, reg); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wrot.iova[1]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.iova[1]); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_BASE_ADDR_C, reg, - 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, VIDO_BASE_ADDR_C, reg); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wrot.iova[2]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.iova[2]); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_BASE_ADDR_V, reg, - 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, VIDO_BASE_ADDR_V, reg); if (mdp_cfg && mdp_cfg->wrot_support_10bit) { if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.scan_10bit); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_SCAN_10BIT, - reg, 0x0000000F); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_SCAN_10BIT, + reg, 0x0000000F); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.pending_zero); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_PENDING_ZERO, - reg, 0x04000000); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_PENDING_ZERO, + reg, 0x04000000); } if (CFG_CHECK(MT8195, p_id)) { reg = CFG_COMP(MT8195, ctx->param, wrot.bit_number); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_CTRL_2, - reg, 0x00000007); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_CTRL_2, + reg, 0x00000007); } /* Write frame related registers */ @@ -721,14 +696,13 @@ static int config_wrot_frame(struct mdp_comp_ctx *ctx, reg = CFG_COMP(MT8183, ctx->param, wrot.control); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.control); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_CTRL, reg, - 0xF131510F); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_CTRL, reg, 0xF131510F); /* Write pre-ultra threshold */ if (CFG_CHECK(MT8195, p_id)) { reg = CFG_COMP(MT8195, ctx->param, wrot.pre_ultra); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_DMA_PREULTRA, reg, - 0x00FFFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_DMA_PREULTRA, reg, + 0x00FFFFFF); } /* Write frame Y pitch */ @@ -736,37 +710,34 @@ static int config_wrot_frame(struct mdp_comp_ctx *ctx, reg = CFG_COMP(MT8183, ctx->param, wrot.stride[0]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.stride[0]); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_STRIDE, reg, - 0x0000FFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_STRIDE, reg, 0x0000FFFF); /* Write frame UV pitch */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wrot.stride[1]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.stride[1]); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_STRIDE_C, reg, - 0xFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_STRIDE_C, reg, 0xFFFF); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wrot.stride[2]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.stride[2]); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_STRIDE_V, reg, - 0xFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_STRIDE_V, reg, 0xFFFF); /* Write matrix control */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wrot.mat_ctrl); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.mat_ctrl); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_MAT_CTRL, reg, 0xF3); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_MAT_CTRL, reg, 0xF3); /* Set the fixed ALPHA as 0xFF */ - MM_REG_WRITE(cmd, subsys_id, base, VIDO_DITHER, 0xFF000000, - 0xFF000000); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_DITHER, 0xFF000000, + 0xFF000000); /* Set VIDO_EOL_SEL */ - MM_REG_WRITE(cmd, subsys_id, base, VIDO_RSV_1, BIT(31), BIT(31)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_RSV_1, BIT(31), BIT(31)); /* Set VIDO_FIFO_TEST */ if (CFG_CHECK(MT8183, p_id)) @@ -775,8 +746,8 @@ static int config_wrot_frame(struct mdp_comp_ctx *ctx, reg = CFG_COMP(MT8195, ctx->param, wrot.fifo_test); if (reg != 0) - MM_REG_WRITE(cmd, subsys_id, base, VIDO_FIFO_TEST, - reg, 0xFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_FIFO_TEST, reg, + 0xFFF); /* Filter enable */ if (mdp_cfg && mdp_cfg->wrot_filter_constraint) { @@ -784,13 +755,13 @@ static int config_wrot_frame(struct mdp_comp_ctx *ctx, reg = CFG_COMP(MT8183, ctx->param, wrot.filter); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.filter); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_MAIN_BUF_SIZE, - reg, 0x77); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_MAIN_BUF_SIZE, reg, + 0x77); /* Turn off WROT DMA DCM */ if (CFG_CHECK(MT8195, p_id)) - MM_REG_WRITE(cmd, subsys_id, base, VIDO_ROT_EN, - (0x1 << 23) + (0x1 << 20), 0x900000); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_ROT_EN, + (0x1 << 23) + (0x1 << 20), 0x900000); } return 0; @@ -808,57 +779,52 @@ static int config_wrot_subfrm(struct mdp_comp_ctx *ctx, reg = CFG_COMP(MT8183, ctx->param, wrot.subfrms[index].offset[0]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.subfrms[index].offset[0]); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_OFST_ADDR, - reg, 0x0FFFFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_OFST_ADDR, reg, 0x0FFFFFFF); /* Write U pixel offset */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wrot.subfrms[index].offset[1]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.subfrms[index].offset[1]); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_OFST_ADDR_C, - reg, 0x0FFFFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_OFST_ADDR_C, reg, 0x0FFFFFFF); /* Write V pixel offset */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wrot.subfrms[index].offset[2]); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.subfrms[index].offset[2]); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_OFST_ADDR_V, - reg, 0x0FFFFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_OFST_ADDR_V, reg, + 0x0FFFFFFF); /* Write source size */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wrot.subfrms[index].src); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.subfrms[index].src); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_IN_SIZE, reg, - 0x1FFF1FFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_IN_SIZE, reg, 0x1FFF1FFF); /* Write target size */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wrot.subfrms[index].clip); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.subfrms[index].clip); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_TAR_SIZE, reg, - 0x1FFF1FFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_TAR_SIZE, reg, 0x1FFF1FFF); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wrot.subfrms[index].clip_ofst); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.subfrms[index].clip_ofst); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_CROP_OFST, reg, - 0x1FFF1FFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_CROP_OFST, reg, 0x1FFF1FFF); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wrot.subfrms[index].main_buf); else if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, wrot.subfrms[index].main_buf); - MM_REG_WRITE(cmd, subsys_id, base, VIDO_MAIN_BUF_SIZE, - reg, 0x1FFF7F00); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_MAIN_BUF_SIZE, reg, + 0x1FFF7F00); /* Enable WROT */ - MM_REG_WRITE(cmd, subsys_id, base, VIDO_ROT_EN, BIT(0), BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_ROT_EN, BIT(0), BIT(0)); return 0; } @@ -881,11 +847,11 @@ static int wait_wrot_event(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) MM_REG_WAIT(cmd, ctx->comp->gce_event[MDP_GCE_EVENT_EOF]); if (mdp_cfg && mdp_cfg->wrot_filter_constraint) - MM_REG_WRITE(cmd, subsys_id, base, VIDO_MAIN_BUF_SIZE, 0x0, - 0x77); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_MAIN_BUF_SIZE, 0x0, + 0x77); /* Disable WROT */ - MM_REG_WRITE(cmd, subsys_id, base, VIDO_ROT_EN, 0x0, BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_ROT_EN, 0x0, BIT(0)); return 0; } @@ -904,9 +870,9 @@ static int init_wdma(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) u8 subsys_id = ctx->comp->subsys_id; /* Reset WDMA */ - MM_REG_WRITE(cmd, subsys_id, base, WDMA_RST, BIT(0), BIT(0)); - MM_REG_POLL(cmd, subsys_id, base, WDMA_FLOW_CTRL_DBG, BIT(0), BIT(0)); - MM_REG_WRITE(cmd, subsys_id, base, WDMA_RST, 0x0, BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_RST, BIT(0), BIT(0)); + MM_REG_POLL_MASK(cmd, subsys_id, base, WDMA_FLOW_CTRL_DBG, BIT(0), BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_RST, 0x0, BIT(0)); return 0; } @@ -918,40 +884,35 @@ static int config_wdma_frame(struct mdp_comp_ctx *ctx, u8 subsys_id = ctx->comp->subsys_id; u32 reg = 0; - MM_REG_WRITE(cmd, subsys_id, base, WDMA_BUF_CON2, 0x10101050, - 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, WDMA_BUF_CON2, 0x10101050); /* Setup frame information */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wdma.wdma_cfg); - MM_REG_WRITE(cmd, subsys_id, base, WDMA_CFG, reg, - 0x0F01B8F0); + MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_CFG, reg, 0x0F01B8F0); /* Setup frame base address */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wdma.iova[0]); - MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_ADDR, reg, - 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_ADDR, reg); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wdma.iova[1]); - MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_U_ADDR, reg, - 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_U_ADDR, reg); if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wdma.iova[2]); - MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_V_ADDR, reg, - 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_V_ADDR, reg); /* Setup Y pitch */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wdma.w_in_byte); - MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_W_IN_BYTE, - reg, 0x0000FFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_DST_W_IN_BYTE, reg, + 0x0000FFFF); /* Setup UV pitch */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wdma.uv_stride); - MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_UV_PITCH, - reg, 0x0000FFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_DST_UV_PITCH, reg, + 0x0000FFFF); /* Set the fixed ALPHA as 0xFF */ - MM_REG_WRITE(cmd, subsys_id, base, WDMA_ALPHA, 0x800000FF, - 0x800000FF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_ALPHA, 0x800000FF, + 0x800000FF); return 0; } @@ -966,36 +927,33 @@ static int config_wdma_subfrm(struct mdp_comp_ctx *ctx, /* Write Y pixel offset */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wdma.subfrms[index].offset[0]); - MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_ADDR_OFFSET, - reg, 0x0FFFFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_DST_ADDR_OFFSET, reg, + 0x0FFFFFFF); /* Write U pixel offset */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wdma.subfrms[index].offset[1]); - MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_U_ADDR_OFFSET, - reg, 0x0FFFFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_DST_U_ADDR_OFFSET, reg, + 0x0FFFFFFF); /* Write V pixel offset */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wdma.subfrms[index].offset[2]); - MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_V_ADDR_OFFSET, - reg, 0x0FFFFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_DST_V_ADDR_OFFSET, reg, + 0x0FFFFFFF); /* Write source size */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wdma.subfrms[index].src); - MM_REG_WRITE(cmd, subsys_id, base, WDMA_SRC_SIZE, reg, - 0x3FFF3FFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_SRC_SIZE, reg, 0x3FFF3FFF); /* Write target size */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wdma.subfrms[index].clip); - MM_REG_WRITE(cmd, subsys_id, base, WDMA_CLIP_SIZE, reg, - 0x3FFF3FFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_CLIP_SIZE, reg, 0x3FFF3FFF); /* Write clip offset */ if (CFG_CHECK(MT8183, p_id)) reg = CFG_COMP(MT8183, ctx->param, wdma.subfrms[index].clip_ofst); - MM_REG_WRITE(cmd, subsys_id, base, WDMA_CLIP_COORD, reg, - 0x3FFF3FFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_CLIP_COORD, reg, 0x3FFF3FFF); /* Enable WDMA */ - MM_REG_WRITE(cmd, subsys_id, base, WDMA_EN, BIT(0), BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_EN, BIT(0), BIT(0)); return 0; } @@ -1007,7 +965,7 @@ static int wait_wdma_event(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) MM_REG_WAIT(cmd, ctx->comp->gce_event[MDP_GCE_EVENT_EOF]); /* Disable WDMA */ - MM_REG_WRITE(cmd, subsys_id, base, WDMA_EN, 0x0, BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_EN, 0x0, BIT(0)); return 0; } @@ -1033,19 +991,17 @@ static int reset_luma_hist(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) /* Reset histogram */ for (i = 0; i <= hist_num; i++) - MM_REG_WRITE_MASK(cmd, subsys_id, base, - (MDP_LUMA_HIST_INIT + (i << 2)), - 0, 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, + (MDP_LUMA_HIST_INIT + (i << 2)), 0); if (mdp_cfg->tdshp_constrain) MM_REG_WRITE(cmd, subsys_id, base, - MDP_DC_TWO_D_W1_RESULT_INIT, 0, 0xFFFFFFFF); + MDP_DC_TWO_D_W1_RESULT_INIT, 0); if (mdp_cfg->tdshp_contour) for (i = 0; i < hist_num; i++) - MM_REG_WRITE_MASK(cmd, subsys_id, base, - (MDP_CONTOUR_HIST_INIT + (i << 2)), - 0, 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, + (MDP_CONTOUR_HIST_INIT + (i << 2)), 0); return 0; } @@ -1055,9 +1011,9 @@ static int init_tdshp(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) phys_addr_t base = ctx->comp->reg_base; u16 subsys_id = ctx->comp->subsys_id; - MM_REG_WRITE(cmd, subsys_id, base, MDP_TDSHP_CTRL, BIT(0), BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_TDSHP_CTRL, BIT(0), BIT(0)); /* Enable FIFO */ - MM_REG_WRITE(cmd, subsys_id, base, MDP_TDSHP_CFG, BIT(1), BIT(1)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_TDSHP_CFG, BIT(1), BIT(1)); return reset_luma_hist(ctx, cmd); } @@ -1072,7 +1028,7 @@ static int config_tdshp_frame(struct mdp_comp_ctx *ctx, if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, tdshp.cfg); - MM_REG_WRITE(cmd, subsys_id, base, MDP_TDSHP_CFG, reg, BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_TDSHP_CFG, reg, BIT(0)); return 0; } @@ -1086,26 +1042,24 @@ static int config_tdshp_subfrm(struct mdp_comp_ctx *ctx, if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, tdshp.subfrms[index].src); - MM_REG_WRITE(cmd, subsys_id, base, MDP_TDSHP_INPUT_SIZE, - reg, MDP_TDSHP_INPUT_SIZE_MASK); + MM_REG_WRITE(cmd, subsys_id, base, MDP_TDSHP_INPUT_SIZE, reg); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, tdshp.subfrms[index].clip_ofst); - MM_REG_WRITE(cmd, subsys_id, base, MDP_TDSHP_OUTPUT_OFFSET, - reg, 0x00FF00FF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_TDSHP_OUTPUT_OFFSET, reg, + 0x00FF00FF); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, tdshp.subfrms[index].clip); - MM_REG_WRITE(cmd, subsys_id, base, MDP_TDSHP_OUTPUT_SIZE, - reg, MDP_TDSHP_OUTPUT_SIZE_MASK); + MM_REG_WRITE(cmd, subsys_id, base, MDP_TDSHP_OUTPUT_SIZE, reg); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, tdshp.subfrms[index].hist_cfg_0); - MM_REG_WRITE(cmd, subsys_id, base, MDP_HIST_CFG_00, reg, 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, MDP_HIST_CFG_00, reg); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, tdshp.subfrms[index].hist_cfg_1); - MM_REG_WRITE(cmd, subsys_id, base, MDP_HIST_CFG_01, reg, 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, MDP_HIST_CFG_01, reg); return 0; } @@ -1122,21 +1076,19 @@ static int init_color(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) phys_addr_t base = ctx->comp->reg_base; u16 subsys_id = ctx->comp->subsys_id; - MM_REG_WRITE(cmd, subsys_id, base, - MDP_COLOR_START, 0x1, BIT(1) | BIT(0)); - MM_REG_WRITE(cmd, subsys_id, base, - MDP_COLOR_WIN_X_MAIN, 0xFFFF0000, 0xFFFFFFFF); - MM_REG_WRITE(cmd, subsys_id, base, - MDP_COLOR_WIN_Y_MAIN, 0xFFFF0000, 0xFFFFFFFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_COLOR_START, 0x1, + BIT(1) | BIT(0)); + MM_REG_WRITE(cmd, subsys_id, base, MDP_COLOR_WIN_X_MAIN, 0xFFFF0000); + MM_REG_WRITE(cmd, subsys_id, base, MDP_COLOR_WIN_Y_MAIN, 0xFFFF0000); /* Reset color matrix */ - MM_REG_WRITE(cmd, subsys_id, base, MDP_COLOR_CM1_EN, 0x0, BIT(0)); - MM_REG_WRITE(cmd, subsys_id, base, MDP_COLOR_CM2_EN, 0x0, BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_COLOR_CM1_EN, 0x0, BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_COLOR_CM2_EN, 0x0, BIT(0)); /* Enable interrupt */ - MM_REG_WRITE(cmd, subsys_id, base, MDP_COLOR_INTEN, 0x7, 0x7); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_COLOR_INTEN, 0x7, 0x7); - MM_REG_WRITE(cmd, subsys_id, base, MDP_COLOR_OUT_SEL, 0x333, 0x333); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_COLOR_OUT_SEL, 0x333, 0x333); return 0; } @@ -1151,8 +1103,7 @@ static int config_color_frame(struct mdp_comp_ctx *ctx, if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, color.start); - MM_REG_WRITE(cmd, subsys_id, base, MDP_COLOR_START, - reg, MDP_COLOR_START_MASK); + MM_REG_WRITE(cmd, subsys_id, base, MDP_COLOR_START, reg); return 0; } @@ -1166,13 +1117,13 @@ static int config_color_subfrm(struct mdp_comp_ctx *ctx, if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, color.subfrms[index].in_hsize); - MM_REG_WRITE(cmd, subsys_id, base, MDP_COLOR_INTERNAL_IP_WIDTH, - reg, 0x00003FFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_COLOR_INTERNAL_IP_WIDTH, + reg, 0x00003FFF); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, color.subfrms[index].in_vsize); - MM_REG_WRITE(cmd, subsys_id, base, MDP_COLOR_INTERNAL_IP_HEIGHT, - reg, 0x00003FFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_COLOR_INTERNAL_IP_HEIGHT, + reg, 0x00003FFF); return 0; } @@ -1190,9 +1141,9 @@ static int init_ccorr(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) u8 subsys_id = ctx->comp->subsys_id; /* CCORR enable */ - MM_REG_WRITE(cmd, subsys_id, base, MDP_CCORR_EN, BIT(0), BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_CCORR_EN, BIT(0), BIT(0)); /* Relay mode */ - MM_REG_WRITE(cmd, subsys_id, base, MDP_CCORR_CFG, BIT(0), BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_CCORR_CFG, BIT(0), BIT(0)); return 0; } @@ -1214,8 +1165,8 @@ static int config_ccorr_subfrm(struct mdp_comp_ctx *ctx, hsize = csf_r - csf_l + 1; vsize = csf_b - csf_t + 1; - MM_REG_WRITE(cmd, subsys_id, base, MDP_CCORR_SIZE, - (hsize << 16) + (vsize << 0), 0x1FFF1FFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_CCORR_SIZE, + (hsize << 16) + (vsize << 0), 0x1FFF1FFF); return 0; } @@ -1231,7 +1182,7 @@ static int init_aal(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) u16 subsys_id = ctx->comp->subsys_id; /* Always set MDP_AAL enable to 1 */ - MM_REG_WRITE(cmd, subsys_id, base, MDP_AAL_EN, BIT(0), BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_AAL_EN, BIT(0), BIT(0)); return 0; } @@ -1246,11 +1197,11 @@ static int config_aal_frame(struct mdp_comp_ctx *ctx, if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, aal.cfg_main); - MM_REG_WRITE(cmd, subsys_id, base, MDP_AAL_CFG_MAIN, reg, BIT(7)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_AAL_CFG_MAIN, reg, BIT(7)); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, aal.cfg); - MM_REG_WRITE(cmd, subsys_id, base, MDP_AAL_CFG, reg, BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_AAL_CFG, reg, BIT(0)); return 0; } @@ -1264,18 +1215,16 @@ static int config_aal_subfrm(struct mdp_comp_ctx *ctx, if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, aal.subfrms[index].src); - MM_REG_WRITE(cmd, subsys_id, base, MDP_AAL_SIZE, - reg, MDP_AAL_SIZE_MASK); + MM_REG_WRITE(cmd, subsys_id, base, MDP_AAL_SIZE, reg); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, aal.subfrms[index].clip_ofst); - MM_REG_WRITE(cmd, subsys_id, base, MDP_AAL_OUTPUT_OFFSET, - reg, 0x00FF00FF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_AAL_OUTPUT_OFFSET, reg, + 0x00FF00FF); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, aal.subfrms[index].clip); - MM_REG_WRITE(cmd, subsys_id, base, MDP_AAL_OUTPUT_SIZE, - reg, MDP_AAL_OUTPUT_SIZE_MASK); + MM_REG_WRITE(cmd, subsys_id, base, MDP_AAL_OUTPUT_SIZE, reg); return 0; } @@ -1293,7 +1242,7 @@ static int init_hdr(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) u16 subsys_id = ctx->comp->subsys_id; /* Always set MDP_HDR enable to 1 */ - MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_TOP, BIT(0), BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_HDR_TOP, BIT(0), BIT(0)); return 0; } @@ -1308,11 +1257,11 @@ static int config_hdr_frame(struct mdp_comp_ctx *ctx, if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, hdr.top); - MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_TOP, reg, BIT(29) | BIT(28)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_HDR_TOP, reg, BIT(29) | BIT(28)); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, hdr.relay); - MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_RELAY, reg, BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_HDR_RELAY, reg, BIT(0)); return 0; } @@ -1326,37 +1275,36 @@ static int config_hdr_subfrm(struct mdp_comp_ctx *ctx, if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, hdr.subfrms[index].win_size); - MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_TILE_POS, - reg, MDP_HDR_TILE_POS_MASK); + MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_TILE_POS, reg); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, hdr.subfrms[index].src); - MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_SIZE_0, reg, 0x1FFF1FFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_HDR_SIZE_0, reg, 0x1FFF1FFF); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, hdr.subfrms[index].clip_ofst0); - MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_SIZE_1, reg, 0x1FFF1FFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_HDR_SIZE_1, reg, 0x1FFF1FFF); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, hdr.subfrms[index].clip_ofst1); - MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_SIZE_2, reg, 0x1FFF1FFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_HDR_SIZE_2, reg, 0x1FFF1FFF); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, hdr.subfrms[index].hist_ctrl_0); - MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_HIST_CTRL_0, reg, 0x00003FFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_HDR_HIST_CTRL_0, reg, 0x00003FFF); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, hdr.subfrms[index].hist_ctrl_1); - MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_HIST_CTRL_1, reg, 0x00003FFF); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_HDR_HIST_CTRL_1, reg, 0x00003FFF); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, hdr.subfrms[index].hdr_top); - MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_TOP, reg, BIT(6) | BIT(5)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_HDR_TOP, reg, BIT(6) | BIT(5)); /* Enable histogram */ if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, hdr.subfrms[index].hist_addr); - MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_HIST_ADDR, reg, BIT(9)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_HDR_HIST_ADDR, reg, BIT(9)); return 0; } @@ -1373,8 +1321,8 @@ static int init_fg(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) phys_addr_t base = ctx->comp->reg_base; u16 subsys_id = ctx->comp->subsys_id; - MM_REG_WRITE(cmd, subsys_id, base, MDP_FG_TRIGGER, BIT(2), BIT(2)); - MM_REG_WRITE(cmd, subsys_id, base, MDP_FG_TRIGGER, 0x0, BIT(2)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_FG_TRIGGER, BIT(2), BIT(2)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_FG_TRIGGER, 0x0, BIT(2)); return 0; } @@ -1389,11 +1337,11 @@ static int config_fg_frame(struct mdp_comp_ctx *ctx, if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, fg.ctrl_0); - MM_REG_WRITE(cmd, subsys_id, base, MDP_FG_FG_CTRL_0, reg, BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_FG_FG_CTRL_0, reg, BIT(0)); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, fg.ck_en); - MM_REG_WRITE(cmd, subsys_id, base, MDP_FG_FG_CK_EN, reg, 0x7); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_FG_FG_CK_EN, reg, 0x7); return 0; } @@ -1407,11 +1355,11 @@ static int config_fg_subfrm(struct mdp_comp_ctx *ctx, if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, fg.subfrms[index].info_0); - MM_REG_WRITE(cmd, subsys_id, base, MDP_FG_TILE_INFO_0, reg, 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, MDP_FG_TILE_INFO_0, reg); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, fg.subfrms[index].info_1); - MM_REG_WRITE(cmd, subsys_id, base, MDP_FG_TILE_INFO_1, reg, 0xFFFFFFFF); + MM_REG_WRITE(cmd, subsys_id, base, MDP_FG_TILE_INFO_1, reg); return 0; } @@ -1428,14 +1376,11 @@ static int init_ovl(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) phys_addr_t base = ctx->comp->reg_base; u16 subsys_id = ctx->comp->subsys_id; - MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_EN, - BIT(0), MDP_OVL_EN_MASK); + MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_EN, BIT(0)); /* Set to relay mode */ - MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_SRC_CON, - BIT(9), MDP_OVL_SRC_CON_MASK); - MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_DP_CON, - BIT(0), MDP_OVL_DP_CON_MASK); + MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_SRC_CON, BIT(9)); + MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_DP_CON, BIT(0)); return 0; } @@ -1450,11 +1395,11 @@ static int config_ovl_frame(struct mdp_comp_ctx *ctx, if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, ovl.L0_con); - MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_L0_CON, reg, BIT(29) | BIT(28)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_OVL_L0_CON, reg, BIT(29) | BIT(28)); if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, ovl.src_con); - MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_SRC_CON, reg, BIT(0)); + MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_OVL_SRC_CON, reg, BIT(0)); return 0; } @@ -1468,14 +1413,12 @@ static int config_ovl_subfrm(struct mdp_comp_ctx *ctx, if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, ovl.subfrms[index].L0_src_size); - MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_L0_SRC_SIZE, - reg, MDP_OVL_L0_SRC_SIZE_MASK); + MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_L0_SRC_SIZE, reg); /* Setup output size */ if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, ovl.subfrms[index].roi_size); - MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_ROI_SIZE, - reg, MDP_OVL_ROI_SIZE_MASK); + MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_ROI_SIZE, reg); return 0; } @@ -1492,13 +1435,10 @@ static int init_pad(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd) phys_addr_t base = ctx->comp->reg_base; u16 subsys_id = ctx->comp->subsys_id; - MM_REG_WRITE(cmd, subsys_id, base, MDP_PAD_CON, - BIT(1), MDP_PAD_CON_MASK); + MM_REG_WRITE(cmd, subsys_id, base, MDP_PAD_CON, BIT(1)); /* Reset */ - MM_REG_WRITE(cmd, subsys_id, base, MDP_PAD_W_SIZE, - 0, MDP_PAD_W_SIZE_MASK); - MM_REG_WRITE(cmd, subsys_id, base, MDP_PAD_H_SIZE, - 0, MDP_PAD_H_SIZE_MASK); + MM_REG_WRITE(cmd, subsys_id, base, MDP_PAD_W_SIZE, 0); + MM_REG_WRITE(cmd, subsys_id, base, MDP_PAD_H_SIZE, 0); return 0; } @@ -1512,8 +1452,7 @@ static int config_pad_subfrm(struct mdp_comp_ctx *ctx, if (CFG_CHECK(MT8195, p_id)) reg = CFG_COMP(MT8195, ctx->param, pad.subfrms[index].pic_size); - MM_REG_WRITE(cmd, subsys_id, base, MDP_PAD_PIC_SIZE, - reg, MDP_PAD_PIC_SIZE_MASK); + MM_REG_WRITE(cmd, subsys_id, base, MDP_PAD_PIC_SIZE, reg); return 0; } diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.h b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.h index 3e5d2da1c807..681906c16419 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.h +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.h @@ -9,18 +9,18 @@ #include "mtk-mdp3-cmdq.h" -#define MM_REG_WRITE_MASK(cmd, id, base, ofst, val, mask, ...) \ - cmdq_pkt_write_mask(&((cmd)->pkt), id, \ - (base) + (ofst), (val), (mask), ##__VA_ARGS__) - -#define MM_REG_WRITE(cmd, id, base, ofst, val, mask, ...) \ +#define MM_REG_WRITE_MASK(cmd, id, base, ofst, val, mask) \ do { \ typeof(mask) (m) = (mask); \ - MM_REG_WRITE_MASK(cmd, id, base, ofst, val, \ + cmdq_pkt_write_mask(&((cmd)->pkt), id, (base) + (ofst), \ + (val), \ (((m) & (ofst##_MASK)) == (ofst##_MASK)) ? \ - (0xffffffff) : (m), ##__VA_ARGS__); \ + (0xffffffff) : (m)); \ } while (0) +#define MM_REG_WRITE(cmd, id, base, ofst, val) \ + cmdq_pkt_write(&((cmd)->pkt), id, (base) + (ofst), (val)) + #define MM_REG_WAIT(cmd, evt) \ do { \ typeof(cmd) (c) = (cmd); \ @@ -49,20 +49,17 @@ do { \ cmdq_pkt_set_event(&((c)->pkt), (e)); \ } while (0) -#define MM_REG_POLL_MASK(cmd, id, base, ofst, val, _mask, ...) \ +#define MM_REG_POLL_MASK(cmd, id, base, ofst, val, _mask) \ do { \ typeof(_mask) (_m) = (_mask); \ cmdq_pkt_poll_mask(&((cmd)->pkt), id, \ - (base) + (ofst), (val), (_m), ##__VA_ARGS__); \ + (base) + (ofst), (val), \ + (((_m) & (ofst##_MASK)) == (ofst##_MASK)) ? \ + (0xffffffff) : (_m)); \ } while (0) -#define MM_REG_POLL(cmd, id, base, ofst, val, mask, ...) \ -do { \ - typeof(mask) (m) = (mask); \ - MM_REG_POLL_MASK((cmd), id, base, ofst, val, \ - (((m) & (ofst##_MASK)) == (ofst##_MASK)) ? \ - (0xffffffff) : (m), ##__VA_ARGS__); \ -} while (0) +#define MM_REG_POLL(cmd, id, base, ofst, val) \ + cmdq_pkt_poll(&((cmd)->pkt), id, (base) + (ofst), (val)) enum mtk_mdp_comp_id { MDP_COMP_NONE = -1, /* Invalid engine */ diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c index 5e94ff0d0756..f571f561f070 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c @@ -312,6 +312,8 @@ static int mdp_probe(struct platform_device *pdev) ret = PTR_ERR(mdp->cmdq_clt[i]); goto err_mbox_destroy; } + + mdp->cmdq_shift_pa[i] = cmdq_get_shift_pa(mdp->cmdq_clt[i]->chan); } init_waitqueue_head(&mdp->callback_wq); diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.h b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.h index 430251f63754..05cade1d098e 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.h +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.h @@ -126,6 +126,7 @@ struct mdp_dev { u32 id_count; struct ida mdp_ida; struct cmdq_client *cmdq_clt[MDP_PP_MAX]; + u8 cmdq_shift_pa[MDP_PP_MAX]; wait_queue_head_t callback_wq; struct v4l2_device v4l2_dev; diff --git a/drivers/media/platform/nuvoton/npcm-video.c b/drivers/media/platform/nuvoton/npcm-video.c index 4f5d75645b2b..024cd8ee1709 100644 --- a/drivers/media/platform/nuvoton/npcm-video.c +++ b/drivers/media/platform/nuvoton/npcm-video.c @@ -1665,9 +1665,9 @@ static int npcm_video_ece_init(struct npcm_video *video) dev_info(dev, "Support HEXTILE pixel format\n"); ece_pdev = of_find_device_by_node(ece_node); - if (IS_ERR(ece_pdev)) { + if (!ece_pdev) { dev_err(dev, "Failed to find ECE device\n"); - return PTR_ERR(ece_pdev); + return -ENODEV; } of_node_put(ece_node); diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c index 7f5fe551179b..1221b309a916 100644 --- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c @@ -2677,11 +2677,12 @@ static void mxc_jpeg_detach_pm_domains(struct mxc_jpeg_dev *jpeg) int i; for (i = 0; i < jpeg->num_domains; i++) { - if (jpeg->pd_dev[i] && !pm_runtime_suspended(jpeg->pd_dev[i])) + if (!IS_ERR_OR_NULL(jpeg->pd_dev[i]) && + !pm_runtime_suspended(jpeg->pd_dev[i])) pm_runtime_force_suspend(jpeg->pd_dev[i]); - if (jpeg->pd_link[i] && !IS_ERR(jpeg->pd_link[i])) + if (!IS_ERR_OR_NULL(jpeg->pd_link[i])) device_link_del(jpeg->pd_link[i]); - if (jpeg->pd_dev[i] && !IS_ERR(jpeg->pd_dev[i])) + if (!IS_ERR_OR_NULL(jpeg->pd_dev[i])) dev_pm_domain_detach(jpeg->pd_dev[i], true); jpeg->pd_dev[i] = NULL; jpeg->pd_link[i] = NULL; diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c index aaf58063677c..1e79b1211b60 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c @@ -307,6 +307,19 @@ static const struct mxc_isi_plat_data mxc_imx8mp_data = { .has_36bit_dma = true, }; +static const struct mxc_isi_plat_data mxc_imx8ulp_data = { + .model = MXC_ISI_IMX8ULP, + .num_ports = 1, + .num_channels = 1, + .reg_offset = 0x0, + .ier_reg = &mxc_imx8_isi_ier_v2, + .set_thd = &mxc_imx8_isi_thd_v1, + .clks = mxc_imx8mn_clks, + .num_clks = ARRAY_SIZE(mxc_imx8mn_clks), + .buf_active_reverse = true, + .has_36bit_dma = false, +}; + static const struct mxc_isi_plat_data mxc_imx93_data = { .model = MXC_ISI_IMX93, .num_ports = 1, @@ -528,6 +541,7 @@ static void mxc_isi_remove(struct platform_device *pdev) static const struct of_device_id mxc_isi_of_match[] = { { .compatible = "fsl,imx8mn-isi", .data = &mxc_imx8mn_data }, { .compatible = "fsl,imx8mp-isi", .data = &mxc_imx8mp_data }, + { .compatible = "fsl,imx8ulp-isi", .data = &mxc_imx8ulp_data }, { .compatible = "fsl,imx93-isi", .data = &mxc_imx93_data }, { /* sentinel */ }, }; diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h index 2810ebe9b5f7..9c7fe9e5f941 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h @@ -158,6 +158,7 @@ struct mxc_gasket_ops { enum model { MXC_ISI_IMX8MN, MXC_ISI_IMX8MP, + MXC_ISI_IMX8ULP, MXC_ISI_IMX93, }; diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c index c0ba34ea82fd..8654150728a8 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c @@ -861,6 +861,7 @@ int mxc_isi_video_buffer_prepare(struct mxc_isi_dev *isi, struct vb2_buffer *vb2 const struct mxc_isi_format_info *info, const struct v4l2_pix_format_mplane *pix) { + struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb2); unsigned int i; for (i = 0; i < info->mem_planes; i++) { @@ -875,6 +876,8 @@ int mxc_isi_video_buffer_prepare(struct mxc_isi_dev *isi, struct vb2_buffer *vb2 vb2_set_plane_payload(vb2, i, size); } + v4l2_buf->field = pix->field; + return 0; } diff --git a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c index df7e93a5a4f6..f341f7b7fd8a 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c @@ -505,9 +505,9 @@ static void csiphy_gen2_config_lanes(struct csiphy_device *csiphy, u32 val; switch (csiphy->camss->res->version) { - case CAMSS_845: - r = &lane_regs_sdm845[0][0]; - array_size = ARRAY_SIZE(lane_regs_sdm845[0]); + case CAMSS_7280: + r = &lane_regs_sm8250[0][0]; + array_size = ARRAY_SIZE(lane_regs_sm8250[0]); break; case CAMSS_8250: r = &lane_regs_sm8250[0][0]; @@ -517,6 +517,10 @@ static void csiphy_gen2_config_lanes(struct csiphy_device *csiphy, r = &lane_regs_sc8280xp[0][0]; array_size = ARRAY_SIZE(lane_regs_sc8280xp[0]); break; + case CAMSS_845: + r = &lane_regs_sdm845[0][0]; + array_size = ARRAY_SIZE(lane_regs_sdm845[0]); + break; default: WARN(1, "unknown cspi version\n"); return; @@ -557,9 +561,10 @@ static bool csiphy_is_gen2(u32 version) bool ret = false; switch (version) { - case CAMSS_845: + case CAMSS_7280: case CAMSS_8250: case CAMSS_8280XP: + case CAMSS_845: ret = true; break; } diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index 5af2b382a843..3791c2d8a6cf 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -103,6 +103,11 @@ const struct csiphy_formats csiphy_formats_8x96 = { .formats = formats_8x96 }; +const struct csiphy_formats csiphy_formats_sc7280 = { + .nformats = ARRAY_SIZE(formats_sdm845), + .formats = formats_sdm845 +}; + const struct csiphy_formats csiphy_formats_sdm845 = { .nformats = ARRAY_SIZE(formats_sdm845), .formats = formats_sdm845 diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h index eebc1ff1cfab..90cc3f976643 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.h +++ b/drivers/media/platform/qcom/camss/camss-csiphy.h @@ -26,6 +26,12 @@ struct csiphy_lane { u8 pol; }; +/** + * struct csiphy_lanes_cfg - CSIPHY lanes configuration + * @num_data: number of data lanes + * @data: data lanes configuration + * @clk: clock lane configuration (only for D-PHY) + */ struct csiphy_lanes_cfg { int num_data; struct csiphy_lane *data; @@ -111,6 +117,7 @@ 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_sc7280; extern const struct csiphy_formats csiphy_formats_sdm845; extern const struct csiphy_hw_ops csiphy_ops_2ph_1_0; diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index 80a62ba11295..95f6a1ac7eaf 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -334,11 +334,12 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, return sink_code; } break; - case CAMSS_8x96: case CAMSS_660: - case CAMSS_845: + case CAMSS_7280: + case CAMSS_8x96: case CAMSS_8250: case CAMSS_8280XP: + case CAMSS_845: switch (sink_code) { case MEDIA_BUS_FMT_YUYV8_1X16: { @@ -1693,9 +1694,10 @@ static int vfe_bpl_align(struct vfe_device *vfe) int ret = 8; switch (vfe->camss->res->version) { - case CAMSS_845: + case CAMSS_7280: case CAMSS_8250: case CAMSS_8280XP: + case CAMSS_845: ret = 16; break; default: diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 9fb31f4c18ad..a85e9df0f301 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -1266,6 +1266,310 @@ static const struct resources_icc icc_res_sm8250[] = { }, }; +static const struct camss_subdev_resources csiphy_res_7280[] = { + /* CSIPHY0 */ + { + .regulators = { "vdda-phy", "vdda-pll" }, + + .clock = { "csiphy0", "csiphy0_timer" }, + .clock_rate = { { 300000000, 400000000 }, + { 300000000 } }, + .reg = { "csiphy0" }, + .interrupt = { "csiphy0" }, + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sc7280 + } + }, + /* CSIPHY1 */ + { + .regulators = { "vdda-phy", "vdda-pll" }, + + .clock = { "csiphy1", "csiphy1_timer" }, + .clock_rate = { { 300000000, 400000000 }, + { 300000000 } }, + .reg = { "csiphy1" }, + .interrupt = { "csiphy1" }, + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sc7280 + } + }, + /* CSIPHY2 */ + { + .regulators = { "vdda-phy", "vdda-pll" }, + + .clock = { "csiphy2", "csiphy2_timer" }, + .clock_rate = { { 300000000, 400000000 }, + { 300000000 } }, + .reg = { "csiphy2" }, + .interrupt = { "csiphy2" }, + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sc7280 + } + }, + /* CSIPHY3 */ + { + .regulators = { "vdda-phy", "vdda-pll" }, + + .clock = { "csiphy3", "csiphy3_timer" }, + .clock_rate = { { 300000000, 400000000 }, + { 300000000 } }, + .reg = { "csiphy3" }, + .interrupt = { "csiphy3" }, + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sc7280 + } + }, + /* CSIPHY4 */ + { + .regulators = { "vdda-phy", "vdda-pll" }, + + .clock = { "csiphy4", "csiphy4_timer" }, + .clock_rate = { { 300000000, 400000000 }, + { 300000000 } }, + .reg = { "csiphy4" }, + .interrupt = { "csiphy4" }, + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sc7280 + } + }, +}; + +static const struct camss_subdev_resources csid_res_7280[] = { + /* CSID0 */ + { + .regulators = {}, + + .clock = { "vfe0_csid", "vfe0_cphy_rx", "vfe0" }, + .clock_rate = { { 300000000, 400000000 }, + { 0 }, + { 380000000, 510000000, 637000000, 760000000 } + }, + + .reg = { "csid0" }, + .interrupt = { "csid0" }, + .csid = { + .is_lite = false, + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + }, + /* CSID1 */ + { + .regulators = {}, + + .clock = { "vfe1_csid", "vfe1_cphy_rx", "vfe1" }, + .clock_rate = { { 300000000, 400000000 }, + { 0 }, + { 380000000, 510000000, 637000000, 760000000 } + }, + + .reg = { "csid1" }, + .interrupt = { "csid1" }, + .csid = { + .is_lite = false, + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + }, + /* CSID2 */ + { + .regulators = {}, + + .clock = { "vfe2_csid", "vfe2_cphy_rx", "vfe2" }, + .clock_rate = { { 300000000, 400000000 }, + { 0 }, + { 380000000, 510000000, 637000000, 760000000 } + }, + + .reg = { "csid2" }, + .interrupt = { "csid2" }, + .csid = { + .is_lite = false, + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + }, + /* CSID3 */ + { + .regulators = {}, + + .clock = { "vfe_lite0_csid", "vfe_lite0_cphy_rx", "vfe_lite0" }, + .clock_rate = { { 300000000, 400000000 }, + { 0 }, + { 320000000, 400000000, 480000000, 600000000 } + }, + + .reg = { "csid_lite0" }, + .interrupt = { "csid_lite0" }, + .csid = { + .is_lite = true, + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + }, + /* CSID4 */ + { + .regulators = {}, + + .clock = { "vfe_lite1_csid", "vfe_lite1_cphy_rx", "vfe_lite1" }, + .clock_rate = { { 300000000, 400000000 }, + { 0 }, + { 320000000, 400000000, 480000000, 600000000 } + }, + + .reg = { "csid_lite1" }, + .interrupt = { "csid_lite1" }, + .csid = { + .is_lite = true, + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + }, +}; + +static const struct camss_subdev_resources vfe_res_7280[] = { + /* VFE0 */ + { + .regulators = {}, + + .clock = { "camnoc_axi", "cpas_ahb", "icp_ahb", "vfe0", + "vfe0_axi", "gcc_cam_hf_axi" }, + .clock_rate = { { 150000000, 240000000, 320000000, 400000000, 480000000 }, + { 80000000 }, + { 0 }, + { 380000000, 510000000, 637000000, 760000000 }, + { 0 }, + { 0 } }, + + .reg = { "vfe0" }, + .interrupt = { "vfe0" }, + .vfe = { + .line_num = 3, + .is_lite = false, + .has_pd = true, + .pd_name = "ife0", + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE1 */ + { + .regulators = {}, + + .clock = { "camnoc_axi", "cpas_ahb", "icp_ahb", "vfe1", + "vfe1_axi", "gcc_cam_hf_axi" }, + .clock_rate = { { 150000000, 240000000, 320000000, 400000000, 480000000 }, + { 80000000 }, + { 0 }, + { 380000000, 510000000, 637000000, 760000000 }, + { 0 }, + { 0 } }, + + .reg = { "vfe1" }, + .interrupt = { "vfe1" }, + .vfe = { + .line_num = 3, + .is_lite = false, + .has_pd = true, + .pd_name = "ife1", + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE2 */ + { + .regulators = {}, + + .clock = { "camnoc_axi", "cpas_ahb", "icp_ahb", "vfe2", + "vfe2_axi", "gcc_cam_hf_axi" }, + .clock_rate = { { 150000000, 240000000, 320000000, 400000000, 480000000 }, + { 80000000 }, + { 0 }, + { 380000000, 510000000, 637000000, 760000000 }, + { 0 }, + { 0 } }, + + .reg = { "vfe2" }, + .interrupt = { "vfe2" }, + .vfe = { + .line_num = 3, + .is_lite = false, + .hw_ops = &vfe_ops_170, + .has_pd = true, + .pd_name = "ife2", + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE3 (lite) */ + { + .clock = { "camnoc_axi", "cpas_ahb", "icp_ahb", + "vfe_lite0", "gcc_cam_hf_axi" }, + .clock_rate = { { 150000000, 240000000, 320000000, 400000000, 480000000 }, + { 80000000 }, + { 0 }, + { 320000000, 400000000, 480000000, 600000000 }, + { 0 } }, + + .regulators = {}, + .reg = { "vfe_lite0" }, + .interrupt = { "vfe_lite0" }, + .vfe = { + .line_num = 4, + .is_lite = true, + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE4 (lite) */ + { + .clock = { "camnoc_axi", "cpas_ahb", "icp_ahb", + "vfe_lite1", "gcc_cam_hf_axi" }, + .clock_rate = { { 150000000, 240000000, 320000000, 400000000, 480000000 }, + { 80000000 }, + { 0 }, + { 320000000, 400000000, 480000000, 600000000 }, + { 0 } }, + + .regulators = {}, + .reg = { "vfe_lite1" }, + .interrupt = { "vfe_lite1" }, + .vfe = { + .line_num = 4, + .is_lite = true, + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, +}; + +static const struct resources_icc icc_res_sc7280[] = { + { + .name = "ahb", + .icc_bw_tbl.avg = 38400, + .icc_bw_tbl.peak = 76800, + }, + { + .name = "hf_0", + .icc_bw_tbl.avg = 2097152, + .icc_bw_tbl.peak = 2097152, + }, +}; + static const struct camss_subdev_resources csiphy_res_sc8280xp[] = { /* CSIPHY0 */ { @@ -1995,6 +2299,24 @@ static int camss_init_subdevices(struct camss *camss) /* * camss_link_entities - Register subdev nodes and create links + * camss_link_err - print error in case link creation fails + * @src_name: name for source of the link + * @sink_name: name for sink of the link + */ +inline void camss_link_err(struct camss *camss, + const char *src_name, + const char *sink_name, + int ret) +{ + dev_err(camss->dev, + "Failed to link %s->%s entities: %d\n", + src_name, + sink_name, + ret); +} + +/* + * camss_link_entities - Register subdev nodes and create links * @camss: CAMSS device * * Return 0 on success or a negative error code on failure @@ -2012,11 +2334,10 @@ static int camss_link_entities(struct camss *camss) 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); + camss_link_err(camss, + camss->csiphy[i].subdev.entity.name, + camss->csid[j].subdev.entity.name, + ret); return ret; } } @@ -2031,11 +2352,10 @@ static int camss_link_entities(struct camss *camss) 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); + camss_link_err(camss, + camss->csid[i].subdev.entity.name, + camss->ispif->line[j].subdev.entity.name, + ret); return ret; } } @@ -2053,11 +2373,9 @@ static int camss_link_entities(struct camss *camss) MSM_VFE_PAD_SINK, 0); if (ret < 0) { - dev_err(camss->dev, - "Failed to link %s->%s entities: %d\n", - ispif->entity.name, - vfe->entity.name, - ret); + camss_link_err(camss, ispif->entity.name, + vfe->entity.name, + ret); return ret; } } @@ -2074,11 +2392,9 @@ static int camss_link_entities(struct camss *camss) MSM_VFE_PAD_SINK, 0); if (ret < 0) { - dev_err(camss->dev, - "Failed to link %s->%s entities: %d\n", - csid->entity.name, - vfe->entity.name, - ret); + camss_link_err(camss, csid->entity.name, + vfe->entity.name, + ret); return ret; } } @@ -2227,9 +2543,9 @@ static int camss_subdev_notifier_complete(struct v4l2_async_notifier *async) input, MSM_CSIPHY_PAD_SINK, MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); if (ret < 0) { - dev_err(camss->dev, - "Failed to link %s->%s entities: %d\n", - sensor->name, input->name, ret); + camss_link_err(camss, sensor->name, + input->name, + ret); return ret; } } @@ -2622,14 +2938,29 @@ static const struct camss_resources sc8280xp_resources = { .link_entities = camss_link_entities }; +static const struct camss_resources sc7280_resources = { + .version = CAMSS_7280, + .pd_name = "top", + .csiphy_res = csiphy_res_7280, + .csid_res = csid_res_7280, + .vfe_res = vfe_res_7280, + .icc_res = icc_res_sc7280, + .icc_path_num = ARRAY_SIZE(icc_res_sc7280), + .csiphy_num = ARRAY_SIZE(csiphy_res_7280), + .csid_num = ARRAY_SIZE(csid_res_7280), + .vfe_num = ARRAY_SIZE(vfe_res_7280), + .link_entities = camss_link_entities +}; + static const struct of_device_id camss_dt_match[] = { { .compatible = "qcom,msm8916-camss", .data = &msm8916_resources }, { .compatible = "qcom,msm8953-camss", .data = &msm8953_resources }, { .compatible = "qcom,msm8996-camss", .data = &msm8996_resources }, + { .compatible = "qcom,sc7280-camss", .data = &sc7280_resources }, + { .compatible = "qcom,sc8280xp-camss", .data = &sc8280xp_resources }, { .compatible = "qcom,sdm660-camss", .data = &sdm660_resources }, { .compatible = "qcom,sdm845-camss", .data = &sdm845_resources }, { .compatible = "qcom,sm8250-camss", .data = &sm8250_resources }, - { .compatible = "qcom,sc8280xp-camss", .data = &sc8280xp_resources }, { } }; diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h index 9da7f48f5dd7..9a046eea334f 100644 --- a/drivers/media/platform/qcom/camss/camss.h +++ b/drivers/media/platform/qcom/camss/camss.h @@ -77,13 +77,14 @@ enum pm_domain { }; enum camss_version { + CAMSS_660, + CAMSS_7280, CAMSS_8x16, CAMSS_8x53, CAMSS_8x96, - CAMSS_660, - CAMSS_845, CAMSS_8250, CAMSS_8280XP, + CAMSS_845, }; enum icc_count { diff --git a/drivers/media/platform/qcom/venus/Kconfig b/drivers/media/platform/qcom/venus/Kconfig index bfd50e8f3421..bc2e410b29cb 100644 --- a/drivers/media/platform/qcom/venus/Kconfig +++ b/drivers/media/platform/qcom/venus/Kconfig @@ -3,6 +3,7 @@ config VIDEO_QCOM_VENUS depends on V4L_MEM2MEM_DRIVERS depends on VIDEO_DEV && QCOM_SMEM depends on (ARCH_QCOM && IOMMU_DMA) || COMPILE_TEST + select OF_DYNAMIC if ARCH_QCOM select QCOM_MDT_LOADER if ARCH_QCOM select QCOM_SCM select VIDEOBUF2_DMA_CONTIG diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index 2d27c5167246..77d48578ecd2 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -286,6 +286,89 @@ static irqreturn_t venus_isr_thread(int irq, void *dev_id) return ret; } +#if defined(CONFIG_OF_DYNAMIC) +static int venus_add_video_core(struct venus_core *core, const char *node_name, + const char *compat) +{ + struct of_changeset *ocs = core->ocs; + struct device *dev = core->dev; + struct device_node *np, *enp; + int ret; + + if (!node_name) + return 0; + + enp = of_find_node_by_name(dev->of_node, node_name); + if (enp) { + of_node_put(enp); + return 0; + } + + np = of_changeset_create_node(ocs, dev->of_node, node_name); + if (!np) { + dev_err(dev, "Unable to create new node\n"); + return -ENODEV; + } + + ret = of_changeset_add_prop_string(ocs, np, "compatible", compat); + if (ret) + dev_err(dev, "unable to add %s\n", compat); + + of_node_put(np); + + return ret; +} + +static int venus_add_dynamic_nodes(struct venus_core *core) +{ + struct device *dev = core->dev; + int ret; + + core->ocs = kmalloc(sizeof(*core->ocs), GFP_KERNEL); + if (!core->ocs) + return -ENOMEM; + + of_changeset_init(core->ocs); + + ret = venus_add_video_core(core, core->res->dec_nodename, "venus-decoder"); + if (ret) + goto err; + + ret = venus_add_video_core(core, core->res->enc_nodename, "venus-encoder"); + if (ret) + goto err; + + ret = of_changeset_apply(core->ocs); + if (ret) { + dev_err(dev, "applying changeset fail ret %d\n", ret); + goto err; + } + + return 0; +err: + of_changeset_destroy(core->ocs); + kfree(core->ocs); + core->ocs = NULL; + return ret; +} + +static void venus_remove_dynamic_nodes(struct venus_core *core) +{ + if (core->ocs) { + of_changeset_revert(core->ocs); + of_changeset_destroy(core->ocs); + kfree(core->ocs); + } +} +#else +static int venus_add_dynamic_nodes(struct venus_core *core) +{ + return 0; +} + +static void venus_remove_dynamic_nodes(struct venus_core *core) {} +#endif + static int venus_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -365,9 +448,15 @@ static int venus_probe(struct platform_device *pdev) if (ret < 0) goto err_runtime_disable; + if (core->res->dec_nodename || core->res->enc_nodename) { + ret = venus_add_dynamic_nodes(core); + if (ret) + goto err_runtime_disable; + } + ret = of_platform_populate(dev->of_node, NULL, NULL, dev); if (ret) - goto err_runtime_disable; + goto err_remove_dynamic_nodes; ret = venus_firmware_init(core); if (ret) @@ -411,6 +500,8 @@ err_firmware_deinit: venus_firmware_deinit(core); err_of_depopulate: of_platform_depopulate(dev); +err_remove_dynamic_nodes: + venus_remove_dynamic_nodes(core); err_runtime_disable: pm_runtime_put_noidle(dev); pm_runtime_disable(dev); @@ -443,6 +534,8 @@ static void venus_remove(struct platform_device *pdev) venus_firmware_deinit(core); + venus_remove_dynamic_nodes(core); + pm_runtime_put_sync(dev); pm_runtime_disable(dev); @@ -506,18 +599,14 @@ err_cpucfg_path: void venus_close_common(struct venus_inst *inst) { /* - * First, remove the inst from the ->instances list, so that - * to_instance() will return NULL. - */ - hfi_session_destroy(inst); - /* - * Second, make sure we don't have IRQ/IRQ-thread currently running + * Make sure we don't have IRQ/IRQ-thread currently running * or pending execution, which would race with the inst destruction. */ synchronize_irq(inst->core->irq); v4l2_m2m_ctx_release(inst->m2m_ctx); v4l2_m2m_release(inst->m2m_dev); + hfi_session_destroy(inst); v4l2_fh_del(&inst->fh); v4l2_fh_exit(&inst->fh); v4l2_ctrl_handler_free(&inst->ctrl_handler); @@ -582,6 +671,8 @@ static const struct venus_resources msm8916_res = { .vmem_addr = 0, .dma_mask = 0xddc00000 - 1, .fwname = "qcom/venus-1.8/venus.mbn", + .dec_nodename = "video-decoder", + .enc_nodename = "video-encoder", }; static const struct freq_tbl msm8996_freq_table[] = { @@ -791,6 +882,8 @@ static const struct venus_resources sdm845_res_v2 = { .cp_nonpixel_start = 0x1000000, .cp_nonpixel_size = 0x24800000, .fwname = "qcom/venus-5.2/venus.mbn", + .dec_nodename = "video-core0", + .enc_nodename = "video-core1", }; static const struct freq_tbl sc7180_freq_table[] = { @@ -839,6 +932,8 @@ static const struct venus_resources sc7180_res = { .cp_nonpixel_start = 0x1000000, .cp_nonpixel_size = 0x24800000, .fwname = "qcom/venus-5.4/venus.mbn", + .dec_nodename = "video-decoder", + .enc_nodename = "video-encoder", }; static const struct freq_tbl sm8250_freq_table[] = { @@ -894,6 +989,8 @@ static const struct venus_resources sm8250_res = { .vmem_addr = 0, .dma_mask = 0xe0000000 - 1, .fwname = "qcom/vpu-1.0/venus.mbn", + .dec_nodename = "video-decoder", + .enc_nodename = "video-encoder", }; static const struct freq_tbl sc7280_freq_table[] = { @@ -956,6 +1053,8 @@ static const struct venus_resources sc7280_res = { .cp_nonpixel_start = 0x1000000, .cp_nonpixel_size = 0x24800000, .fwname = "qcom/vpu-2.0/venus.mbn", + .dec_nodename = "video-decoder", + .enc_nodename = "video-encoder", }; static const struct of_device_id venus_dt_match[] = { diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 44f1c3bc4186..abeeafa86697 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -90,6 +90,8 @@ struct venus_resources { u32 cp_nonpixel_start; u32 cp_nonpixel_size; const char *fwname; + const char *enc_nodename; + const char *dec_nodename; }; enum venus_fmt { @@ -169,6 +171,7 @@ struct venus_format { * @root: debugfs root directory * @venus_ver: the venus firmware version * @dump_core: a flag indicating that a core dump is required + * @ocs: OF changeset pointer */ struct venus_core { void __iomem *base; @@ -231,6 +234,7 @@ struct venus_core { u32 rev; } venus_ver; unsigned long dump_core; + struct of_changeset *ocs; }; struct vdec_controls { diff --git a/drivers/media/platform/qcom/venus/hfi.c b/drivers/media/platform/qcom/venus/hfi.c index e00aedb41d16..675e6fd1e9fa 100644 --- a/drivers/media/platform/qcom/venus/hfi.c +++ b/drivers/media/platform/qcom/venus/hfi.c @@ -138,29 +138,6 @@ int hfi_core_trigger_ssr(struct venus_core *core, u32 type) return core->ops->core_trigger_ssr(core, type); } -int hfi_core_ping(struct venus_core *core) -{ - int ret; - - mutex_lock(&core->lock); - - ret = core->ops->core_ping(core, 0xbeef); - if (ret) - goto unlock; - - ret = wait_for_completion_timeout(&core->done, TIMEOUT); - if (!ret) { - ret = -ETIMEDOUT; - goto unlock; - } - ret = 0; - if (core->error != HFI_ERR_NONE) - ret = -ENODEV; -unlock: - mutex_unlock(&core->lock); - return ret; -} - static int wait_session_msg(struct venus_inst *inst) { int ret; diff --git a/drivers/media/platform/qcom/venus/hfi.h b/drivers/media/platform/qcom/venus/hfi.h index f25d412d6553..0338841d5992 100644 --- a/drivers/media/platform/qcom/venus/hfi.h +++ b/drivers/media/platform/qcom/venus/hfi.h @@ -108,7 +108,6 @@ struct hfi_inst_ops { struct hfi_ops { int (*core_init)(struct venus_core *core); int (*core_deinit)(struct venus_core *core); - int (*core_ping)(struct venus_core *core, u32 cookie); int (*core_trigger_ssr)(struct venus_core *core, u32 trigger_type); int (*session_init)(struct venus_inst *inst, u32 session_type, @@ -152,7 +151,6 @@ int hfi_core_deinit(struct venus_core *core, bool blocking); int hfi_core_suspend(struct venus_core *core); int hfi_core_resume(struct venus_core *core, bool force); int hfi_core_trigger_ssr(struct venus_core *core, u32 type); -int hfi_core_ping(struct venus_core *core); int hfi_session_create(struct venus_inst *inst, const struct hfi_inst_ops *ops); void hfi_session_destroy(struct venus_inst *inst); int hfi_session_init(struct venus_inst *inst, u32 pixfmt); diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c index f9437b6412b9..a9167867063c 100644 --- a/drivers/media/platform/qcom/venus/hfi_venus.c +++ b/drivers/media/platform/qcom/venus/hfi_venus.c @@ -1178,16 +1178,6 @@ static int venus_core_deinit(struct venus_core *core) return 0; } -static int venus_core_ping(struct venus_core *core, u32 cookie) -{ - struct venus_hfi_device *hdev = to_hfi_priv(core); - struct hfi_sys_ping_pkt pkt; - - pkt_sys_ping(&pkt, cookie); - - return venus_iface_cmdq_write(hdev, &pkt, false); -} - static int venus_core_trigger_ssr(struct venus_core *core, u32 trigger_type) { struct venus_hfi_device *hdev = to_hfi_priv(core); @@ -1639,7 +1629,6 @@ static int venus_suspend(struct venus_core *core) static const struct hfi_ops venus_hfi_ops = { .core_init = venus_core_init, .core_deinit = venus_core_deinit, - .core_ping = venus_core_ping, .core_trigger_ssr = venus_core_trigger_ssr, .session_init = venus_session_init, diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 98c22b9f9372..9f82882b77bc 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -1697,10 +1697,6 @@ static int vdec_open(struct file *file) if (ret) goto err_free; - ret = hfi_session_create(inst, &vdec_hfi_ops); - if (ret) - goto err_ctrl_deinit; - vdec_inst_init(inst); ida_init(&inst->dpb_ids); @@ -1712,15 +1708,19 @@ static int vdec_open(struct file *file) inst->m2m_dev = v4l2_m2m_init(&vdec_m2m_ops); if (IS_ERR(inst->m2m_dev)) { ret = PTR_ERR(inst->m2m_dev); - goto err_session_destroy; + goto err_ctrl_deinit; } inst->m2m_ctx = v4l2_m2m_ctx_init(inst->m2m_dev, inst, m2m_queue_init); if (IS_ERR(inst->m2m_ctx)) { ret = PTR_ERR(inst->m2m_ctx); - goto err_m2m_release; + goto err_m2m_dev_release; } + ret = hfi_session_create(inst, &vdec_hfi_ops); + if (ret) + goto err_m2m_ctx_release; + v4l2_fh_init(&inst->fh, core->vdev_dec); inst->fh.ctrl_handler = &inst->ctrl_handler; @@ -1730,10 +1730,10 @@ static int vdec_open(struct file *file) return 0; -err_m2m_release: +err_m2m_ctx_release: + v4l2_m2m_ctx_release(inst->m2m_ctx); +err_m2m_dev_release: v4l2_m2m_release(inst->m2m_dev); -err_session_destroy: - hfi_session_destroy(inst); err_ctrl_deinit: v4l2_ctrl_handler_free(&inst->ctrl_handler); err_free: diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index c1c543535aaf..c7f8e37dba9b 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -1492,10 +1492,6 @@ static int venc_open(struct file *file) if (ret) goto err_free; - ret = hfi_session_create(inst, &venc_hfi_ops); - if (ret) - goto err_ctrl_deinit; - venc_inst_init(inst); /* @@ -1505,15 +1501,19 @@ static int venc_open(struct file *file) inst->m2m_dev = v4l2_m2m_init(&venc_m2m_ops); if (IS_ERR(inst->m2m_dev)) { ret = PTR_ERR(inst->m2m_dev); - goto err_session_destroy; + goto err_ctrl_deinit; } inst->m2m_ctx = v4l2_m2m_ctx_init(inst->m2m_dev, inst, m2m_queue_init); if (IS_ERR(inst->m2m_ctx)) { ret = PTR_ERR(inst->m2m_ctx); - goto err_m2m_release; + goto err_m2m_dev_release; } + ret = hfi_session_create(inst, &venc_hfi_ops); + if (ret) + goto err_m2m_ctx_release; + v4l2_fh_init(&inst->fh, core->vdev_enc); inst->fh.ctrl_handler = &inst->ctrl_handler; @@ -1523,10 +1523,10 @@ static int venc_open(struct file *file) return 0; -err_m2m_release: +err_m2m_ctx_release: + v4l2_m2m_ctx_release(inst->m2m_ctx); +err_m2m_dev_release: v4l2_m2m_release(inst->m2m_dev); -err_session_destroy: - hfi_session_destroy(inst); err_ctrl_deinit: v4l2_ctrl_handler_free(&inst->ctrl_handler); err_free: diff --git a/drivers/media/platform/renesas/rcar-csi2.c b/drivers/media/platform/renesas/rcar-csi2.c index 27ffdd28cbf7..0a53dd47d7bf 100644 --- a/drivers/media/platform/renesas/rcar-csi2.c +++ b/drivers/media/platform/renesas/rcar-csi2.c @@ -183,17 +183,19 @@ struct rcar_csi2; #define V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(n) (0x23840 + ((n) * 2)) /* n = 0 - 11 */ #define V4H_CORE_DIG_RW_COMMON_REG(n) (0x23880 + ((n) * 2)) /* n = 0 - 15 */ #define V4H_CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_REG(n) (0x239e0 + ((n) * 2)) /* n = 0 - 3 */ -#define V4H_CORE_DIG_CLANE_1_RW_CFG_0_REG 0x2a400 #define V4H_CORE_DIG_CLANE_1_RW_HS_TX_6_REG 0x2a60c /* V4H C-PHY */ #define V4H_CORE_DIG_RW_TRIO0_REG(n) (0x22100 + ((n) * 2)) /* n = 0 - 3 */ #define V4H_CORE_DIG_RW_TRIO1_REG(n) (0x22500 + ((n) * 2)) /* n = 0 - 3 */ #define V4H_CORE_DIG_RW_TRIO2_REG(n) (0x22900 + ((n) * 2)) /* n = 0 - 3 */ +#define V4H_CORE_DIG_CLANE_0_RW_CFG_0_REG 0x2a000 #define V4H_CORE_DIG_CLANE_0_RW_LP_0_REG 0x2a080 #define V4H_CORE_DIG_CLANE_0_RW_HS_RX_REG(n) (0x2a100 + ((n) * 2)) /* n = 0 - 6 */ +#define V4H_CORE_DIG_CLANE_1_RW_CFG_0_REG 0x2a400 #define V4H_CORE_DIG_CLANE_1_RW_LP_0_REG 0x2a480 #define V4H_CORE_DIG_CLANE_1_RW_HS_RX_REG(n) (0x2a500 + ((n) * 2)) /* n = 0 - 6 */ +#define V4H_CORE_DIG_CLANE_2_RW_CFG_0_REG 0x2a800 #define V4H_CORE_DIG_CLANE_2_RW_LP_0_REG 0x2a880 #define V4H_CORE_DIG_CLANE_2_RW_HS_RX_REG(n) (0x2a900 + ((n) * 2)) /* n = 0 - 6 */ @@ -672,6 +674,21 @@ static const struct rcar_csi2_format *rcsi2_code_to_fmt(unsigned int code) return NULL; } +struct rcsi2_cphy_line_order { + enum v4l2_mbus_csi2_cphy_line_orders_type order; + u16 cfg; + u16 ctrl29; +}; + +static const struct rcsi2_cphy_line_order rcsi2_cphy_line_orders[] = { + { .order = V4L2_MBUS_CSI2_CPHY_LINE_ORDER_ABC, .cfg = 0x0, .ctrl29 = 0x0 }, + { .order = V4L2_MBUS_CSI2_CPHY_LINE_ORDER_ACB, .cfg = 0xa, .ctrl29 = 0x1 }, + { .order = V4L2_MBUS_CSI2_CPHY_LINE_ORDER_BAC, .cfg = 0xc, .ctrl29 = 0x1 }, + { .order = V4L2_MBUS_CSI2_CPHY_LINE_ORDER_BCA, .cfg = 0x5, .ctrl29 = 0x0 }, + { .order = V4L2_MBUS_CSI2_CPHY_LINE_ORDER_CAB, .cfg = 0x3, .ctrl29 = 0x0 }, + { .order = V4L2_MBUS_CSI2_CPHY_LINE_ORDER_CBA, .cfg = 0x9, .ctrl29 = 0x1 } +}; + enum rcar_csi2_pads { RCAR_CSI2_SINK, RCAR_CSI2_SOURCE_VC0, @@ -722,6 +739,7 @@ struct rcar_csi2 { bool cphy; unsigned short lanes; unsigned char lane_swap[4]; + enum v4l2_mbus_csi2_cphy_line_orders_type line_orders[3]; }; static inline struct rcar_csi2 *sd_to_csi2(struct v4l2_subdev *sd) @@ -754,11 +772,24 @@ static void rcsi2_write(struct rcar_csi2 *priv, unsigned int reg, u32 data) iowrite32(data, priv->base + reg); } +static u16 rcsi2_read16(struct rcar_csi2 *priv, unsigned int reg) +{ + return ioread16(priv->base + reg); +} + static void rcsi2_write16(struct rcar_csi2 *priv, unsigned int reg, u16 data) { iowrite16(data, priv->base + reg); } +static void rcsi2_modify16(struct rcar_csi2 *priv, unsigned int reg, u16 data, u16 mask) +{ + u16 val; + + val = rcsi2_read16(priv, reg) & ~mask; + rcsi2_write16(priv, reg, val | data); +} + static int rcsi2_phtw_write(struct rcar_csi2 *priv, u8 data, u8 code) { unsigned int timeout; @@ -1112,6 +1143,26 @@ static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv, return 0; } +static void rsci2_set_line_order(struct rcar_csi2 *priv, + enum v4l2_mbus_csi2_cphy_line_orders_type order, + unsigned int cfgreg, unsigned int ctrlreg) +{ + const struct rcsi2_cphy_line_order *info = NULL; + + for (unsigned int i = 0; i < ARRAY_SIZE(rcsi2_cphy_line_orders); i++) { + if (rcsi2_cphy_line_orders[i].order == order) { + info = &rcsi2_cphy_line_orders[i]; + break; + } + } + + if (!info) + return; + + rcsi2_modify16(priv, cfgreg, info->cfg, 0x000f); + rcsi2_modify16(priv, ctrlreg, info->ctrl29, 0x0100); +} + static int rcsi2_wait_phy_start_v4h(struct rcar_csi2 *priv, u32 match) { unsigned int timeout; @@ -1189,12 +1240,18 @@ static int rcsi2_c_phy_setting_v4h(struct rcar_csi2 *priv, int msps) rcsi2_write16(priv, V4H_CORE_DIG_RW_TRIO1_REG(1), conf->trio1); rcsi2_write16(priv, V4H_CORE_DIG_RW_TRIO2_REG(1), conf->trio1); - /* - * Configure pin-swap. - * TODO: This registers is not documented yet, the values should depend - * on the 'clock-lanes' and 'data-lanes' devicetree properties. - */ - rcsi2_write16(priv, V4H_CORE_DIG_CLANE_1_RW_CFG_0_REG, 0xf5); + /* Configure data line order. */ + rsci2_set_line_order(priv, priv->line_orders[0], + V4H_CORE_DIG_CLANE_0_RW_CFG_0_REG, + V4H_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_REG(9)); + rsci2_set_line_order(priv, priv->line_orders[1], + V4H_CORE_DIG_CLANE_1_RW_CFG_0_REG, + V4H_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_REG(9)); + rsci2_set_line_order(priv, priv->line_orders[2], + V4H_CORE_DIG_CLANE_2_RW_CFG_0_REG, + V4H_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_REG(9)); + + /* TODO: This registers is not documented. */ rcsi2_write16(priv, V4H_CORE_DIG_CLANE_1_RW_HS_TX_6_REG, 0x5000); /* Leave Shutdown mode */ @@ -1349,15 +1406,15 @@ static int rcsi2_init_common_v4m(struct rcar_csi2 *priv, unsigned int mbps) static const struct phtw_value step2[] = { { .data = 0x00, .code = 0x00 }, { .data = 0x80, .code = 0xe0 }, - { .data = 0x01, .code = 0xe1 }, + { .data = 0x31, .code = 0xe1 }, { .data = 0x06, .code = 0x00 }, - { .data = 0x0f, .code = 0x11 }, + { .data = 0x11, .code = 0x11 }, { .data = 0x08, .code = 0x00 }, - { .data = 0x0f, .code = 0x11 }, + { .data = 0x11, .code = 0x11 }, { .data = 0x0a, .code = 0x00 }, - { .data = 0x0f, .code = 0x11 }, + { .data = 0x11, .code = 0x11 }, { .data = 0x0c, .code = 0x00 }, - { .data = 0x0f, .code = 0x11 }, + { .data = 0x11, .code = 0x11 }, { .data = 0x01, .code = 0x00 }, { .data = 0x31, .code = 0xaa }, { .data = 0x05, .code = 0x00 }, @@ -1370,6 +1427,11 @@ static int rcsi2_init_common_v4m(struct rcar_csi2 *priv, unsigned int mbps) { .data = 0x05, .code = 0x09 }, }; + static const struct phtw_value step3[] = { + { .data = 0x01, .code = 0x00 }, + { .data = 0x06, .code = 0xab }, + }; + if (priv->info->hsfreqrange) { ret = rcsi2_set_phypll(priv, mbps); if (ret) @@ -1400,7 +1462,7 @@ static int rcsi2_init_common_v4m(struct rcar_csi2 *priv, unsigned int mbps) return ret; } - return ret; + return rcsi2_phtw_write_array(priv, step3, ARRAY_SIZE(step3)); } static int rcsi2_start_receiver_v4m(struct rcar_csi2 *priv, @@ -1732,6 +1794,9 @@ static int rcsi2_parse_v4l2(struct rcar_csi2 *priv, } } + for (i = 0; i < ARRAY_SIZE(priv->line_orders); i++) + priv->line_orders[i] = vep->bus.mipi_csi2.line_orders[i]; + return 0; } diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index 17a1af507a27..cd69c8a686d3 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -558,7 +558,7 @@ static int rzg2l_cru_start_streaming_vq(struct vb2_queue *vq, unsigned int count goto assert_aresetn; } - /* Allocate scratch buffer. */ + /* Allocate scratch buffer */ cru->scratch = dma_alloc_coherent(cru->dev, cru->format.sizeimage, &cru->scratch_phys, GFP_KERNEL); if (!cru->scratch) { diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c index 8a48e9d91f96..4396348811c8 100644 --- a/drivers/media/platform/rockchip/rga/rga-buf.c +++ b/drivers/media/platform/rockchip/rga/rga-buf.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2017 Fuzhou Rockchip Electronics Co.Ltd + * Copyright (C) 2017 Rockchip Electronics Co., Ltd. * Author: Jacob Chen <jacob-chen@iotwrt.com> */ diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index 11c3d7234757..bf55beec0fac 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd + * Copyright (C) Rockchip Electronics Co., Ltd. * Author: Jacob Chen <jacob-chen@iotwrt.com> */ diff --git a/drivers/media/platform/rockchip/rga/rga-hw.h b/drivers/media/platform/rockchip/rga/rga-hw.h index e8917e5630a4..cc6bd7f5b030 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.h +++ b/drivers/media/platform/rockchip/rga/rga-hw.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd + * Copyright (C) Rockchip Electronics Co., Ltd. * Author: Jacob Chen <jacob-chen@iotwrt.com> */ #ifndef __RGA_HW_H__ diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index 1739ac0c8e92..3dccab5fa4a1 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd + * Copyright (C) Rockchip Electronics Co., Ltd. * Author: Jacob Chen <jacob-chen@iotwrt.com> */ diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index 8105bb2efe57..530e12de73c4 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd + * Copyright (C) Rockchip Electronics Co., Ltd. * Author: Jacob Chen <jacob-chen@iotwrt.com> */ #ifndef __RGA_H__ diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c index 02339cd94486..6dcefd144d5a 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c @@ -35,8 +35,6 @@ #define RKISP1_SP_DEV_NAME RKISP1_DRIVER_NAME "_selfpath" #define RKISP1_MP_DEV_NAME RKISP1_DRIVER_NAME "_mainpath" -#define RKISP1_MIN_BUFFERS_NEEDED 3 - enum rkisp1_plane { RKISP1_PLANE_Y = 0, RKISP1_PLANE_CB = 1, @@ -1561,7 +1559,7 @@ static int rkisp1_register_capture(struct rkisp1_capture *cap) q->ops = &rkisp1_vb2_ops; q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct rkisp1_buffer); - q->min_queued_buffers = RKISP1_MIN_BUFFERS_NEEDED; + q->min_queued_buffers = 1; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &node->vlock; q->dev = cap->rkisp1->dev; diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c index 0100b9c3edbe..dc65a7924f8a 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c @@ -228,6 +228,9 @@ static int rkisp1_subdev_notifier_register(struct rkisp1_device *rkisp1) break; } + if (ret) + break; + /* Parse the endpoint and validate the bus type. */ ret = v4l2_fwnode_endpoint_parse(ep, &vep); if (ret) { diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.c b/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.c index 7a48fad1df16..ac67a04e5eeb 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.c @@ -12,137 +12,6 @@ #include "fimc-is-errno.h" -const char *fimc_is_param_strerr(unsigned int error) -{ - switch (error) { - case ERROR_COMMON_CMD: - return "ERROR_COMMON_CMD: Invalid Command"; - case ERROR_COMMON_PARAMETER: - return "ERROR_COMMON_PARAMETER: Invalid Parameter"; - case ERROR_COMMON_SETFILE_LOAD: - return "ERROR_COMMON_SETFILE_LOAD: Illegal Setfile Loading"; - case ERROR_COMMON_SETFILE_ADJUST: - return "ERROR_COMMON_SETFILE_ADJUST: Setfile isn't adjusted"; - case ERROR_COMMON_SETFILE_INDEX: - return "ERROR_COMMON_SETFILE_INDEX: Invalid setfile index"; - case ERROR_COMMON_INPUT_PATH: - return "ERROR_COMMON_INPUT_PATH: Input path can be changed in ready state"; - case ERROR_COMMON_INPUT_INIT: - return "ERROR_COMMON_INPUT_INIT: IP can not start if input path is not set"; - case ERROR_COMMON_OUTPUT_PATH: - return "ERROR_COMMON_OUTPUT_PATH: Output path can be changed in ready state (stop)"; - case ERROR_COMMON_OUTPUT_INIT: - return "ERROR_COMMON_OUTPUT_INIT: IP can not start if output path is not set"; - case ERROR_CONTROL_BYPASS: - return "ERROR_CONTROL_BYPASS"; - case ERROR_OTF_INPUT_FORMAT: - return "ERROR_OTF_INPUT_FORMAT: Invalid format (DRC: YUV444, FD: YUV444, 422, 420)"; - case ERROR_OTF_INPUT_WIDTH: - return "ERROR_OTF_INPUT_WIDTH: Invalid width (DRC: 128~8192, FD: 32~8190)"; - case ERROR_OTF_INPUT_HEIGHT: - return "ERROR_OTF_INPUT_HEIGHT: Invalid bit-width (DRC: 8~12bits, FD: 8bit)"; - case ERROR_OTF_INPUT_BIT_WIDTH: - return "ERROR_OTF_INPUT_BIT_WIDTH: Invalid bit-width (DRC: 8~12bits, FD: 8bit)"; - case ERROR_DMA_INPUT_WIDTH: - return "ERROR_DMA_INPUT_WIDTH: Invalid width (DRC: 128~8192, FD: 32~8190)"; - case ERROR_DMA_INPUT_HEIGHT: - return "ERROR_DMA_INPUT_HEIGHT: Invalid height (DRC: 64~8192, FD: 16~8190)"; - case ERROR_DMA_INPUT_FORMAT: - return "ERROR_DMA_INPUT_FORMAT: Invalid format (DRC: YUV444 or YUV422, FD: YUV444,422,420)"; - case ERROR_DMA_INPUT_BIT_WIDTH: - return "ERROR_DMA_INPUT_BIT_WIDTH: Invalid bit-width (DRC: 8~12bits, FD: 8bit)"; - case ERROR_DMA_INPUT_ORDER: - return "ERROR_DMA_INPUT_ORDER: Invalid order(DRC: YYCbCr,YCbYCr,FD:NO,YYCbCr,YCbYCr,CbCr,CrCb)"; - case ERROR_DMA_INPUT_PLANE: - return "ERROR_DMA_INPUT_PLANE: Invalid plane (DRC: 3, FD: 1, 2, 3)"; - case ERROR_OTF_OUTPUT_WIDTH: - return "ERROR_OTF_OUTPUT_WIDTH: Invalid width (DRC: 128~8192)"; - case ERROR_OTF_OUTPUT_HEIGHT: - return "ERROR_OTF_OUTPUT_HEIGHT: Invalid height (DRC: 64~8192)"; - case ERROR_OTF_OUTPUT_FORMAT: - return "ERROR_OTF_OUTPUT_FORMAT: Invalid format (DRC: YUV444)"; - case ERROR_OTF_OUTPUT_BIT_WIDTH: - return "ERROR_OTF_OUTPUT_BIT_WIDTH: Invalid bit-width (DRC: 8~12bits, FD: 8bit)"; - case ERROR_DMA_OUTPUT_WIDTH: - return "ERROR_DMA_OUTPUT_WIDTH"; - case ERROR_DMA_OUTPUT_HEIGHT: - return "ERROR_DMA_OUTPUT_HEIGHT"; - case ERROR_DMA_OUTPUT_FORMAT: - return "ERROR_DMA_OUTPUT_FORMAT"; - case ERROR_DMA_OUTPUT_BIT_WIDTH: - return "ERROR_DMA_OUTPUT_BIT_WIDTH"; - case ERROR_DMA_OUTPUT_PLANE: - return "ERROR_DMA_OUTPUT_PLANE"; - case ERROR_DMA_OUTPUT_ORDER: - return "ERROR_DMA_OUTPUT_ORDER"; - - /* Sensor Error(100~199) */ - case ERROR_SENSOR_I2C_FAIL: - return "ERROR_SENSOR_I2C_FAIL"; - case ERROR_SENSOR_INVALID_FRAMERATE: - return "ERROR_SENSOR_INVALID_FRAMERATE"; - case ERROR_SENSOR_INVALID_EXPOSURETIME: - return "ERROR_SENSOR_INVALID_EXPOSURETIME"; - case ERROR_SENSOR_INVALID_SIZE: - return "ERROR_SENSOR_INVALID_SIZE"; - case ERROR_SENSOR_INVALID_SETTING: - return "ERROR_SENSOR_INVALID_SETTING"; - case ERROR_SENSOR_ACTUATOR_INIT_FAIL: - return "ERROR_SENSOR_ACTUATOR_INIT_FAIL"; - case ERROR_SENSOR_INVALID_AF_POS: - return "ERROR_SENSOR_INVALID_AF_POS"; - case ERROR_SENSOR_UNSUPPORT_FUNC: - return "ERROR_SENSOR_UNSUPPORT_FUNC"; - case ERROR_SENSOR_UNSUPPORT_PERI: - return "ERROR_SENSOR_UNSUPPORT_PERI"; - case ERROR_SENSOR_UNSUPPORT_AF: - return "ERROR_SENSOR_UNSUPPORT_AF"; - - /* ISP Error (200~299) */ - case ERROR_ISP_AF_BUSY: - return "ERROR_ISP_AF_BUSY"; - case ERROR_ISP_AF_INVALID_COMMAND: - return "ERROR_ISP_AF_INVALID_COMMAND"; - case ERROR_ISP_AF_INVALID_MODE: - return "ERROR_ISP_AF_INVALID_MODE"; - - /* DRC Error (300~399) */ - /* FD Error (400~499) */ - case ERROR_FD_CONFIG_MAX_NUMBER_STATE: - return "ERROR_FD_CONFIG_MAX_NUMBER_STATE"; - case ERROR_FD_CONFIG_MAX_NUMBER_INVALID: - return "ERROR_FD_CONFIG_MAX_NUMBER_INVALID"; - case ERROR_FD_CONFIG_YAW_ANGLE_STATE: - return "ERROR_FD_CONFIG_YAW_ANGLE_STATE"; - case ERROR_FD_CONFIG_YAW_ANGLE_INVALID: - return "ERROR_FD_CONFIG_YAW_ANGLE_INVALID\n"; - case ERROR_FD_CONFIG_ROLL_ANGLE_STATE: - return "ERROR_FD_CONFIG_ROLL_ANGLE_STATE"; - case ERROR_FD_CONFIG_ROLL_ANGLE_INVALID: - return "ERROR_FD_CONFIG_ROLL_ANGLE_INVALID"; - case ERROR_FD_CONFIG_SMILE_MODE_INVALID: - return "ERROR_FD_CONFIG_SMILE_MODE_INVALID"; - case ERROR_FD_CONFIG_BLINK_MODE_INVALID: - return "ERROR_FD_CONFIG_BLINK_MODE_INVALID"; - case ERROR_FD_CONFIG_EYES_DETECT_INVALID: - return "ERROR_FD_CONFIG_EYES_DETECT_INVALID"; - case ERROR_FD_CONFIG_MOUTH_DETECT_INVALID: - return "ERROR_FD_CONFIG_MOUTH_DETECT_INVALID"; - case ERROR_FD_CONFIG_ORIENTATION_STATE: - return "ERROR_FD_CONFIG_ORIENTATION_STATE"; - case ERROR_FD_CONFIG_ORIENTATION_INVALID: - return "ERROR_FD_CONFIG_ORIENTATION_INVALID"; - case ERROR_FD_CONFIG_ORIENTATION_VALUE_INVALID: - return "ERROR_FD_CONFIG_ORIENTATION_VALUE_INVALID"; - case ERROR_FD_RESULT: - return "ERROR_FD_RESULT"; - case ERROR_FD_MODE: - return "ERROR_FD_MODE"; - default: - return "Unknown"; - } -} - const char *fimc_is_strerr(unsigned int error) { error &= ~IS_ERROR_TIME_OUT_FLAG; diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.h b/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.h index 809e117331c0..fa8204ffec7b 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.h +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.h @@ -240,6 +240,5 @@ enum fimc_is_error { }; const char *fimc_is_strerr(unsigned int error); -const char *fimc_is_param_strerr(unsigned int error); #endif /* FIMC_IS_ERR_H_ */ diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is-param.c b/drivers/media/platform/samsung/exynos4-is/fimc-is-param.c index 9c816ae3b3e5..443362da8cc8 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-is-param.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-param.c @@ -204,15 +204,6 @@ int __is_hw_update_params(struct fimc_is *is) return ret; } -void __is_get_frame_size(struct fimc_is *is, struct v4l2_mbus_framefmt *mf) -{ - struct isp_param *isp; - - isp = &is->config[is->config_index].isp; - mf->width = isp->otf_input.width; - mf->height = isp->otf_input.height; -} - void __is_set_frame_size(struct fimc_is *is, struct v4l2_mbus_framefmt *mf) { unsigned int index = is->config_index; diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is-param.h b/drivers/media/platform/samsung/exynos4-is/fimc-is-param.h index 206904674927..10ad02f36fed 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-is-param.h +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-param.h @@ -994,7 +994,6 @@ void fimc_is_set_initial_params(struct fimc_is *is); unsigned int __get_pending_param_count(struct fimc_is *is); int __is_hw_update_params(struct fimc_is *is); -void __is_get_frame_size(struct fimc_is *is, struct v4l2_mbus_framefmt *mf); void __is_set_frame_size(struct fimc_is *is, struct v4l2_mbus_framefmt *mf); void __is_set_sensor(struct fimc_is *is, int fps); void __is_set_isp_aa_ae(struct fimc_is *is); diff --git a/drivers/media/platform/samsung/exynos4-is/mipi-csis.c b/drivers/media/platform/samsung/exynos4-is/mipi-csis.c index 63f3eecdd7e6..452880b5350c 100644 --- a/drivers/media/platform/samsung/exynos4-is/mipi-csis.c +++ b/drivers/media/platform/samsung/exynos4-is/mipi-csis.c @@ -940,13 +940,19 @@ static int s5pcsis_pm_resume(struct device *dev, bool runtime) state->supplies); goto unlock; } - clk_enable(state->clock[CSIS_CLK_GATE]); + ret = clk_enable(state->clock[CSIS_CLK_GATE]); + if (ret) { + phy_power_off(state->phy); + regulator_bulk_disable(CSIS_NUM_SUPPLIES, + state->supplies); + goto unlock; + } } if (state->flags & ST_STREAMING) s5pcsis_start_stream(state); state->flags &= ~ST_SUSPENDED; - unlock: +unlock: mutex_unlock(&state->lock); return ret ? -EAGAIN : 0; } diff --git a/drivers/media/platform/samsung/s3c-camif/camif-core.c b/drivers/media/platform/samsung/s3c-camif/camif-core.c index de6e8f151849..221e3c447f36 100644 --- a/drivers/media/platform/samsung/s3c-camif/camif-core.c +++ b/drivers/media/platform/samsung/s3c-camif/camif-core.c @@ -527,10 +527,19 @@ static void s3c_camif_remove(struct platform_device *pdev) static int s3c_camif_runtime_resume(struct device *dev) { struct camif_dev *camif = dev_get_drvdata(dev); + int ret; + + ret = clk_enable(camif->clock[CLK_GATE]); + if (ret) + return ret; - clk_enable(camif->clock[CLK_GATE]); /* null op on s3c244x */ - clk_enable(camif->clock[CLK_CAM]); + ret = clk_enable(camif->clock[CLK_CAM]); + if (ret) { + clk_disable(camif->clock[CLK_GATE]); + return ret; + } + return 0; } diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c index 2fe3c9228ac5..5f80931f056d 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c @@ -774,8 +774,10 @@ static int s5p_mfc_open(struct file *file) int ret = 0; mfc_debug_enter(); - if (mutex_lock_interruptible(&dev->mfc_mutex)) - return -ERESTARTSYS; + if (mutex_lock_interruptible(&dev->mfc_mutex)) { + ret = -ERESTARTSYS; + goto err_enter; + } dev->num_inst++; /* It is guarded by mfc_mutex in vfd */ /* Allocate memory for context */ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); @@ -946,6 +948,7 @@ err_no_ctx: err_alloc: dev->num_inst--; mutex_unlock(&dev->mfc_mutex); +err_enter: mfc_debug_leave(); return ret; } diff --git a/drivers/media/platform/st/stm32/Kconfig b/drivers/media/platform/st/stm32/Kconfig index 9df9a2a17728..f12e67bcc9bc 100644 --- a/drivers/media/platform/st/stm32/Kconfig +++ b/drivers/media/platform/st/stm32/Kconfig @@ -1,6 +1,20 @@ # SPDX-License-Identifier: GPL-2.0-only # V4L drivers +config VIDEO_STM32_CSI + tristate "STM32 Camera Serial Interface (CSI) support" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV && OF + depends on ARCH_STM32 || COMPILE_TEST + select MEDIA_CONTROLLER + select V4L2_FWNODE + help + This module makes the STM32 Camera Serial Interface (CSI) + available as a v4l2 device. + + To compile this driver as a module, choose M here: the module + will be called stm32-csi. + config VIDEO_STM32_DCMI tristate "STM32 Digital Camera Memory Interface (DCMI) support" depends on V4L_PLATFORM_DRIVERS diff --git a/drivers/media/platform/st/stm32/Makefile b/drivers/media/platform/st/stm32/Makefile index 7ed8297b9b19..9ae57897f030 100644 --- a/drivers/media/platform/st/stm32/Makefile +++ b/drivers/media/platform/st/stm32/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_VIDEO_STM32_CSI) += stm32-csi.o obj-$(CONFIG_VIDEO_STM32_DCMI) += stm32-dcmi.o obj-$(CONFIG_VIDEO_STM32_DCMIPP) += stm32-dcmipp/ stm32-dma2d-objs := dma2d/dma2d.o dma2d/dma2d-hw.o diff --git a/drivers/media/platform/st/stm32/stm32-csi.c b/drivers/media/platform/st/stm32/stm32-csi.c new file mode 100644 index 000000000000..48941aae8c9b --- /dev/null +++ b/drivers/media/platform/st/stm32/stm32-csi.c @@ -0,0 +1,1137 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for STM32 Camera Serial Interface + * + * Copyright (C) STMicroelectronics SA 2024 + * Author: Alain Volmat <alain.volmat@foss.st.com> + * for STMicroelectronics. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/reset.h> +#include <linux/slab.h> + +#include <media/mipi-csi2.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> + +#define STM32_CSI_CR 0x0000 +#define STM32_CSI_CR_CSIEN BIT(0) +#define STM32_CSI_CR_VCXSTART(x) BIT(2 + ((x) * 4)) +#define STM32_CSI_CR_VCXSTOP(x) BIT(3 + ((x) * 4)) +#define STM32_CSI_PCR 0x0004 +#define STM32_CSI_PCR_DL1EN BIT(3) +#define STM32_CSI_PCR_DL0EN BIT(2) +#define STM32_CSI_PCR_CLEN BIT(1) +#define STM32_CSI_PCR_PWRDOWN BIT(0) +#define STM32_CSI_VCXCFGR1(x) ((((x) + 1) * 0x0010) + 0x0) +#define STM32_CSI_VCXCFGR1_ALLDT BIT(0) +#define STM32_CSI_VCXCFGR1_DT0EN BIT(1) +#define STM32_CSI_VCXCFGR1_DT1EN BIT(2) +#define STM32_CSI_VCXCFGR1_CDTFT_SHIFT 8 +#define STM32_CSI_VCXCFGR1_DT0_SHIFT 16 +#define STM32_CSI_VCXCFGR1_DT0FT_SHIFT 24 +#define STM32_CSI_VCXCFGR2(x) ((((x) + 1) * 0x0010) + 0x4) +#define STM32_CSI_VCXCFGR2_DT1_SHIFT 0 +#define STM32_CSI_VCXCFGR2_DT1FT_SHIFT 8 +#define STM32_CSI_INPUT_BPP8 2 +#define STM32_CSI_INPUT_BPP10 3 +#define STM32_CSI_INPUT_BPP12 4 +#define STM32_CSI_INPUT_BPP14 5 +#define STM32_CSI_LMCFGR 0x0070 +#define STM32_CSI_LMCFGR_LANENB_SHIFT 8 +#define STM32_CSI_LMCFGR_DLMAP_SHIFT 16 +#define STM32_CSI_IER0 0x0080 +#define STM32_CSI_IER1 0x0084 +#define STM32_CSI_SR0 0x0090 +#define STM32_CSI_SR0_SYNCERRF BIT(30) +#define STM32_CSI_SR0_SPKTERRF BIT(28) +#define STM32_CSI_SR0_IDERRF BIT(27) +#define STM32_CSI_SR0_CECCERRF BIT(26) +#define STM32_CSI_SR0_ECCERRF BIT(25) +#define STM32_CSI_SR0_CRCERRF BIT(24) +#define STM32_CSI_SR0_CCFIFOFF BIT(21) +#define STM32_CSI_SR0_VCXSTATEF(x) BIT(17 + (x)) +#define STM32_CSI_SR1 0x0094 +#define STM32_CSI_SR1_ECTRLDL1F BIT(12) +#define STM32_CSI_SR1_ESYNCESCDL1F BIT(11) +#define STM32_CSI_SR1_EESCDL1F BIT(10) +#define STM32_CSI_SR1_ESOTSYNCDL1F BIT(9) +#define STM32_CSI_SR1_ESOTDL1F BIT(8) +#define STM32_CSI_SR1_ECTRLDL0F BIT(4) +#define STM32_CSI_SR1_ESYNCESCDL0F BIT(3) +#define STM32_CSI_SR1_EESCDL0F BIT(2) +#define STM32_CSI_SR1_ESOTSYNCDL0F BIT(1) +#define STM32_CSI_SR1_ESOTDL0F BIT(0) +#define STM32_CSI_FCR0 0x0100 +#define STM32_CSI_FCR1 0x0104 +#define STM32_CSI_SPDFR 0x0110 +#define STM32_CSI_DT_MASK 0x3f +#define STM32_CSI_VC_MASK 0x03 +#define STM32_CSI_ERR1 0x0114 +#define STM32_CSI_ERR1_IDVCERR_SHIFT 22 +#define STM32_CSI_ERR1_IDDTERR_SHIFT 16 +#define STM32_CSI_ERR1_CECCVCERR_SHIFT 14 +#define STM32_CSI_ERR1_CECCDTERR_SHIFT 8 +#define STM32_CSI_ERR1_CRCVCERR_SHIFT 6 +#define STM32_CSI_ERR1_CRCDTERR_SHIFT 0 +#define STM32_CSI_ERR2 0x0118 +#define STM32_CSI_ERR2_SYNCVCERR_SHIFT 18 +#define STM32_CSI_ERR2_SPKTVCERR_SHIFT 6 +#define STM32_CSI_ERR2_SPKTDTERR_SHIFT 0 +#define STM32_CSI_PRCR 0x1000 +#define STM32_CSI_PRCR_PEN BIT(1) +#define STM32_CSI_PMCR 0x1004 +#define STM32_CSI_PFCR 0x1008 +#define STM32_CSI_PFCR_CCFR_MASK GENMASK(5, 0) +#define STM32_CSI_PFCR_CCFR_SHIFT 0 +#define STM32_CSI_PFCR_HSFR_MASK GENMASK(14, 8) +#define STM32_CSI_PFCR_HSFR_SHIFT 8 +#define STM32_CSI_PFCR_DLD BIT(16) +#define STM32_CSI_PTCR0 0x1010 +#define STM32_CSI_PTCR0_TCKEN BIT(0) +#define STM32_CSI_PTCR1 0x1014 +#define STM32_CSI_PTCR1_TWM BIT(16) +#define STM32_CSI_PTCR1_TDI_MASK GENMASK(7, 0) +#define STM32_CSI_PTCR1_TDI_SHIFT 0 +#define STM32_CSI_PTSR 0x1018 + +#define STM32_CSI_LANES_MAX 2 + +#define STM32_CSI_SR0_ERRORS (STM32_CSI_SR0_SYNCERRF | STM32_CSI_SR0_SPKTERRF |\ + STM32_CSI_SR0_IDERRF | STM32_CSI_SR0_CECCERRF |\ + STM32_CSI_SR0_ECCERRF | STM32_CSI_SR0_CRCERRF |\ + STM32_CSI_SR0_CCFIFOFF) +#define STM32_CSI_SR1_DL0_ERRORS (STM32_CSI_SR1_ECTRLDL0F | STM32_CSI_SR1_ESYNCESCDL0F |\ + STM32_CSI_SR1_EESCDL0F | STM32_CSI_SR1_ESOTSYNCDL0F |\ + STM32_CSI_SR1_ESOTDL0F) +#define STM32_CSI_SR1_DL1_ERRORS (STM32_CSI_SR1_ECTRLDL1F | STM32_CSI_SR1_ESYNCESCDL1F |\ + STM32_CSI_SR1_EESCDL1F | STM32_CSI_SR1_ESOTSYNCDL1F |\ + STM32_CSI_SR1_ESOTDL1F) +#define STM32_CSI_SR1_ERRORS (STM32_CSI_SR1_DL0_ERRORS | STM32_CSI_SR1_DL1_ERRORS) + +enum stm32_csi_pads { + STM32_CSI_PAD_SINK, + STM32_CSI_PAD_SOURCE, + STM32_CSI_PAD_MAX, +}; + +struct stm32_csi_event { + u32 mask; + const char * const name; +}; + +static const struct stm32_csi_event stm32_csi_events_sr0[] = { + {STM32_CSI_SR0_SYNCERRF, "Synchronization error"}, + {STM32_CSI_SR0_SPKTERRF, "Short packet error"}, + {STM32_CSI_SR0_IDERRF, "Data type ID error"}, + {STM32_CSI_SR0_CECCERRF, "Corrected ECC error"}, + {STM32_CSI_SR0_ECCERRF, "ECC error"}, + {STM32_CSI_SR0_CRCERRF, "CRC error"}, + {STM32_CSI_SR0_CCFIFOFF, "Clk changer FIFO full error"}, +}; + +#define STM32_CSI_NUM_SR0_EVENTS ARRAY_SIZE(stm32_csi_events_sr0) + +static const struct stm32_csi_event stm32_csi_events_sr1[] = { + {STM32_CSI_SR1_ECTRLDL1F, "L1: D-PHY control error"}, + {STM32_CSI_SR1_ESYNCESCDL1F, + "L1: D-PHY low power data transmission synchro error"}, + {STM32_CSI_SR1_EESCDL1F, "L1: D-PHY escape entry error"}, + {STM32_CSI_SR1_ESOTSYNCDL1F, + "L1: Start of transmission synchro error"}, + {STM32_CSI_SR1_ESOTDL1F, "L1: Start of transmission error"}, + {STM32_CSI_SR1_ECTRLDL0F, "L0: D-PHY control error"}, + {STM32_CSI_SR1_ESYNCESCDL0F, + "L0: D-PHY low power data transmission synchro error"}, + {STM32_CSI_SR1_EESCDL0F, "L0: D-PHY escape entry error"}, + {STM32_CSI_SR1_ESOTSYNCDL0F, + "L0: Start of transmission synchro error"}, + {STM32_CSI_SR1_ESOTDL0F, "L0: Start of transmission error"}, +}; + +#define STM32_CSI_NUM_SR1_EVENTS ARRAY_SIZE(stm32_csi_events_sr1) + +enum stm32_csi_clk { + STM32_CSI_CLK_PCLK, + STM32_CSI_CLK_TXESC, + STM32_CSI_CLK_CSI2PHY, + STM32_CSI_CLK_NB, +}; + +static const char * const stm32_csi_clks_id[] = { + "pclk", + "txesc", + "csi2phy", +}; + +struct stm32_csi_dev { + struct device *dev; + + void __iomem *base; + + struct clk_bulk_data clks[STM32_CSI_CLK_NB]; + struct regulator_bulk_data supplies[2]; + + u8 lanes[STM32_CSI_LANES_MAX]; + u8 num_lanes; + + /* + * spinlock slock is used to protect to srX_counters tables being + * accessed from log_status and interrupt context + */ + spinlock_t slock; + + u32 sr0_counters[STM32_CSI_NUM_SR0_EVENTS]; + u32 sr1_counters[STM32_CSI_NUM_SR1_EVENTS]; + + struct v4l2_subdev sd; + struct v4l2_async_notifier notifier; + struct media_pad pads[STM32_CSI_PAD_MAX]; + + /* Remote source */ + struct v4l2_subdev *s_subdev; + u32 s_subdev_pad_nb; +}; + +struct stm32_csi_fmts { + u32 code; + u32 datatype; + u32 input_fmt; + u8 bpp; +}; + +#define FMT_MBUS_DT_DTFMT_BPP(mbus, dt, input, byteperpixel) \ + { \ + .code = MEDIA_BUS_FMT_##mbus, \ + .datatype = MIPI_CSI2_DT_##dt, \ + .input_fmt = STM32_CSI_INPUT_##input, \ + .bpp = byteperpixel, \ + } +static const struct stm32_csi_fmts stm32_csi_formats[] = { + /* YUV 422 8 bit */ + FMT_MBUS_DT_DTFMT_BPP(UYVY8_1X16, YUV422_8B, BPP8, 8), + FMT_MBUS_DT_DTFMT_BPP(YUYV8_1X16, YUV422_8B, BPP8, 8), + FMT_MBUS_DT_DTFMT_BPP(YVYU8_1X16, YUV422_8B, BPP8, 8), + FMT_MBUS_DT_DTFMT_BPP(VYUY8_1X16, YUV422_8B, BPP8, 8), + + /* Raw Bayer */ + /* 8 bit */ + FMT_MBUS_DT_DTFMT_BPP(SBGGR8_1X8, RAW8, BPP8, 8), + FMT_MBUS_DT_DTFMT_BPP(SGBRG8_1X8, RAW8, BPP8, 8), + FMT_MBUS_DT_DTFMT_BPP(SGRBG8_1X8, RAW8, BPP8, 8), + FMT_MBUS_DT_DTFMT_BPP(SRGGB8_1X8, RAW8, BPP8, 8), + /* 10 bit */ + FMT_MBUS_DT_DTFMT_BPP(SRGGB10_1X10, RAW10, BPP10, 10), + FMT_MBUS_DT_DTFMT_BPP(SGBRG10_1X10, RAW10, BPP10, 10), + FMT_MBUS_DT_DTFMT_BPP(SGRBG10_1X10, RAW10, BPP10, 10), + FMT_MBUS_DT_DTFMT_BPP(SRGGB10_1X10, RAW10, BPP10, 10), + /* 12 bit */ + FMT_MBUS_DT_DTFMT_BPP(SRGGB12_1X12, RAW12, BPP12, 12), + FMT_MBUS_DT_DTFMT_BPP(SGBRG12_1X12, RAW12, BPP12, 12), + FMT_MBUS_DT_DTFMT_BPP(SGRBG12_1X12, RAW12, BPP12, 12), + FMT_MBUS_DT_DTFMT_BPP(SRGGB12_1X12, RAW12, BPP12, 12), + /* 14 bit */ + FMT_MBUS_DT_DTFMT_BPP(SRGGB14_1X14, RAW14, BPP14, 14), + FMT_MBUS_DT_DTFMT_BPP(SGBRG14_1X14, RAW14, BPP14, 14), + FMT_MBUS_DT_DTFMT_BPP(SGRBG14_1X14, RAW14, BPP14, 14), + FMT_MBUS_DT_DTFMT_BPP(SRGGB14_1X14, RAW14, BPP14, 14), + + /* RGB 565 */ + FMT_MBUS_DT_DTFMT_BPP(RGB565_1X16, RGB565, BPP8, 8), + + /* JPEG (datatype isn't used) */ + FMT_MBUS_DT_DTFMT_BPP(JPEG_1X8, NULL, BPP8, 8), +}; + +struct stm32_csi_mbps_phy_reg { + unsigned int mbps; + unsigned int hsfreqrange; + unsigned int osc_freq_target; +}; + +/* + * Table describing configuration of the PHY depending on the + * intended Bit Rate. From table 5-8 Frequency Ranges and Defaults + * of the Synopsis DWC MIPI PHY databook + */ +static const struct stm32_csi_mbps_phy_reg snps_stm32mp25[] = { + { .mbps = 80, .hsfreqrange = 0x00, .osc_freq_target = 460 }, + { .mbps = 90, .hsfreqrange = 0x10, .osc_freq_target = 460 }, + { .mbps = 100, .hsfreqrange = 0x20, .osc_freq_target = 460 }, + { .mbps = 110, .hsfreqrange = 0x30, .osc_freq_target = 460 }, + { .mbps = 120, .hsfreqrange = 0x01, .osc_freq_target = 460 }, + { .mbps = 130, .hsfreqrange = 0x11, .osc_freq_target = 460 }, + { .mbps = 140, .hsfreqrange = 0x21, .osc_freq_target = 460 }, + { .mbps = 150, .hsfreqrange = 0x31, .osc_freq_target = 460 }, + { .mbps = 160, .hsfreqrange = 0x02, .osc_freq_target = 460 }, + { .mbps = 170, .hsfreqrange = 0x12, .osc_freq_target = 460 }, + { .mbps = 180, .hsfreqrange = 0x22, .osc_freq_target = 460 }, + { .mbps = 190, .hsfreqrange = 0x32, .osc_freq_target = 460 }, + { .mbps = 205, .hsfreqrange = 0x03, .osc_freq_target = 460 }, + { .mbps = 220, .hsfreqrange = 0x13, .osc_freq_target = 460 }, + { .mbps = 235, .hsfreqrange = 0x23, .osc_freq_target = 460 }, + { .mbps = 250, .hsfreqrange = 0x33, .osc_freq_target = 460 }, + { .mbps = 275, .hsfreqrange = 0x04, .osc_freq_target = 460 }, + { .mbps = 300, .hsfreqrange = 0x14, .osc_freq_target = 460 }, + { .mbps = 325, .hsfreqrange = 0x25, .osc_freq_target = 460 }, + { .mbps = 350, .hsfreqrange = 0x35, .osc_freq_target = 460 }, + { .mbps = 400, .hsfreqrange = 0x05, .osc_freq_target = 460 }, + { .mbps = 450, .hsfreqrange = 0x16, .osc_freq_target = 460 }, + { .mbps = 500, .hsfreqrange = 0x26, .osc_freq_target = 460 }, + { .mbps = 550, .hsfreqrange = 0x37, .osc_freq_target = 460 }, + { .mbps = 600, .hsfreqrange = 0x07, .osc_freq_target = 460 }, + { .mbps = 650, .hsfreqrange = 0x18, .osc_freq_target = 460 }, + { .mbps = 700, .hsfreqrange = 0x28, .osc_freq_target = 460 }, + { .mbps = 750, .hsfreqrange = 0x39, .osc_freq_target = 460 }, + { .mbps = 800, .hsfreqrange = 0x09, .osc_freq_target = 460 }, + { .mbps = 850, .hsfreqrange = 0x19, .osc_freq_target = 460 }, + { .mbps = 900, .hsfreqrange = 0x29, .osc_freq_target = 460 }, + { .mbps = 950, .hsfreqrange = 0x3a, .osc_freq_target = 460 }, + { .mbps = 1000, .hsfreqrange = 0x0a, .osc_freq_target = 460 }, + { .mbps = 1050, .hsfreqrange = 0x1a, .osc_freq_target = 460 }, + { .mbps = 1100, .hsfreqrange = 0x2a, .osc_freq_target = 460 }, + { .mbps = 1150, .hsfreqrange = 0x3b, .osc_freq_target = 460 }, + { .mbps = 1200, .hsfreqrange = 0x0b, .osc_freq_target = 460 }, + { .mbps = 1250, .hsfreqrange = 0x1b, .osc_freq_target = 460 }, + { .mbps = 1300, .hsfreqrange = 0x2b, .osc_freq_target = 460 }, + { .mbps = 1350, .hsfreqrange = 0x3c, .osc_freq_target = 460 }, + { .mbps = 1400, .hsfreqrange = 0x0c, .osc_freq_target = 460 }, + { .mbps = 1450, .hsfreqrange = 0x1c, .osc_freq_target = 460 }, + { .mbps = 1500, .hsfreqrange = 0x2c, .osc_freq_target = 460 }, + { .mbps = 1550, .hsfreqrange = 0x3d, .osc_freq_target = 285 }, + { .mbps = 1600, .hsfreqrange = 0x0d, .osc_freq_target = 295 }, + { .mbps = 1650, .hsfreqrange = 0x1d, .osc_freq_target = 304 }, + { .mbps = 1700, .hsfreqrange = 0x2e, .osc_freq_target = 313 }, + { .mbps = 1750, .hsfreqrange = 0x3e, .osc_freq_target = 322 }, + { .mbps = 1800, .hsfreqrange = 0x0e, .osc_freq_target = 331 }, + { .mbps = 1850, .hsfreqrange = 0x1e, .osc_freq_target = 341 }, + { .mbps = 1900, .hsfreqrange = 0x2f, .osc_freq_target = 350 }, + { .mbps = 1950, .hsfreqrange = 0x3f, .osc_freq_target = 359 }, + { .mbps = 2000, .hsfreqrange = 0x0f, .osc_freq_target = 368 }, + { .mbps = 2050, .hsfreqrange = 0x40, .osc_freq_target = 377 }, + { .mbps = 2100, .hsfreqrange = 0x41, .osc_freq_target = 387 }, + { .mbps = 2150, .hsfreqrange = 0x42, .osc_freq_target = 396 }, + { .mbps = 2200, .hsfreqrange = 0x43, .osc_freq_target = 405 }, + { .mbps = 2250, .hsfreqrange = 0x44, .osc_freq_target = 414 }, + { .mbps = 2300, .hsfreqrange = 0x45, .osc_freq_target = 423 }, + { .mbps = 2350, .hsfreqrange = 0x46, .osc_freq_target = 432 }, + { .mbps = 2400, .hsfreqrange = 0x47, .osc_freq_target = 442 }, + { .mbps = 2450, .hsfreqrange = 0x48, .osc_freq_target = 451 }, + { .mbps = 2500, .hsfreqrange = 0x49, .osc_freq_target = 460 }, + { /* sentinel */ } +}; + +static const struct v4l2_mbus_framefmt fmt_default = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_RGB565_1X16, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_REC709, + .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT, + .quantization = V4L2_QUANTIZATION_DEFAULT, + .xfer_func = V4L2_XFER_FUNC_DEFAULT, +}; + +static const struct stm32_csi_fmts *stm32_csi_code_to_fmt(unsigned int code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(stm32_csi_formats); i++) + if (stm32_csi_formats[i].code == code) + return &stm32_csi_formats[i]; + + return NULL; +} + +static inline struct stm32_csi_dev *to_csidev(struct v4l2_subdev *sd) +{ + return container_of(sd, struct stm32_csi_dev, sd); +} + +static int stm32_csi_setup_lane_merger(struct stm32_csi_dev *csidev) +{ + u32 lmcfgr = 0; + int i; + + for (i = 0; i < csidev->num_lanes; i++) { + if (!csidev->lanes[i] || csidev->lanes[i] > STM32_CSI_LANES_MAX) { + dev_err(csidev->dev, "Invalid lane id (%d)\n", csidev->lanes[i]); + return -EINVAL; + } + lmcfgr |= (csidev->lanes[i] << ((i * 4) + STM32_CSI_LMCFGR_DLMAP_SHIFT)); + } + + lmcfgr |= (csidev->num_lanes << STM32_CSI_LMCFGR_LANENB_SHIFT); + + writel_relaxed(lmcfgr, csidev->base + STM32_CSI_LMCFGR); + + return 0; +} + +static void stm32_csi_phy_reg_write(struct stm32_csi_dev *csidev, + u32 addr, u32 val) +{ + /* Based on sequence described at section 5.2.3.2 of DesignWave document */ + /* For writing the 4-bit testcode MSBs */ + /* Set testen to high */ + writel_relaxed(STM32_CSI_PTCR1_TWM, csidev->base + STM32_CSI_PTCR1); + + /* Set testclk to high */ + writel_relaxed(STM32_CSI_PTCR0_TCKEN, csidev->base + STM32_CSI_PTCR0); + + /* Place 0x00 in testdin */ + writel_relaxed(STM32_CSI_PTCR1_TWM, csidev->base + STM32_CSI_PTCR1); + + /* + * Set testclk to low (with the falling edge on testclk, the testdin + * signal content is latched internally) + */ + writel_relaxed(0, csidev->base + STM32_CSI_PTCR0); + + /* Set testen to low */ + writel_relaxed(0, csidev->base + STM32_CSI_PTCR1); + + /* Place the 8-bit word corresponding to the testcode MSBs in testdin */ + writel_relaxed(((addr >> 8) & STM32_CSI_PTCR1_TDI_MASK) << STM32_CSI_PTCR1_TDI_SHIFT, + csidev->base + STM32_CSI_PTCR1); + + /* Set testclk to high */ + writel_relaxed(STM32_CSI_PTCR0_TCKEN, csidev->base + STM32_CSI_PTCR0); + + /* For writing the 8-bit testcode LSBs */ + /* Set testclk to low */ + writel_relaxed(0, csidev->base + STM32_CSI_PTCR0); + + /* Set testen to high */ + writel_relaxed(STM32_CSI_PTCR1_TWM, csidev->base + STM32_CSI_PTCR1); + + /* Set testclk to high */ + writel_relaxed(STM32_CSI_PTCR0_TCKEN, csidev->base + STM32_CSI_PTCR0); + + /* Place the 8-bit word test data in testdin */ + writel_relaxed((addr & STM32_CSI_PTCR1_TDI_MASK) << + STM32_CSI_PTCR1_TDI_SHIFT | STM32_CSI_PTCR1_TWM, + csidev->base + STM32_CSI_PTCR1); + + /* + * Set testclk to low (with the falling edge on testclk, the testdin + * signal content is latched internally) + */ + writel_relaxed(0, csidev->base + STM32_CSI_PTCR0); + + /* Set testen to low */ + writel_relaxed(0, csidev->base + STM32_CSI_PTCR1); + + /* For writing the data */ + /* Place the 8-bit word corresponding to the page offset in testdin */ + writel_relaxed((val & STM32_CSI_PTCR1_TDI_MASK) << STM32_CSI_PTCR1_TDI_SHIFT, + csidev->base + STM32_CSI_PTCR1); + + /* Set testclk to high (test data is programmed internally */ + writel_relaxed(STM32_CSI_PTCR0_TCKEN, csidev->base + STM32_CSI_PTCR0); + + /* Finish by setting testclk to low */ + writel_relaxed(0, csidev->base + STM32_CSI_PTCR0); +} + +static int stm32_csi_start(struct stm32_csi_dev *csidev, + struct v4l2_subdev_state *state) +{ + const struct stm32_csi_mbps_phy_reg *phy_regs; + struct v4l2_mbus_framefmt *sink_fmt; + const struct stm32_csi_fmts *fmt; + unsigned long phy_clk_frate; + unsigned int mbps; + u32 lanes_ie = 0; + u32 lanes_en = 0; + s64 link_freq; + int ret; + u32 ccfr; + + dev_dbg(csidev->dev, "Starting the CSI2\n"); + + /* Get the bpp value on pad0 (input of CSI) */ + sink_fmt = v4l2_subdev_state_get_format(state, STM32_CSI_PAD_SINK); + fmt = stm32_csi_code_to_fmt(sink_fmt->code); + + /* Get the remote sensor link frequency */ + if (!csidev->s_subdev) + return -EIO; + + link_freq = v4l2_get_link_freq(csidev->s_subdev->ctrl_handler, + fmt->bpp, 2 * csidev->num_lanes); + if (link_freq < 0) + return link_freq; + + /* MBPS is expressed in Mbps, hence link_freq / 100000 * 2 */ + mbps = div_s64(link_freq, 500000); + dev_dbg(csidev->dev, "Computed Mbps: %u\n", mbps); + + for (phy_regs = snps_stm32mp25; phy_regs->mbps != 0; phy_regs++) + if (phy_regs->mbps >= mbps) + break; + + if (!phy_regs->mbps) { + dev_err(csidev->dev, "Unsupported PHY speed (%u Mbps)", mbps); + return -ERANGE; + } + + dev_dbg(csidev->dev, "PHY settings: (%u Mbps, %u HS FRange, %u OSC Freq)\n", + phy_regs->mbps, phy_regs->hsfreqrange, + phy_regs->osc_freq_target); + + /* Prepare lanes related configuration bits */ + lanes_ie |= STM32_CSI_SR1_DL0_ERRORS; + lanes_en |= STM32_CSI_PCR_DL0EN; + if (csidev->num_lanes == 2) { + lanes_ie |= STM32_CSI_SR1_DL1_ERRORS; + lanes_en |= STM32_CSI_PCR_DL1EN; + } + + ret = pm_runtime_get_sync(csidev->dev); + if (ret < 0) + return ret; + + /* Retrieve CSI2PHY clock rate to compute CCFR value */ + phy_clk_frate = clk_get_rate(csidev->clks[STM32_CSI_CLK_CSI2PHY].clk); + if (!phy_clk_frate) { + pm_runtime_put(csidev->dev); + dev_err(csidev->dev, "CSI2PHY clock rate invalid (0)\n"); + return ret; + } + + ret = stm32_csi_setup_lane_merger(csidev); + if (ret) { + pm_runtime_put(csidev->dev); + return ret; + } + + /* Enable the CSI */ + writel_relaxed(STM32_CSI_CR_CSIEN, csidev->base + STM32_CSI_CR); + + /* Enable some global CSI related interrupts - bits are same as SR0 */ + writel_relaxed(STM32_CSI_SR0_ERRORS, csidev->base + STM32_CSI_IER0); + + /* Enable lanes related error interrupts */ + writel_relaxed(lanes_ie, csidev->base + STM32_CSI_IER1); + + /* Initialization of the D-PHY */ + /* Stop the D-PHY */ + writel_relaxed(0, csidev->base + STM32_CSI_PRCR); + + /* Keep the D-PHY in power down state */ + writel_relaxed(0, csidev->base + STM32_CSI_PCR); + + /* Enable testclr clock during 15ns */ + writel_relaxed(STM32_CSI_PTCR0_TCKEN, csidev->base + STM32_CSI_PTCR0); + udelay(1); + writel_relaxed(0, csidev->base + STM32_CSI_PTCR0); + + /* Set hsfreqrange */ + phy_clk_frate /= 1000000; + ccfr = (phy_clk_frate - 17) * 4; + writel_relaxed((ccfr << STM32_CSI_PFCR_CCFR_SHIFT) | + (phy_regs->hsfreqrange << STM32_CSI_PFCR_HSFR_SHIFT), + csidev->base + STM32_CSI_PFCR); + + /* set reg @08 deskew_polarity_rw 1'b1 */ + stm32_csi_phy_reg_write(csidev, 0x08, 0x38); + + /* set reg @0xE4 counter_for_des_en_config_if_rx 0x10 + DLL prog EN */ + /* This is because 13<= cfgclkfreqrange[5:0]<=38 */ + stm32_csi_phy_reg_write(csidev, 0xe4, 0x11); + + /* set reg @0xe2 & reg @0xe3 value DLL target oscilation freq */ + /* Based on the table page 77, osc_freq_target */ + stm32_csi_phy_reg_write(csidev, 0xe2, phy_regs->osc_freq_target & 0xFF); + stm32_csi_phy_reg_write(csidev, 0xe3, (phy_regs->osc_freq_target >> 8) & 0x0F); + + writel_relaxed(STM32_CSI_PFCR_DLD | readl_relaxed(csidev->base + STM32_CSI_PFCR), + csidev->base + STM32_CSI_PFCR); + + /* Enable Lanes */ + writel_relaxed(lanes_en | STM32_CSI_PCR_CLEN, csidev->base + STM32_CSI_PCR); + writel_relaxed(lanes_en | STM32_CSI_PCR_CLEN | STM32_CSI_PCR_PWRDOWN, + csidev->base + STM32_CSI_PCR); + + writel_relaxed(STM32_CSI_PRCR_PEN, csidev->base + STM32_CSI_PRCR); + + /* Remove the force */ + writel_relaxed(0, csidev->base + STM32_CSI_PMCR); + + return ret; +} + +static void stm32_csi_stop(struct stm32_csi_dev *csidev) +{ + dev_dbg(csidev->dev, "Stopping the CSI2\n"); + + /* Disable the D-PHY */ + writel_relaxed(0, csidev->base + STM32_CSI_PCR); + + /* Disable ITs */ + writel_relaxed(0, csidev->base + STM32_CSI_IER0); + writel_relaxed(0, csidev->base + STM32_CSI_IER1); + + /* Disable the CSI */ + writel_relaxed(0, csidev->base + STM32_CSI_CR); + + pm_runtime_put(csidev->dev); +} + +static int stm32_csi_start_vc(struct stm32_csi_dev *csidev, + struct v4l2_subdev_state *state, u32 vc) +{ + struct v4l2_mbus_framefmt *mbus_fmt; + const struct stm32_csi_fmts *fmt; + u32 cfgr1 = 0; + int ret = 0; + u32 status; + + mbus_fmt = v4l2_subdev_state_get_format(state, STM32_CSI_PAD_SOURCE); + fmt = stm32_csi_code_to_fmt(mbus_fmt->code); + + /* If the mbus code is JPEG, don't enable filtering */ + if (mbus_fmt->code == MEDIA_BUS_FMT_JPEG_1X8) { + cfgr1 |= STM32_CSI_VCXCFGR1_ALLDT; + cfgr1 |= fmt->input_fmt << STM32_CSI_VCXCFGR1_CDTFT_SHIFT; + dev_dbg(csidev->dev, "VC%d: enable AllDT mode\n", vc); + } else { + cfgr1 |= fmt->datatype << STM32_CSI_VCXCFGR1_DT0_SHIFT; + cfgr1 |= fmt->input_fmt << STM32_CSI_VCXCFGR1_DT0FT_SHIFT; + cfgr1 |= STM32_CSI_VCXCFGR1_DT0EN; + dev_dbg(csidev->dev, "VC%d: enable DT0(0x%x)/DT0FT(0x%x)\n", + vc, fmt->datatype, fmt->input_fmt); + } + writel_relaxed(cfgr1, csidev->base + STM32_CSI_VCXCFGR1(vc)); + + /* Enable processing of the virtual-channel and wait for its status */ + writel_relaxed(STM32_CSI_CR_VCXSTART(vc) | STM32_CSI_CR_CSIEN, + csidev->base + STM32_CSI_CR); + + ret = readl_relaxed_poll_timeout(csidev->base + STM32_CSI_SR0, + status, + status & STM32_CSI_SR0_VCXSTATEF(vc), + 1000, 1000000); + if (ret) { + dev_err(csidev->dev, "failed to start VC(%d)\n", vc); + return ret; + } + + return 0; +} + +static int stm32_csi_stop_vc(struct stm32_csi_dev *csidev, u32 vc) +{ + int ret = 0; + u32 status; + + /* Stop the Virtual Channel */ + writel_relaxed(STM32_CSI_CR_VCXSTOP(vc) | STM32_CSI_CR_CSIEN, + csidev->base + STM32_CSI_CR); + + ret = readl_relaxed_poll_timeout(csidev->base + STM32_CSI_SR0, + status, + !(status & STM32_CSI_SR0_VCXSTATEF(vc)), + 1000, 1000000); + if (ret) { + dev_err(csidev->dev, "failed to stop VC(%d)\n", vc); + return ret; + } + + /* Disable all DTs */ + writel_relaxed(0, csidev->base + STM32_CSI_VCXCFGR1(vc)); + writel_relaxed(0, csidev->base + STM32_CSI_VCXCFGR2(vc)); + + return 0; +} + +static int stm32_csi_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct stm32_csi_dev *csidev = to_csidev(sd); + int ret; + + ret = v4l2_subdev_disable_streams(csidev->s_subdev, + csidev->s_subdev_pad_nb, BIT_ULL(0)); + if (ret) + return ret; + + /* Stop the VC0 */ + ret = stm32_csi_stop_vc(csidev, 0); + if (ret) + dev_err(csidev->dev, "Failed to stop VC0\n"); + + stm32_csi_stop(csidev); + + return 0; +} + +static int stm32_csi_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct stm32_csi_dev *csidev = to_csidev(sd); + int ret; + + ret = stm32_csi_start(csidev, state); + if (ret) + return ret; + + /* Configure & start the VC0 */ + ret = stm32_csi_start_vc(csidev, state, 0); + if (ret) { + dev_err(csidev->dev, "Failed to start VC0\n"); + stm32_csi_stop(csidev); + return ret; + } + + ret = v4l2_subdev_enable_streams(csidev->s_subdev, + csidev->s_subdev_pad_nb, BIT_ULL(0)); + if (ret) { + stm32_csi_stop_vc(csidev, 0); + stm32_csi_stop(csidev); + return ret; + } + + return 0; +} + +static int stm32_csi_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + int i; + + for (i = 0; i < sd->entity.num_pads; i++) + *v4l2_subdev_state_get_format(state, i) = fmt_default; + + return 0; +} + +static int stm32_csi_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(stm32_csi_formats)) + return -EINVAL; + + code->code = stm32_csi_formats[code->index].code; + return 0; +} + +static int stm32_csi_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct stm32_csi_dev *csidev = to_csidev(sd); + struct v4l2_mbus_framefmt *framefmt; + const struct stm32_csi_fmts *fmt; + + fmt = stm32_csi_code_to_fmt(format->format.code); + if (!fmt) { + dev_dbg(csidev->dev, "Unsupported code %d, use default\n", + format->format.code); + format->format.code = fmt_default.code; + } + + framefmt = v4l2_subdev_state_get_format(state, STM32_CSI_PAD_SINK); + + if (format->pad == STM32_CSI_PAD_SOURCE) + format->format = *framefmt; + else + *framefmt = format->format; + + framefmt = v4l2_subdev_state_get_format(state, STM32_CSI_PAD_SOURCE); + *framefmt = format->format; + + return 0; +} + +static int stm32_csi_log_status(struct v4l2_subdev *sd) +{ + struct stm32_csi_dev *csidev = to_csidev(sd); + unsigned long flags; + unsigned int i; + + spin_lock_irqsave(&csidev->slock, flags); + + for (i = 0; i < STM32_CSI_NUM_SR0_EVENTS; i++) { + if (csidev->sr0_counters[i]) + dev_info(csidev->dev, "%s events: %d\n", + stm32_csi_events_sr0[i].name, + csidev->sr0_counters[i]); + } + + for (i = 0; i < STM32_CSI_NUM_SR1_EVENTS; i++) { + if (csidev->sr1_counters[i]) + dev_info(csidev->dev, "%s events: %d\n", + stm32_csi_events_sr1[i].name, + csidev->sr1_counters[i]); + } + + spin_unlock_irqrestore(&csidev->slock, flags); + + return 0; +} + +static const struct v4l2_subdev_core_ops stm32_csi_core_ops = { + .log_status = stm32_csi_log_status, +}; + +static const struct v4l2_subdev_video_ops stm32_csi_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_pad_ops stm32_csi_pad_ops = { + .enum_mbus_code = stm32_csi_enum_mbus_code, + .set_fmt = stm32_csi_set_pad_format, + .get_fmt = v4l2_subdev_get_fmt, + .enable_streams = stm32_csi_enable_streams, + .disable_streams = stm32_csi_disable_streams, +}; + +static const struct v4l2_subdev_ops stm32_csi_subdev_ops = { + .core = &stm32_csi_core_ops, + .pad = &stm32_csi_pad_ops, + .video = &stm32_csi_video_ops, +}; + +static const struct v4l2_subdev_internal_ops stm32_csi_subdev_internal_ops = { + .init_state = stm32_csi_init_state, +}; + +static int stm32_csi_async_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *s_subdev, + struct v4l2_async_connection *asd) +{ + struct v4l2_subdev *sd = notifier->sd; + struct stm32_csi_dev *csidev = to_csidev(sd); + int remote_pad; + + remote_pad = media_entity_get_fwnode_pad(&s_subdev->entity, + s_subdev->fwnode, + MEDIA_PAD_FL_SOURCE); + if (remote_pad < 0) { + dev_err(csidev->dev, "Couldn't find output pad for subdev %s\n", + s_subdev->name); + return remote_pad; + } + + csidev->s_subdev = s_subdev; + csidev->s_subdev_pad_nb = remote_pad; + + return media_create_pad_link(&csidev->s_subdev->entity, + remote_pad, &csidev->sd.entity, + STM32_CSI_PAD_SINK, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); +} + +static const struct v4l2_async_notifier_operations stm32_csi_notifier_ops = { + .bound = stm32_csi_async_bound, +}; + +static irqreturn_t stm32_csi_irq_thread(int irq, void *arg) +{ + struct stm32_csi_dev *csidev = arg; + unsigned long flags; + u32 sr0, sr1; + int i; + + sr0 = readl_relaxed(csidev->base + STM32_CSI_SR0); + sr1 = readl_relaxed(csidev->base + STM32_CSI_SR1); + + /* Clear interrupt */ + writel_relaxed(sr0 & STM32_CSI_SR0_ERRORS, + csidev->base + STM32_CSI_FCR0); + writel_relaxed(sr1 & STM32_CSI_SR1_ERRORS, + csidev->base + STM32_CSI_FCR1); + + spin_lock_irqsave(&csidev->slock, flags); + + for (i = 0; i < STM32_CSI_NUM_SR0_EVENTS; i++) + if (sr0 & stm32_csi_events_sr0[i].mask) + csidev->sr0_counters[i]++; + + for (i = 0; i < STM32_CSI_NUM_SR1_EVENTS; i++) + if (sr1 & stm32_csi_events_sr1[i].mask) + csidev->sr1_counters[i]++; + + spin_unlock_irqrestore(&csidev->slock, flags); + + return IRQ_HANDLED; +} + +static int stm32_csi_get_resources(struct stm32_csi_dev *csidev, + struct platform_device *pdev) +{ + int irq, ret, i; + + csidev->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); + if (IS_ERR(csidev->base)) + return dev_err_probe(&pdev->dev, PTR_ERR(csidev->base), + "Failed to ioremap resource\n"); + + for (i = 0; i < STM32_CSI_CLK_NB; i++) + csidev->clks[i].id = stm32_csi_clks_id[i]; + + ret = devm_clk_bulk_get(&pdev->dev, STM32_CSI_CLK_NB, + csidev->clks); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "Couldn't get clks\n"); + + csidev->supplies[0].supply = "vdd"; + csidev->supplies[1].supply = "vdda18"; + ret = devm_regulator_bulk_get(&pdev->dev, ARRAY_SIZE(csidev->supplies), + csidev->supplies); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to request regulator vdd\n"); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + stm32_csi_irq_thread, IRQF_ONESHOT, + dev_name(&pdev->dev), csidev); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Unable to request irq"); + + return 0; +} + +static int stm32_csi_parse_dt(struct stm32_csi_dev *csidev) +{ + struct v4l2_fwnode_endpoint v4l2_ep = { .bus_type = V4L2_MBUS_CSI2_DPHY }; + struct v4l2_async_connection *asd; + struct fwnode_handle *ep; + int ret; + + /* Get bus characteristics from devicetree */ + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(csidev->dev), 0, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!ep) { + dev_err(csidev->dev, "Could not find the endpoint\n"); + return -ENODEV; + } + + ret = v4l2_fwnode_endpoint_parse(ep, &v4l2_ep); + fwnode_handle_put(ep); + if (ret) { + dev_err(csidev->dev, "Could not parse v4l2 endpoint\n"); + return ret; + } + + csidev->num_lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes; + if (csidev->num_lanes > STM32_CSI_LANES_MAX) { + dev_err(csidev->dev, "Unsupported number of data-lanes: %d\n", + csidev->num_lanes); + return -EINVAL; + } + + memcpy(csidev->lanes, v4l2_ep.bus.mipi_csi2.data_lanes, + sizeof(csidev->lanes)); + + ep = fwnode_graph_get_next_endpoint(dev_fwnode(csidev->dev), NULL); + if (!ep) { + dev_err(csidev->dev, "Failed to get next endpoint\n"); + return -EINVAL; + } + + v4l2_async_subdev_nf_init(&csidev->notifier, &csidev->sd); + + asd = v4l2_async_nf_add_fwnode_remote(&csidev->notifier, ep, + struct v4l2_async_connection); + + fwnode_handle_put(ep); + + if (IS_ERR(asd)) { + dev_err(csidev->dev, "Failed to add fwnode remote subdev\n"); + return PTR_ERR(asd); + } + + csidev->notifier.ops = &stm32_csi_notifier_ops; + + ret = v4l2_async_nf_register(&csidev->notifier); + if (ret) { + dev_err(csidev->dev, "Failed to register notifier\n"); + v4l2_async_nf_cleanup(&csidev->notifier); + return ret; + } + + return ret; +} + +static int stm32_csi_probe(struct platform_device *pdev) +{ + struct stm32_csi_dev *csidev; + struct reset_control *rstc; + int ret; + + csidev = devm_kzalloc(&pdev->dev, sizeof(*csidev), GFP_KERNEL); + if (!csidev) + return -ENOMEM; + + platform_set_drvdata(pdev, csidev); + csidev->dev = &pdev->dev; + + spin_lock_init(&csidev->slock); + + ret = stm32_csi_get_resources(csidev, pdev); + if (ret) + goto err_free_priv; + + ret = stm32_csi_parse_dt(csidev); + if (ret) + goto err_free_priv; + + csidev->sd.owner = THIS_MODULE; + csidev->sd.dev = &pdev->dev; + csidev->sd.internal_ops = &stm32_csi_subdev_internal_ops; + v4l2_subdev_init(&csidev->sd, &stm32_csi_subdev_ops); + v4l2_set_subdevdata(&csidev->sd, &pdev->dev); + snprintf(csidev->sd.name, sizeof(csidev->sd.name), "%s", + dev_name(&pdev->dev)); + + /* Create our media pads */ + csidev->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + csidev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + csidev->pads[STM32_CSI_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + csidev->pads[STM32_CSI_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&csidev->sd.entity, STM32_CSI_PAD_MAX, + csidev->pads); + if (ret) + goto err_cleanup; + + ret = v4l2_subdev_init_finalize(&csidev->sd); + if (ret < 0) + goto err_cleanup; + + ret = v4l2_async_register_subdev(&csidev->sd); + if (ret < 0) + goto err_cleanup; + + /* Reset device */ + rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); + if (IS_ERR(rstc)) { + ret = dev_err_probe(&pdev->dev, PTR_ERR(rstc), + "Couldn't get reset control\n"); + goto err_cleanup; + } + + ret = reset_control_assert(rstc); + if (ret) { + ret = dev_err_probe(&pdev->dev, ret, + "Failed to assert the reset line\n"); + goto err_cleanup; + } + + usleep_range(3000, 5000); + + ret = reset_control_deassert(rstc); + if (ret) { + ret = dev_err_probe(&pdev->dev, ret, + "Failed to deassert the reset line\n"); + goto err_cleanup; + } + + pm_runtime_enable(&pdev->dev); + + dev_info(&pdev->dev, + "Probed CSI with %u lanes\n", csidev->num_lanes); + + return 0; + +err_cleanup: + v4l2_async_nf_cleanup(&csidev->notifier); +err_free_priv: + return ret; +} + +static void stm32_csi_remove(struct platform_device *pdev) +{ + struct stm32_csi_dev *csidev = platform_get_drvdata(pdev); + + v4l2_async_unregister_subdev(&csidev->sd); + + pm_runtime_disable(&pdev->dev); +} + +static int stm32_csi_runtime_suspend(struct device *dev) +{ + struct stm32_csi_dev *csidev = dev_get_drvdata(dev); + int ret; + + clk_bulk_disable_unprepare(STM32_CSI_CLK_NB, csidev->clks); + + ret = regulator_bulk_disable(ARRAY_SIZE(csidev->supplies), + csidev->supplies); + if (ret < 0) + dev_err(dev, "cannot disable regulators %d\n", ret); + + return 0; +} + +static int stm32_csi_runtime_resume(struct device *dev) +{ + struct stm32_csi_dev *csidev = dev_get_drvdata(dev); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(csidev->supplies), + csidev->supplies); + if (ret) + goto error_out; + + ret = clk_bulk_prepare_enable(STM32_CSI_CLK_NB, csidev->clks); + if (ret) + goto error_disable_supplies; + + return 0; + +error_disable_supplies: + ret = regulator_bulk_disable(ARRAY_SIZE(csidev->supplies), csidev->supplies); + if (ret < 0) + dev_err(dev, "cannot disable regulators %d\n", ret); +error_out: + dev_err(csidev->dev, "Failed to resume: %d\n", ret); + + return ret; +} + +static const struct of_device_id stm32_csi_of_table[] = { + { .compatible = "st,stm32mp25-csi", }, + { /* end node */ }, +}; +MODULE_DEVICE_TABLE(of, stm32_csi_of_table); + +static const struct dev_pm_ops stm32_csi_pm_ops = { + RUNTIME_PM_OPS(stm32_csi_runtime_suspend, + stm32_csi_runtime_resume, NULL) +}; + +static struct platform_driver stm32_csi_driver = { + .driver = { + .name = "stm32-csi", + .of_match_table = stm32_csi_of_table, + .pm = pm_ptr(&stm32_csi_pm_ops), + }, + .probe = stm32_csi_probe, + .remove = stm32_csi_remove, +}; + +module_platform_driver(stm32_csi_driver); + +MODULE_AUTHOR("Alain Volmat <alain.volmat@foss.st.com>"); +MODULE_DESCRIPTION("STM32 CSI controller"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile b/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile index 8920d9388a21..159105fb40b8 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -stm32-dcmipp-y := dcmipp-core.o dcmipp-common.o dcmipp-parallel.o dcmipp-byteproc.o dcmipp-bytecap.o +stm32-dcmipp-y := dcmipp-core.o dcmipp-common.o dcmipp-input.o dcmipp-byteproc.o dcmipp-bytecap.o obj-$(CONFIG_VIDEO_STM32_DCMIPP) += stm32-dcmipp.o diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c index 7edd49bfe7e5..1c1b6b48918e 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c @@ -56,15 +56,32 @@ struct dcmipp_bytecap_pix_map { static const struct dcmipp_bytecap_pix_map dcmipp_bytecap_pix_map_list[] = { PIXMAP_MBUS_PFMT(RGB565_2X8_LE, RGB565), + PIXMAP_MBUS_PFMT(RGB565_1X16, RGB565), PIXMAP_MBUS_PFMT(YUYV8_2X8, YUYV), + PIXMAP_MBUS_PFMT(YUYV8_1X16, YUYV), PIXMAP_MBUS_PFMT(YVYU8_2X8, YVYU), + PIXMAP_MBUS_PFMT(YVYU8_1X16, YVYU), PIXMAP_MBUS_PFMT(UYVY8_2X8, UYVY), + PIXMAP_MBUS_PFMT(UYVY8_1X16, UYVY), PIXMAP_MBUS_PFMT(VYUY8_2X8, VYUY), + PIXMAP_MBUS_PFMT(VYUY8_1X16, VYUY), PIXMAP_MBUS_PFMT(Y8_1X8, GREY), PIXMAP_MBUS_PFMT(SBGGR8_1X8, SBGGR8), PIXMAP_MBUS_PFMT(SGBRG8_1X8, SGBRG8), PIXMAP_MBUS_PFMT(SGRBG8_1X8, SGRBG8), PIXMAP_MBUS_PFMT(SRGGB8_1X8, SRGGB8), + PIXMAP_MBUS_PFMT(SBGGR10_1X10, SBGGR10), + PIXMAP_MBUS_PFMT(SGBRG10_1X10, SGBRG10), + PIXMAP_MBUS_PFMT(SGRBG10_1X10, SGRBG10), + PIXMAP_MBUS_PFMT(SRGGB10_1X10, SRGGB10), + PIXMAP_MBUS_PFMT(SBGGR12_1X12, SBGGR12), + PIXMAP_MBUS_PFMT(SGBRG12_1X12, SGBRG12), + PIXMAP_MBUS_PFMT(SGRBG12_1X12, SGRBG12), + PIXMAP_MBUS_PFMT(SRGGB12_1X12, SRGGB12), + PIXMAP_MBUS_PFMT(SBGGR14_1X14, SBGGR14), + PIXMAP_MBUS_PFMT(SGBRG14_1X14, SGBRG14), + PIXMAP_MBUS_PFMT(SGRBG14_1X14, SGRBG14), + PIXMAP_MBUS_PFMT(SRGGB14_1X14, SRGGB14), PIXMAP_MBUS_PFMT(JPEG_1X8, JPEG), }; @@ -112,6 +129,7 @@ struct dcmipp_bytecap_device { u32 sequence; struct media_pipeline pipe; struct v4l2_subdev *s_subdev; + u32 s_subdev_pad_nb; enum dcmipp_state state; @@ -250,34 +268,34 @@ static int dcmipp_bytecap_enum_fmt_vid_cap(struct file *file, void *priv, { const struct dcmipp_bytecap_pix_map *vpix; unsigned int index = f->index; - unsigned int i; + unsigned int i, prev_pixelformat = 0; - if (f->mbus_code) { - /* - * If a media bus code is specified, only enumerate formats - * compatible with it. - */ - for (i = 0; i < ARRAY_SIZE(dcmipp_bytecap_pix_map_list); i++) { - vpix = &dcmipp_bytecap_pix_map_list[i]; - if (vpix->code != f->mbus_code) - continue; + /* + * List up all formats (or only ones matching f->mbus_code), taking + * care of removing duplicated entries (due to support of both + * parallel & csi 16 bits formats + */ + for (i = 0; i < ARRAY_SIZE(dcmipp_bytecap_pix_map_list); i++) { + vpix = &dcmipp_bytecap_pix_map_list[i]; + /* Skip formats not matching requested mbus code */ + if (f->mbus_code && vpix->code != f->mbus_code) + continue; - if (index == 0) - break; + /* Skip duplicated pixelformat */ + if (vpix->pixelformat == prev_pixelformat) + continue; - index--; - } + prev_pixelformat = vpix->pixelformat; - if (i == ARRAY_SIZE(dcmipp_bytecap_pix_map_list)) - return -EINVAL; - } else { - /* Otherwise, enumerate all formats. */ - if (f->index >= ARRAY_SIZE(dcmipp_bytecap_pix_map_list)) - return -EINVAL; + if (index == 0) + break; - vpix = &dcmipp_bytecap_pix_map_list[f->index]; + index--; } + if (i == ARRAY_SIZE(dcmipp_bytecap_pix_map_list)) + return -EINVAL; + f->pixelformat = vpix->pixelformat; return 0; @@ -337,33 +355,6 @@ static const struct v4l2_ioctl_ops dcmipp_bytecap_ioctl_ops = { .vidioc_streamoff = vb2_ioctl_streamoff, }; -static int dcmipp_pipeline_s_stream(struct dcmipp_bytecap_device *vcap, - int state) -{ - struct media_pad *pad; - int ret; - - /* - * Get source subdev - since link is IMMUTABLE, pointer is cached - * within the dcmipp_bytecap_device structure - */ - if (!vcap->s_subdev) { - pad = media_pad_remote_pad_first(&vcap->vdev.entity.pads[0]); - if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) - return -EINVAL; - vcap->s_subdev = media_entity_to_v4l2_subdev(pad->entity); - } - - ret = v4l2_subdev_call(vcap->s_subdev, video, s_stream, state); - if (ret < 0) { - dev_err(vcap->dev, "failed to %s streaming (%d)\n", - state ? "start" : "stop", ret); - return ret; - } - - return 0; -} - static void dcmipp_start_capture(struct dcmipp_bytecap_device *vcap, struct dcmipp_buf *buf) { @@ -395,11 +386,24 @@ static int dcmipp_bytecap_start_streaming(struct vb2_queue *vq, struct dcmipp_bytecap_device *vcap = vb2_get_drv_priv(vq); struct media_entity *entity = &vcap->vdev.entity; struct dcmipp_buf *buf; + struct media_pad *pad; int ret; vcap->sequence = 0; memset(&vcap->count, 0, sizeof(vcap->count)); + /* + * Get source subdev - since link is IMMUTABLE, pointer is cached + * within the dcmipp_bytecap_device structure + */ + if (!vcap->s_subdev) { + pad = media_pad_remote_pad_first(&vcap->vdev.entity.pads[0]); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + return -EINVAL; + vcap->s_subdev = media_entity_to_v4l2_subdev(pad->entity); + vcap->s_subdev_pad_nb = pad->index; + } + ret = pm_runtime_resume_and_get(vcap->dev); if (ret < 0) { dev_err(vcap->dev, "%s: Failed to start streaming, cannot get sync (%d)\n", @@ -414,7 +418,8 @@ static int dcmipp_bytecap_start_streaming(struct vb2_queue *vq, goto err_pm_put; } - ret = dcmipp_pipeline_s_stream(vcap, 1); + ret = v4l2_subdev_enable_streams(vcap->s_subdev, + vcap->s_subdev_pad_nb, BIT_ULL(0)); if (ret) goto err_media_pipeline_stop; @@ -482,7 +487,10 @@ static void dcmipp_bytecap_stop_streaming(struct vb2_queue *vq) int ret; u32 status; - dcmipp_pipeline_s_stream(vcap, 0); + ret = v4l2_subdev_disable_streams(vcap->s_subdev, + vcap->s_subdev_pad_nb, BIT_ULL(0)); + if (ret) + dev_warn(vcap->dev, "Failed to disable stream\n"); /* Stop the media pipeline */ media_pipeline_stop(vcap->vdev.entity.pads); @@ -810,8 +818,7 @@ static int dcmipp_bytecap_link_validate(struct media_link *link) .which = V4L2_SUBDEV_FORMAT_ACTIVE, .pad = link->source->index, }; - const struct dcmipp_bytecap_pix_map *vpix; - int ret; + int ret, i; ret = v4l2_subdev_call(source_sd, pad, get_fmt, NULL, &source_fmt); if (ret < 0) @@ -825,10 +832,17 @@ static int dcmipp_bytecap_link_validate(struct media_link *link) return -EINVAL; } - vpix = dcmipp_bytecap_pix_map_by_pixelformat(vcap->format.pixelformat); - if (source_fmt.format.code != vpix->code) { - dev_err(vcap->dev, "Wrong mbus_code 0x%x, (0x%x expected)\n", - vpix->code, source_fmt.format.code); + for (i = 0; i < ARRAY_SIZE(dcmipp_bytecap_pix_map_list); i++) { + if (dcmipp_bytecap_pix_map_list[i].pixelformat == + vcap->format.pixelformat && + dcmipp_bytecap_pix_map_list[i].code == + source_fmt.format.code) + break; + } + + if (i == ARRAY_SIZE(dcmipp_bytecap_pix_map_list)) { + dev_err(vcap->dev, "mbus code 0x%x do not match capture device format (0x%x)\n", + vcap->format.pixelformat, source_fmt.format.code); return -EINVAL; } @@ -887,7 +901,7 @@ struct dcmipp_ent_device *dcmipp_bytecap_ent_init(struct device *dev, q->dev = dev; /* DCMIPP requires 16 bytes aligned buffers */ - ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32) & ~0x0f); + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); if (ret) { dev_err(dev, "Failed to set DMA mask\n"); goto err_mutex_destroy; diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c index 5a361ad6b023..3c742a546441 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c @@ -48,15 +48,32 @@ struct dcmipp_byteproc_pix_map { } static const struct dcmipp_byteproc_pix_map dcmipp_byteproc_pix_map_list[] = { PIXMAP_MBUS_BPP(RGB565_2X8_LE, 2), + PIXMAP_MBUS_BPP(RGB565_1X16, 2), PIXMAP_MBUS_BPP(YUYV8_2X8, 2), + PIXMAP_MBUS_BPP(YUYV8_1X16, 2), PIXMAP_MBUS_BPP(YVYU8_2X8, 2), + PIXMAP_MBUS_BPP(YVYU8_1X16, 2), PIXMAP_MBUS_BPP(UYVY8_2X8, 2), + PIXMAP_MBUS_BPP(UYVY8_1X16, 2), PIXMAP_MBUS_BPP(VYUY8_2X8, 2), + PIXMAP_MBUS_BPP(VYUY8_1X16, 2), PIXMAP_MBUS_BPP(Y8_1X8, 1), PIXMAP_MBUS_BPP(SBGGR8_1X8, 1), PIXMAP_MBUS_BPP(SGBRG8_1X8, 1), PIXMAP_MBUS_BPP(SGRBG8_1X8, 1), PIXMAP_MBUS_BPP(SRGGB8_1X8, 1), + PIXMAP_MBUS_BPP(SBGGR10_1X10, 2), + PIXMAP_MBUS_BPP(SGBRG10_1X10, 2), + PIXMAP_MBUS_BPP(SGRBG10_1X10, 2), + PIXMAP_MBUS_BPP(SRGGB10_1X10, 2), + PIXMAP_MBUS_BPP(SBGGR12_1X12, 2), + PIXMAP_MBUS_BPP(SGBRG12_1X12, 2), + PIXMAP_MBUS_BPP(SGRBG12_1X12, 2), + PIXMAP_MBUS_BPP(SRGGB12_1X12, 2), + PIXMAP_MBUS_BPP(SBGGR14_1X14, 2), + PIXMAP_MBUS_BPP(SGBRG14_1X14, 2), + PIXMAP_MBUS_BPP(SGRBG14_1X14, 2), + PIXMAP_MBUS_BPP(SRGGB14_1X14, 2), PIXMAP_MBUS_BPP(JPEG_1X8, 1), }; @@ -78,7 +95,6 @@ struct dcmipp_byteproc_device { struct v4l2_subdev sd; struct device *dev; void __iomem *regs; - bool streaming; }; static const struct v4l2_mbus_framefmt fmt_default = { @@ -239,11 +255,10 @@ static int dcmipp_byteproc_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { - struct dcmipp_byteproc_device *byteproc = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *mf; struct v4l2_rect *crop, *compose; - if (byteproc->streaming) + if (v4l2_subdev_is_streaming(sd)) return -EBUSY; mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); @@ -382,30 +397,19 @@ static int dcmipp_byteproc_set_selection(struct v4l2_subdev *sd, return 0; } -static const struct v4l2_subdev_pad_ops dcmipp_byteproc_pad_ops = { - .enum_mbus_code = dcmipp_byteproc_enum_mbus_code, - .enum_frame_size = dcmipp_byteproc_enum_frame_size, - .get_fmt = v4l2_subdev_get_fmt, - .set_fmt = dcmipp_byteproc_set_fmt, - .get_selection = dcmipp_byteproc_get_selection, - .set_selection = dcmipp_byteproc_set_selection, -}; - static int dcmipp_byteproc_configure_scale_crop - (struct dcmipp_byteproc_device *byteproc) + (struct dcmipp_byteproc_device *byteproc, + struct v4l2_subdev_state *state) { const struct dcmipp_byteproc_pix_map *vpix; - struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *sink_fmt; u32 hprediv, vprediv; struct v4l2_rect *compose, *crop; u32 val = 0; - state = v4l2_subdev_lock_and_get_active_state(&byteproc->sd); sink_fmt = v4l2_subdev_state_get_format(state, 0); compose = v4l2_subdev_state_get_compose(state, 0); crop = v4l2_subdev_state_get_crop(state, 1); - v4l2_subdev_unlock_state(state); /* find output format bpp */ vpix = dcmipp_byteproc_pix_map_by_code(sink_fmt->code); @@ -460,48 +464,73 @@ static int dcmipp_byteproc_configure_scale_crop return 0; } -static int dcmipp_byteproc_s_stream(struct v4l2_subdev *sd, int enable) +static int dcmipp_byteproc_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) { struct dcmipp_byteproc_device *byteproc = v4l2_get_subdevdata(sd); struct v4l2_subdev *s_subdev; - struct media_pad *pad; - int ret = 0; + struct media_pad *s_pad; + int ret; /* Get source subdev */ - pad = media_pad_remote_pad_first(&sd->entity.pads[0]); - if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]); + if (!s_pad || !is_media_entity_v4l2_subdev(s_pad->entity)) return -EINVAL; - s_subdev = media_entity_to_v4l2_subdev(pad->entity); - - if (enable) { - ret = dcmipp_byteproc_configure_scale_crop(byteproc); - if (ret) - return ret; - - ret = v4l2_subdev_call(s_subdev, video, s_stream, enable); - if (ret < 0) { - dev_err(byteproc->dev, - "failed to start source subdev streaming (%d)\n", - ret); - return ret; - } - } else { - ret = v4l2_subdev_call(s_subdev, video, s_stream, enable); - if (ret < 0) { - dev_err(byteproc->dev, - "failed to stop source subdev streaming (%d)\n", - ret); - return ret; - } + s_subdev = media_entity_to_v4l2_subdev(s_pad->entity); + + ret = dcmipp_byteproc_configure_scale_crop(byteproc, state); + if (ret) + return ret; + + ret = v4l2_subdev_enable_streams(s_subdev, s_pad->index, BIT_ULL(0)); + if (ret < 0) { + dev_err(byteproc->dev, + "failed to start source subdev streaming (%d)\n", ret); + return ret; } - byteproc->streaming = enable; + return 0; +} + +static int dcmipp_byteproc_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct dcmipp_byteproc_device *byteproc = v4l2_get_subdevdata(sd); + struct v4l2_subdev *s_subdev; + struct media_pad *s_pad; + int ret; + + /* Get source subdev */ + s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]); + if (!s_pad || !is_media_entity_v4l2_subdev(s_pad->entity)) + return -EINVAL; + s_subdev = media_entity_to_v4l2_subdev(s_pad->entity); + + ret = v4l2_subdev_disable_streams(s_subdev, s_pad->index, BIT_ULL(0)); + if (ret < 0) { + dev_err(byteproc->dev, + "failed to start source subdev streaming (%d)\n", ret); + return ret; + } return 0; } +static const struct v4l2_subdev_pad_ops dcmipp_byteproc_pad_ops = { + .enum_mbus_code = dcmipp_byteproc_enum_mbus_code, + .enum_frame_size = dcmipp_byteproc_enum_frame_size, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = dcmipp_byteproc_set_fmt, + .get_selection = dcmipp_byteproc_get_selection, + .set_selection = dcmipp_byteproc_set_selection, + .enable_streams = dcmipp_byteproc_enable_streams, + .disable_streams = dcmipp_byteproc_disable_streams, +}; + static const struct v4l2_subdev_video_ops dcmipp_byteproc_video_ops = { - .s_stream = dcmipp_byteproc_s_stream, + .s_stream = v4l2_subdev_s_stream_helper, }; static const struct v4l2_subdev_ops dcmipp_byteproc_ops = { diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h index 7a7cf43baf24..fe5f97233f5e 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h @@ -199,11 +199,11 @@ static inline void __reg_clear(struct device *dev, void __iomem *base, u32 reg, } /* DCMIPP subdev init / release entry points */ -struct dcmipp_ent_device *dcmipp_par_ent_init(struct device *dev, +struct dcmipp_ent_device *dcmipp_inp_ent_init(struct device *dev, const char *entity_name, struct v4l2_device *v4l2_dev, void __iomem *regs); -void dcmipp_par_ent_release(struct dcmipp_ent_device *ved); +void dcmipp_inp_ent_release(struct dcmipp_ent_device *ved); struct dcmipp_ent_device * dcmipp_byteproc_ent_init(struct device *dev, const char *entity_name, struct v4l2_device *v4l2_dev, void __iomem *regs); 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 3806f7c6e2fe..71acf539e1f3 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c @@ -40,6 +40,7 @@ struct dcmipp_device { /* Hardware resources */ void __iomem *regs; + struct clk *mclk; struct clk *kclk; /* The pipeline configuration */ @@ -87,6 +88,7 @@ struct dcmipp_pipeline_config { size_t num_ents; const struct dcmipp_ent_link *links; size_t num_links; + u32 hw_revision; }; /* -------------------------------------------------------------------------- @@ -95,9 +97,9 @@ struct dcmipp_pipeline_config { static const struct dcmipp_ent_config stm32mp13_ent_config[] = { { - .name = "dcmipp_parallel", - .init = dcmipp_par_ent_init, - .release = dcmipp_par_ent_release, + .name = "dcmipp_input", + .init = dcmipp_inp_ent_init, + .release = dcmipp_inp_ent_release, }, { .name = "dcmipp_dump_postproc", @@ -111,22 +113,58 @@ static const struct dcmipp_ent_config stm32mp13_ent_config[] = { }, }; -#define ID_PARALLEL 0 +#define ID_INPUT 0 #define ID_DUMP_BYTEPROC 1 #define ID_DUMP_CAPTURE 2 static const struct dcmipp_ent_link stm32mp13_ent_links[] = { - DCMIPP_ENT_LINK(ID_PARALLEL, 1, ID_DUMP_BYTEPROC, 0, + DCMIPP_ENT_LINK(ID_INPUT, 1, ID_DUMP_BYTEPROC, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), DCMIPP_ENT_LINK(ID_DUMP_BYTEPROC, 1, ID_DUMP_CAPTURE, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), }; +#define DCMIPP_STM32MP13_VERR 0x10 static const struct dcmipp_pipeline_config stm32mp13_pipe_cfg = { .ents = stm32mp13_ent_config, .num_ents = ARRAY_SIZE(stm32mp13_ent_config), .links = stm32mp13_ent_links, - .num_links = ARRAY_SIZE(stm32mp13_ent_links) + .num_links = ARRAY_SIZE(stm32mp13_ent_links), + .hw_revision = DCMIPP_STM32MP13_VERR +}; + +static const struct dcmipp_ent_config stm32mp25_ent_config[] = { + { + .name = "dcmipp_input", + .init = dcmipp_inp_ent_init, + .release = dcmipp_inp_ent_release, + }, + { + .name = "dcmipp_dump_postproc", + .init = dcmipp_byteproc_ent_init, + .release = dcmipp_byteproc_ent_release, + }, + { + .name = "dcmipp_dump_capture", + .init = dcmipp_bytecap_ent_init, + .release = dcmipp_bytecap_ent_release, + }, +}; + +static const struct dcmipp_ent_link stm32mp25_ent_links[] = { + DCMIPP_ENT_LINK(ID_INPUT, 1, ID_DUMP_BYTEPROC, 0, + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), + DCMIPP_ENT_LINK(ID_DUMP_BYTEPROC, 1, ID_DUMP_CAPTURE, 0, + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), +}; + +#define DCMIPP_STM32MP25_VERR 0x30 +static const struct dcmipp_pipeline_config stm32mp25_pipe_cfg = { + .ents = stm32mp25_ent_config, + .num_ents = ARRAY_SIZE(stm32mp25_ent_config), + .links = stm32mp25_ent_links, + .num_links = ARRAY_SIZE(stm32mp25_ent_links), + .hw_revision = DCMIPP_STM32MP25_VERR }; #define LINK_FLAG_TO_STR(f) ((f) == 0 ? "" :\ @@ -209,6 +247,7 @@ err_init_entity: static const struct of_device_id dcmipp_of_match[] = { { .compatible = "st,stm32mp13-dcmipp", .data = &stm32mp13_pipe_cfg }, + { .compatible = "st,stm32mp25-dcmipp", .data = &stm32mp25_pipe_cfg }, { /* end node */ }, }; MODULE_DEVICE_TABLE(of, dcmipp_of_match); @@ -258,13 +297,22 @@ static int dcmipp_graph_notify_bound(struct v4l2_async_notifier *notifier, { struct dcmipp_device *dcmipp = notifier_to_dcmipp(notifier); unsigned int ret; - int src_pad; + int src_pad, i; struct dcmipp_ent_device *sink; - struct v4l2_fwnode_endpoint vep = { .bus_type = V4L2_MBUS_PARALLEL }; + struct v4l2_fwnode_endpoint vep = { 0 }; struct fwnode_handle *ep; + enum v4l2_mbus_type supported_types[] = { + V4L2_MBUS_PARALLEL, V4L2_MBUS_BT656, V4L2_MBUS_CSI2_DPHY + }; + int supported_types_nb = ARRAY_SIZE(supported_types); dev_dbg(dcmipp->dev, "Subdev \"%s\" bound\n", subdev->name); + /* Only MP25 supports CSI input */ + if (!of_device_is_compatible(dcmipp->dev->of_node, + "st,stm32mp25-dcmipp")) + supported_types_nb--; + /* * Link this sub-device to DCMIPP, it could be * a parallel camera sensor or a CSI-2 to parallel bridge @@ -281,21 +329,23 @@ static int dcmipp_graph_notify_bound(struct v4l2_async_notifier *notifier, return -ENODEV; } - /* Check for parallel bus-type first, then bt656 */ - ret = v4l2_fwnode_endpoint_parse(ep, &vep); - if (ret) { - vep.bus_type = V4L2_MBUS_BT656; + /* Check for supported MBUS type */ + for (i = 0; i < supported_types_nb; i++) { + vep.bus_type = supported_types[i]; ret = v4l2_fwnode_endpoint_parse(ep, &vep); - if (ret) { - dev_err(dcmipp->dev, "Could not parse the endpoint\n"); - fwnode_handle_put(ep); - return ret; - } + if (!ret) + break; } fwnode_handle_put(ep); - if (vep.bus.parallel.bus_width == 0) { + if (ret) { + dev_err(dcmipp->dev, "Could not parse the endpoint\n"); + return ret; + } + + if (vep.bus_type != V4L2_MBUS_CSI2_DPHY && + vep.bus.parallel.bus_width == 0) { dev_err(dcmipp->dev, "Invalid parallel interface bus-width\n"); return -ENODEV; } @@ -308,11 +358,13 @@ static int dcmipp_graph_notify_bound(struct v4l2_async_notifier *notifier, return -ENODEV; } - /* Parallel input device detected, connect it to parallel subdev */ - sink = dcmipp->entity[ID_PARALLEL]; - sink->bus.flags = vep.bus.parallel.flags; - sink->bus.bus_width = vep.bus.parallel.bus_width; - sink->bus.data_shift = vep.bus.parallel.data_shift; + /* Connect input device to the dcmipp_input subdev */ + sink = dcmipp->entity[ID_INPUT]; + if (vep.bus_type != V4L2_MBUS_CSI2_DPHY) { + sink->bus.flags = vep.bus.parallel.flags; + sink->bus.bus_width = vep.bus.parallel.bus_width; + sink->bus.data_shift = vep.bus.parallel.data_shift; + } sink->bus_type = vep.bus_type; ret = media_create_pad_link(&subdev->entity, src_pad, sink->ent, 0, MEDIA_LNK_FL_IMMUTABLE | @@ -411,7 +463,7 @@ static int dcmipp_graph_init(struct dcmipp_device *dcmipp) static int dcmipp_probe(struct platform_device *pdev) { struct dcmipp_device *dcmipp; - struct clk *kclk; + struct clk *kclk, *mclk; const struct dcmipp_pipeline_config *pipe_cfg; struct reset_control *rstc; int irq; @@ -471,12 +523,20 @@ static int dcmipp_probe(struct platform_device *pdev) return ret; } - kclk = devm_clk_get(&pdev->dev, NULL); + kclk = devm_clk_get(&pdev->dev, "kclk"); if (IS_ERR(kclk)) return dev_err_probe(&pdev->dev, PTR_ERR(kclk), "Unable to get kclk\n"); dcmipp->kclk = kclk; + if (!of_device_is_compatible(pdev->dev.of_node, "st,stm32mp13-dcmipp")) { + mclk = devm_clk_get(&pdev->dev, "mclk"); + if (IS_ERR(mclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(mclk), + "Unable to get mclk\n"); + dcmipp->mclk = mclk; + } + dcmipp->entity = devm_kcalloc(&pdev->dev, dcmipp->pipe_cfg->num_ents, sizeof(*dcmipp->entity), GFP_KERNEL); if (!dcmipp->entity) @@ -496,6 +556,7 @@ static int dcmipp_probe(struct platform_device *pdev) /* Initialize media device */ strscpy(dcmipp->mdev.model, DCMIPP_MDEV_MODEL_NAME, sizeof(dcmipp->mdev.model)); + dcmipp->mdev.hw_revision = pipe_cfg->hw_revision; dcmipp->mdev.dev = &pdev->dev; media_device_init(&dcmipp->mdev); @@ -538,6 +599,7 @@ static int dcmipp_runtime_suspend(struct device *dev) struct dcmipp_device *dcmipp = dev_get_drvdata(dev); clk_disable_unprepare(dcmipp->kclk); + clk_disable_unprepare(dcmipp->mclk); return 0; } @@ -547,9 +609,17 @@ static int dcmipp_runtime_resume(struct device *dev) struct dcmipp_device *dcmipp = dev_get_drvdata(dev); int ret; + ret = clk_prepare_enable(dcmipp->mclk); + if (ret) { + dev_err(dev, "%s: Failed to prepare_enable mclk\n", __func__); + return ret; + } + ret = clk_prepare_enable(dcmipp->kclk); - if (ret) + if (ret) { + clk_disable_unprepare(dcmipp->mclk); dev_err(dev, "%s: Failed to prepare_enable kclk\n", __func__); + } return ret; } diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c new file mode 100644 index 000000000000..7e5311b67d7e --- /dev/null +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c @@ -0,0 +1,540 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for STM32 Digital Camera Memory Interface Pixel Processor + * + * Copyright (C) STMicroelectronics SA 2023 + * Authors: Hugues Fruchet <hugues.fruchet@foss.st.com> + * Alain Volmat <alain.volmat@foss.st.com> + * for STMicroelectronics. + */ + +#include <linux/v4l2-mediabus.h> +#include <media/mipi-csi2.h> +#include <media/v4l2-event.h> +#include <media/v4l2-subdev.h> + +#include "dcmipp-common.h" + +#define DCMIPP_PRCR 0x104 +#define DCMIPP_PRCR_FORMAT_SHIFT 16 +#define DCMIPP_PRCR_FORMAT_YUV422 0x1e +#define DCMIPP_PRCR_FORMAT_RGB565 0x22 +#define DCMIPP_PRCR_FORMAT_RAW8 0x2a +#define DCMIPP_PRCR_FORMAT_RAW10 0x2b +#define DCMIPP_PRCR_FORMAT_RAW12 0x2c +#define DCMIPP_PRCR_FORMAT_RAW14 0x2d +#define DCMIPP_PRCR_FORMAT_G8 0x4a +#define DCMIPP_PRCR_FORMAT_BYTE_STREAM 0x5a +#define DCMIPP_PRCR_ESS BIT(4) +#define DCMIPP_PRCR_PCKPOL BIT(5) +#define DCMIPP_PRCR_HSPOL BIT(6) +#define DCMIPP_PRCR_VSPOL BIT(7) +#define DCMIPP_PRCR_ENABLE BIT(14) +#define DCMIPP_PRCR_SWAPCYCLES BIT(25) + +#define DCMIPP_PRESCR 0x108 +#define DCMIPP_PRESUR 0x10c + +#define DCMIPP_CMCR 0x204 +#define DCMIPP_CMCR_INSEL BIT(0) + +#define DCMIPP_P0FSCR 0x404 +#define DCMIPP_P0FSCR_DTMODE_MASK GENMASK(17, 16) +#define DCMIPP_P0FSCR_DTMODE_SHIFT 16 +#define DCMIPP_P0FSCR_DTMODE_DTIDA 0x00 +#define DCMIPP_P0FSCR_DTMODE_ALLDT 0x03 +#define DCMIPP_P0FSCR_DTIDA_MASK GENMASK(5, 0) +#define DCMIPP_P0FSCR_DTIDA_SHIFT 0 + +#define IS_SINK(pad) (!(pad)) +#define IS_SRC(pad) ((pad)) + +struct dcmipp_inp_pix_map { + unsigned int code_sink; + unsigned int code_src; + /* Parallel related information */ + u8 prcr_format; + u8 prcr_swapcycles; + /* CSI related information */ + unsigned int dt; +}; + +#define PIXMAP_SINK_SRC_PRCR_SWAP(sink, src, prcr, swap, data_type) \ + { \ + .code_sink = MEDIA_BUS_FMT_##sink, \ + .code_src = MEDIA_BUS_FMT_##src, \ + .prcr_format = DCMIPP_PRCR_FORMAT_##prcr, \ + .prcr_swapcycles = swap, \ + .dt = data_type, \ + } +static const struct dcmipp_inp_pix_map dcmipp_inp_pix_map_list[] = { + /* RGB565 */ + PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_2X8_LE, RGB565_2X8_LE, RGB565, 1, MIPI_CSI2_DT_RGB565), + PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_2X8_BE, RGB565_2X8_LE, RGB565, 0, MIPI_CSI2_DT_RGB565), + PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_1X16, RGB565_1X16, RGB565, 0, MIPI_CSI2_DT_RGB565), + /* YUV422 */ + PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, YUYV8_2X8, YUV422, 1, MIPI_CSI2_DT_YUV422_8B), + PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_1X16, YUYV8_1X16, YUV422, 0, MIPI_CSI2_DT_YUV422_8B), + PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, UYVY8_2X8, YUV422, 0, MIPI_CSI2_DT_YUV422_8B), + PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_2X8, UYVY8_2X8, YUV422, 1, MIPI_CSI2_DT_YUV422_8B), + PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_1X16, UYVY8_1X16, YUV422, 0, MIPI_CSI2_DT_YUV422_8B), + PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_2X8, YUYV8_2X8, YUV422, 0, MIPI_CSI2_DT_YUV422_8B), + PIXMAP_SINK_SRC_PRCR_SWAP(YVYU8_2X8, YVYU8_2X8, YUV422, 1, MIPI_CSI2_DT_YUV422_8B), + PIXMAP_SINK_SRC_PRCR_SWAP(YVYU8_1X16, YVYU8_1X16, YUV422, 0, MIPI_CSI2_DT_YUV422_8B), + PIXMAP_SINK_SRC_PRCR_SWAP(VYUY8_2X8, VYUY8_2X8, YUV422, 1, MIPI_CSI2_DT_YUV422_8B), + PIXMAP_SINK_SRC_PRCR_SWAP(VYUY8_1X16, VYUY8_1X16, YUV422, 0, MIPI_CSI2_DT_YUV422_8B), + /* GREY */ + PIXMAP_SINK_SRC_PRCR_SWAP(Y8_1X8, Y8_1X8, G8, 0, MIPI_CSI2_DT_RAW8), + /* Raw Bayer */ + PIXMAP_SINK_SRC_PRCR_SWAP(SBGGR8_1X8, SBGGR8_1X8, RAW8, 0, MIPI_CSI2_DT_RAW8), + PIXMAP_SINK_SRC_PRCR_SWAP(SGBRG8_1X8, SGBRG8_1X8, RAW8, 0, MIPI_CSI2_DT_RAW8), + PIXMAP_SINK_SRC_PRCR_SWAP(SGRBG8_1X8, SGRBG8_1X8, RAW8, 0, MIPI_CSI2_DT_RAW8), + PIXMAP_SINK_SRC_PRCR_SWAP(SRGGB8_1X8, SRGGB8_1X8, RAW8, 0, MIPI_CSI2_DT_RAW8), + PIXMAP_SINK_SRC_PRCR_SWAP(SBGGR10_1X10, SBGGR10_1X10, RAW10, 0, MIPI_CSI2_DT_RAW10), + PIXMAP_SINK_SRC_PRCR_SWAP(SGBRG10_1X10, SGBRG10_1X10, RAW10, 0, MIPI_CSI2_DT_RAW10), + PIXMAP_SINK_SRC_PRCR_SWAP(SGRBG10_1X10, SGRBG10_1X10, RAW10, 0, MIPI_CSI2_DT_RAW10), + PIXMAP_SINK_SRC_PRCR_SWAP(SRGGB10_1X10, SRGGB10_1X10, RAW10, 0, MIPI_CSI2_DT_RAW10), + PIXMAP_SINK_SRC_PRCR_SWAP(SBGGR12_1X12, SBGGR12_1X12, RAW12, 0, MIPI_CSI2_DT_RAW12), + PIXMAP_SINK_SRC_PRCR_SWAP(SGBRG12_1X12, SGBRG12_1X12, RAW12, 0, MIPI_CSI2_DT_RAW12), + PIXMAP_SINK_SRC_PRCR_SWAP(SGRBG12_1X12, SGRBG12_1X12, RAW12, 0, MIPI_CSI2_DT_RAW12), + PIXMAP_SINK_SRC_PRCR_SWAP(SRGGB12_1X12, SRGGB12_1X12, RAW12, 0, MIPI_CSI2_DT_RAW12), + PIXMAP_SINK_SRC_PRCR_SWAP(SBGGR14_1X14, SBGGR14_1X14, RAW14, 0, MIPI_CSI2_DT_RAW14), + PIXMAP_SINK_SRC_PRCR_SWAP(SGBRG14_1X14, SGBRG14_1X14, RAW14, 0, MIPI_CSI2_DT_RAW14), + PIXMAP_SINK_SRC_PRCR_SWAP(SGRBG14_1X14, SGRBG14_1X14, RAW14, 0, MIPI_CSI2_DT_RAW14), + PIXMAP_SINK_SRC_PRCR_SWAP(SRGGB14_1X14, SRGGB14_1X14, RAW14, 0, MIPI_CSI2_DT_RAW14), + /* JPEG */ + PIXMAP_SINK_SRC_PRCR_SWAP(JPEG_1X8, JPEG_1X8, BYTE_STREAM, 0, 0), +}; + +/* + * Search through the pix_map table, skipping two consecutive entry with the + * same code + */ +static inline const struct dcmipp_inp_pix_map *dcmipp_inp_pix_map_by_index + (unsigned int index, + unsigned int pad) +{ + unsigned int i = 0; + u32 prev_code = 0, cur_code; + + while (i < ARRAY_SIZE(dcmipp_inp_pix_map_list)) { + if (IS_SRC(pad)) + cur_code = dcmipp_inp_pix_map_list[i].code_src; + else + cur_code = dcmipp_inp_pix_map_list[i].code_sink; + + if (cur_code == prev_code) { + i++; + continue; + } + prev_code = cur_code; + + if (index == 0) + break; + i++; + index--; + } + + if (i >= ARRAY_SIZE(dcmipp_inp_pix_map_list)) + return NULL; + + return &dcmipp_inp_pix_map_list[i]; +} + +static inline const struct dcmipp_inp_pix_map *dcmipp_inp_pix_map_by_code + (u32 code_sink, u32 code_src) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(dcmipp_inp_pix_map_list); i++) { + if ((dcmipp_inp_pix_map_list[i].code_sink == code_sink && + dcmipp_inp_pix_map_list[i].code_src == code_src) || + (dcmipp_inp_pix_map_list[i].code_sink == code_src && + dcmipp_inp_pix_map_list[i].code_src == code_sink) || + (dcmipp_inp_pix_map_list[i].code_sink == code_sink && + code_src == 0) || + (code_sink == 0 && + dcmipp_inp_pix_map_list[i].code_src == code_src)) + return &dcmipp_inp_pix_map_list[i]; + } + return NULL; +} + +struct dcmipp_inp_device { + struct dcmipp_ent_device ved; + struct v4l2_subdev sd; + struct device *dev; + void __iomem *regs; +}; + +static const struct v4l2_mbus_framefmt fmt_default = { + .width = DCMIPP_FMT_WIDTH_DEFAULT, + .height = DCMIPP_FMT_HEIGHT_DEFAULT, + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .field = V4L2_FIELD_NONE, + .colorspace = DCMIPP_COLORSPACE_DEFAULT, + .ycbcr_enc = DCMIPP_YCBCR_ENC_DEFAULT, + .quantization = DCMIPP_QUANTIZATION_DEFAULT, + .xfer_func = DCMIPP_XFER_FUNC_DEFAULT, +}; + +static int dcmipp_inp_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; + + mf = v4l2_subdev_state_get_format(sd_state, i); + *mf = fmt_default; + } + + return 0; +} + +static int dcmipp_inp_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + const struct dcmipp_inp_pix_map *vpix = + dcmipp_inp_pix_map_by_index(code->index, code->pad); + + if (!vpix) + return -EINVAL; + + code->code = IS_SRC(code->pad) ? vpix->code_src : vpix->code_sink; + + return 0; +} + +static int dcmipp_inp_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + const struct dcmipp_inp_pix_map *vpix; + + if (fse->index) + return -EINVAL; + + /* Only accept code in the pix map table */ + vpix = dcmipp_inp_pix_map_by_code(IS_SINK(fse->pad) ? fse->code : 0, + IS_SRC(fse->pad) ? fse->code : 0); + if (!vpix) + return -EINVAL; + + fse->min_width = DCMIPP_FRAME_MIN_WIDTH; + fse->max_width = DCMIPP_FRAME_MAX_WIDTH; + fse->min_height = DCMIPP_FRAME_MIN_HEIGHT; + fse->max_height = DCMIPP_FRAME_MAX_HEIGHT; + + return 0; +} + +static void dcmipp_inp_adjust_fmt(struct dcmipp_inp_device *inp, + struct v4l2_mbus_framefmt *fmt, __u32 pad) +{ + const struct dcmipp_inp_pix_map *vpix; + + /* Only accept code in the pix map table */ + vpix = dcmipp_inp_pix_map_by_code(IS_SINK(pad) ? fmt->code : 0, + IS_SRC(pad) ? fmt->code : 0); + if (!vpix) + fmt->code = fmt_default.code; + + /* Exclude JPEG if BT656 bus is selected */ + if (vpix && vpix->code_sink == MEDIA_BUS_FMT_JPEG_1X8 && + inp->ved.bus_type == V4L2_MBUS_BT656) + fmt->code = fmt_default.code; + + fmt->width = clamp_t(u32, fmt->width, DCMIPP_FRAME_MIN_WIDTH, + DCMIPP_FRAME_MAX_WIDTH) & ~1; + fmt->height = clamp_t(u32, fmt->height, DCMIPP_FRAME_MIN_HEIGHT, + DCMIPP_FRAME_MAX_HEIGHT) & ~1; + + if (fmt->field == V4L2_FIELD_ANY || fmt->field == V4L2_FIELD_ALTERNATE) + fmt->field = fmt_default.field; + + dcmipp_colorimetry_clamp(fmt); +} + +static int dcmipp_inp_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct dcmipp_inp_device *inp = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *mf; + + if (v4l2_subdev_is_streaming(sd)) + return -EBUSY; + + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); + + /* Set the new format */ + dcmipp_inp_adjust_fmt(inp, &fmt->format, fmt->pad); + + dev_dbg(inp->dev, "%s: format update: old:%dx%d (0x%x, %d, %d, %d, %d) new:%dx%d (0x%x, %d, %d, %d, %d)\n", + inp->sd.name, + /* old */ + mf->width, mf->height, mf->code, + mf->colorspace, mf->quantization, + mf->xfer_func, mf->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); + + *mf = fmt->format; + + /* When setting the sink format, report that format on the src pad */ + if (IS_SINK(fmt->pad)) { + mf = v4l2_subdev_state_get_format(sd_state, 1); + *mf = fmt->format; + dcmipp_inp_adjust_fmt(inp, mf, 1); + } + + return 0; +} + +static int dcmipp_inp_configure_parallel(struct dcmipp_inp_device *inp, + struct v4l2_subdev_state *state) +{ + u32 val = 0; + const struct dcmipp_inp_pix_map *vpix; + struct v4l2_mbus_framefmt *sink_fmt; + struct v4l2_mbus_framefmt *src_fmt; + + /* Set vertical synchronization polarity */ + if (inp->ved.bus.flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) + val |= DCMIPP_PRCR_VSPOL; + + /* Set horizontal synchronization polarity */ + if (inp->ved.bus.flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) + val |= DCMIPP_PRCR_HSPOL; + + /* Set pixel clock polarity */ + if (inp->ved.bus.flags & V4L2_MBUS_PCLK_SAMPLE_RISING) + val |= DCMIPP_PRCR_PCKPOL; + + /* + * BT656 embedded synchronisation bus mode. + * + * Default SAV/EAV mode is supported here with default codes + * SAV=0xff000080 & EAV=0xff00009d. + * With DCMIPP this means LSC=SAV=0x80 & LEC=EAV=0x9d. + */ + if (inp->ved.bus_type == V4L2_MBUS_BT656) { + val |= DCMIPP_PRCR_ESS; + + /* Unmask all codes */ + reg_write(inp, DCMIPP_PRESUR, 0xffffffff);/* FEC:LEC:LSC:FSC */ + + /* Trig on LSC=0x80 & LEC=0x9d codes, ignore FSC and FEC */ + reg_write(inp, DCMIPP_PRESCR, 0xff9d80ff);/* FEC:LEC:LSC:FSC */ + } + + /* Set format */ + sink_fmt = v4l2_subdev_state_get_format(state, 0); + src_fmt = v4l2_subdev_state_get_format(state, 1); + + vpix = dcmipp_inp_pix_map_by_code(sink_fmt->code, src_fmt->code); + if (!vpix) { + dev_err(inp->dev, "Invalid sink/src format configuration\n"); + return -EINVAL; + } + + val |= vpix->prcr_format << DCMIPP_PRCR_FORMAT_SHIFT; + + /* swap cycles */ + if (vpix->prcr_swapcycles) + val |= DCMIPP_PRCR_SWAPCYCLES; + + reg_write(inp, DCMIPP_PRCR, val); + + /* Select the DCMIPP parallel interface */ + reg_write(inp, DCMIPP_CMCR, 0); + + /* Enable parallel interface */ + reg_set(inp, DCMIPP_PRCR, DCMIPP_PRCR_ENABLE); + + return 0; +} + +static int dcmipp_inp_configure_csi(struct dcmipp_inp_device *inp, + struct v4l2_subdev_state *state) +{ + const struct dcmipp_inp_pix_map *vpix; + struct v4l2_mbus_framefmt *sink_fmt; + struct v4l2_mbus_framefmt *src_fmt; + + /* Get format information */ + sink_fmt = v4l2_subdev_state_get_format(state, 0); + src_fmt = v4l2_subdev_state_get_format(state, 1); + + vpix = dcmipp_inp_pix_map_by_code(sink_fmt->code, src_fmt->code); + if (!vpix) { + dev_err(inp->dev, "Invalid sink/src format configuration\n"); + return -EINVAL; + } + + /* Apply configuration on each input pipe */ + reg_clear(inp, DCMIPP_P0FSCR, + DCMIPP_P0FSCR_DTMODE_MASK | DCMIPP_P0FSCR_DTIDA_MASK); + + /* In case of JPEG we don't know the DT so we allow all data */ + /* + * TODO - check instead dt == 0 for the time being to allow other + * unknown data-type + */ + if (!vpix->dt) + reg_set(inp, DCMIPP_P0FSCR, + DCMIPP_P0FSCR_DTMODE_ALLDT << DCMIPP_P0FSCR_DTMODE_SHIFT); + else + reg_set(inp, DCMIPP_P0FSCR, + vpix->dt << DCMIPP_P0FSCR_DTIDA_SHIFT | + DCMIPP_P0FSCR_DTMODE_DTIDA); + + /* Select the DCMIPP CSI interface */ + reg_write(inp, DCMIPP_CMCR, DCMIPP_CMCR_INSEL); + + return 0; +} + +static int dcmipp_inp_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct dcmipp_inp_device *inp = + container_of(sd, struct dcmipp_inp_device, sd); + struct v4l2_subdev *s_subdev; + struct media_pad *s_pad; + int ret = 0; + + /* Get source subdev */ + s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]); + if (!s_pad || !is_media_entity_v4l2_subdev(s_pad->entity)) + return -EINVAL; + s_subdev = media_entity_to_v4l2_subdev(s_pad->entity); + + if (inp->ved.bus_type == V4L2_MBUS_PARALLEL || + inp->ved.bus_type == V4L2_MBUS_BT656) + ret = dcmipp_inp_configure_parallel(inp, state); + else if (inp->ved.bus_type == V4L2_MBUS_CSI2_DPHY) + ret = dcmipp_inp_configure_csi(inp, state); + if (ret) + return ret; + + ret = v4l2_subdev_enable_streams(s_subdev, s_pad->index, BIT_ULL(0)); + if (ret < 0) { + dev_err(inp->dev, + "failed to start source subdev streaming (%d)\n", ret); + return ret; + } + + return 0; +} + +static int dcmipp_inp_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct dcmipp_inp_device *inp = + container_of(sd, struct dcmipp_inp_device, sd); + struct v4l2_subdev *s_subdev; + struct media_pad *s_pad; + int ret; + + /* Get source subdev */ + s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]); + if (!s_pad || !is_media_entity_v4l2_subdev(s_pad->entity)) + return -EINVAL; + s_subdev = media_entity_to_v4l2_subdev(s_pad->entity); + + ret = v4l2_subdev_disable_streams(s_subdev, s_pad->index, BIT_ULL(0)); + if (ret < 0) { + dev_err(inp->dev, + "failed to stop source subdev streaming (%d)\n", ret); + return ret; + } + + if (inp->ved.bus_type == V4L2_MBUS_PARALLEL || + inp->ved.bus_type == V4L2_MBUS_BT656) { + /* Disable parallel interface */ + reg_clear(inp, DCMIPP_PRCR, DCMIPP_PRCR_ENABLE); + } + + return 0; +} + +static const struct v4l2_subdev_pad_ops dcmipp_inp_pad_ops = { + .enum_mbus_code = dcmipp_inp_enum_mbus_code, + .enum_frame_size = dcmipp_inp_enum_frame_size, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = dcmipp_inp_set_fmt, + .enable_streams = dcmipp_inp_enable_streams, + .disable_streams = dcmipp_inp_disable_streams, +}; + +static const struct v4l2_subdev_video_ops dcmipp_inp_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_ops dcmipp_inp_ops = { + .pad = &dcmipp_inp_pad_ops, + .video = &dcmipp_inp_video_ops, +}; + +static void dcmipp_inp_release(struct v4l2_subdev *sd) +{ + struct dcmipp_inp_device *inp = + container_of(sd, struct dcmipp_inp_device, sd); + + kfree(inp); +} + +static const struct v4l2_subdev_internal_ops dcmipp_inp_int_ops = { + .init_state = dcmipp_inp_init_state, + .release = dcmipp_inp_release, +}; + +void dcmipp_inp_ent_release(struct dcmipp_ent_device *ved) +{ + struct dcmipp_inp_device *inp = + container_of(ved, struct dcmipp_inp_device, ved); + + dcmipp_ent_sd_unregister(ved, &inp->sd); +} + +struct dcmipp_ent_device *dcmipp_inp_ent_init(struct device *dev, + const char *entity_name, + struct v4l2_device *v4l2_dev, + void __iomem *regs) +{ + struct dcmipp_inp_device *inp; + const unsigned long pads_flag[] = { + MEDIA_PAD_FL_SINK, MEDIA_PAD_FL_SOURCE, + }; + int ret; + + /* Allocate the inp struct */ + inp = kzalloc(sizeof(*inp), GFP_KERNEL); + if (!inp) + return ERR_PTR(-ENOMEM); + + inp->regs = regs; + + /* Initialize ved and sd */ + ret = dcmipp_ent_sd_register(&inp->ved, &inp->sd, v4l2_dev, + entity_name, MEDIA_ENT_F_VID_IF_BRIDGE, + ARRAY_SIZE(pads_flag), pads_flag, + &dcmipp_inp_int_ops, &dcmipp_inp_ops, + NULL, NULL); + if (ret) { + kfree(inp); + return ERR_PTR(ret); + } + + inp->dev = dev; + + return &inp->ved; +} diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-parallel.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-parallel.c deleted file mode 100644 index 62c5c3331cfe..000000000000 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-parallel.c +++ /dev/null @@ -1,440 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Driver for STM32 Digital Camera Memory Interface Pixel Processor - * - * Copyright (C) STMicroelectronics SA 2023 - * Authors: Hugues Fruchet <hugues.fruchet@foss.st.com> - * Alain Volmat <alain.volmat@foss.st.com> - * for STMicroelectronics. - */ - -#include <linux/v4l2-mediabus.h> -#include <media/v4l2-event.h> -#include <media/v4l2-subdev.h> - -#include "dcmipp-common.h" - -#define DCMIPP_PRCR 0x104 -#define DCMIPP_PRCR_FORMAT_SHIFT 16 -#define DCMIPP_PRCR_FORMAT_YUV422 0x1e -#define DCMIPP_PRCR_FORMAT_RGB565 0x22 -#define DCMIPP_PRCR_FORMAT_RAW8 0x2a -#define DCMIPP_PRCR_FORMAT_G8 0x4a -#define DCMIPP_PRCR_FORMAT_BYTE_STREAM 0x5a -#define DCMIPP_PRCR_ESS BIT(4) -#define DCMIPP_PRCR_PCKPOL BIT(5) -#define DCMIPP_PRCR_HSPOL BIT(6) -#define DCMIPP_PRCR_VSPOL BIT(7) -#define DCMIPP_PRCR_ENABLE BIT(14) -#define DCMIPP_PRCR_SWAPCYCLES BIT(25) - -#define DCMIPP_PRESCR 0x108 -#define DCMIPP_PRESUR 0x10c - -#define IS_SINK(pad) (!(pad)) -#define IS_SRC(pad) ((pad)) - -struct dcmipp_par_pix_map { - unsigned int code_sink; - unsigned int code_src; - u8 prcr_format; - u8 prcr_swapcycles; -}; - -#define PIXMAP_SINK_SRC_PRCR_SWAP(sink, src, prcr, swap) \ - { \ - .code_sink = MEDIA_BUS_FMT_##sink, \ - .code_src = MEDIA_BUS_FMT_##src, \ - .prcr_format = DCMIPP_PRCR_FORMAT_##prcr, \ - .prcr_swapcycles = swap, \ - } -static const struct dcmipp_par_pix_map dcmipp_par_pix_map_list[] = { - /* RGB565 */ - PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_2X8_LE, RGB565_2X8_LE, RGB565, 1), - PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_2X8_BE, RGB565_2X8_LE, RGB565, 0), - /* YUV422 */ - PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, YUYV8_2X8, YUV422, 1), - PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, UYVY8_2X8, YUV422, 0), - PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_2X8, UYVY8_2X8, YUV422, 1), - PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_2X8, YUYV8_2X8, YUV422, 0), - PIXMAP_SINK_SRC_PRCR_SWAP(YVYU8_2X8, YVYU8_2X8, YUV422, 1), - PIXMAP_SINK_SRC_PRCR_SWAP(VYUY8_2X8, VYUY8_2X8, YUV422, 1), - /* GREY */ - PIXMAP_SINK_SRC_PRCR_SWAP(Y8_1X8, Y8_1X8, G8, 0), - /* Raw Bayer */ - PIXMAP_SINK_SRC_PRCR_SWAP(SBGGR8_1X8, SBGGR8_1X8, RAW8, 0), - PIXMAP_SINK_SRC_PRCR_SWAP(SGBRG8_1X8, SGBRG8_1X8, RAW8, 0), - PIXMAP_SINK_SRC_PRCR_SWAP(SGRBG8_1X8, SGRBG8_1X8, RAW8, 0), - PIXMAP_SINK_SRC_PRCR_SWAP(SRGGB8_1X8, SRGGB8_1X8, RAW8, 0), - /* JPEG */ - PIXMAP_SINK_SRC_PRCR_SWAP(JPEG_1X8, JPEG_1X8, BYTE_STREAM, 0), -}; - -/* - * Search through the pix_map table, skipping two consecutive entry with the - * same code - */ -static inline const struct dcmipp_par_pix_map *dcmipp_par_pix_map_by_index - (unsigned int index, - unsigned int pad) -{ - unsigned int i = 0; - u32 prev_code = 0, cur_code; - - while (i < ARRAY_SIZE(dcmipp_par_pix_map_list)) { - if (IS_SRC(pad)) - cur_code = dcmipp_par_pix_map_list[i].code_src; - else - cur_code = dcmipp_par_pix_map_list[i].code_sink; - - if (cur_code == prev_code) { - i++; - continue; - } - prev_code = cur_code; - - if (index == 0) - break; - i++; - index--; - } - - if (i >= ARRAY_SIZE(dcmipp_par_pix_map_list)) - return NULL; - - return &dcmipp_par_pix_map_list[i]; -} - -static inline const struct dcmipp_par_pix_map *dcmipp_par_pix_map_by_code - (u32 code_sink, u32 code_src) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(dcmipp_par_pix_map_list); i++) { - if ((dcmipp_par_pix_map_list[i].code_sink == code_sink && - dcmipp_par_pix_map_list[i].code_src == code_src) || - (dcmipp_par_pix_map_list[i].code_sink == code_src && - dcmipp_par_pix_map_list[i].code_src == code_sink) || - (dcmipp_par_pix_map_list[i].code_sink == code_sink && - code_src == 0) || - (code_sink == 0 && - dcmipp_par_pix_map_list[i].code_src == code_src)) - return &dcmipp_par_pix_map_list[i]; - } - return NULL; -} - -struct dcmipp_par_device { - struct dcmipp_ent_device ved; - struct v4l2_subdev sd; - struct device *dev; - void __iomem *regs; - bool streaming; -}; - -static const struct v4l2_mbus_framefmt fmt_default = { - .width = DCMIPP_FMT_WIDTH_DEFAULT, - .height = DCMIPP_FMT_HEIGHT_DEFAULT, - .code = MEDIA_BUS_FMT_RGB565_2X8_LE, - .field = V4L2_FIELD_NONE, - .colorspace = DCMIPP_COLORSPACE_DEFAULT, - .ycbcr_enc = DCMIPP_YCBCR_ENC_DEFAULT, - .quantization = DCMIPP_QUANTIZATION_DEFAULT, - .xfer_func = DCMIPP_XFER_FUNC_DEFAULT, -}; - -static int dcmipp_par_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; - - mf = v4l2_subdev_state_get_format(sd_state, i); - *mf = fmt_default; - } - - return 0; -} - -static int dcmipp_par_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_mbus_code_enum *code) -{ - const struct dcmipp_par_pix_map *vpix = - dcmipp_par_pix_map_by_index(code->index, code->pad); - - if (!vpix) - return -EINVAL; - - code->code = IS_SRC(code->pad) ? vpix->code_src : vpix->code_sink; - - return 0; -} - -static int dcmipp_par_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_size_enum *fse) -{ - const struct dcmipp_par_pix_map *vpix; - - if (fse->index) - return -EINVAL; - - /* Only accept code in the pix map table */ - vpix = dcmipp_par_pix_map_by_code(IS_SINK(fse->pad) ? fse->code : 0, - IS_SRC(fse->pad) ? fse->code : 0); - if (!vpix) - return -EINVAL; - - fse->min_width = DCMIPP_FRAME_MIN_WIDTH; - fse->max_width = DCMIPP_FRAME_MAX_WIDTH; - fse->min_height = DCMIPP_FRAME_MIN_HEIGHT; - fse->max_height = DCMIPP_FRAME_MAX_HEIGHT; - - return 0; -} - -static void dcmipp_par_adjust_fmt(struct dcmipp_par_device *par, - struct v4l2_mbus_framefmt *fmt, __u32 pad) -{ - const struct dcmipp_par_pix_map *vpix; - - /* Only accept code in the pix map table */ - vpix = dcmipp_par_pix_map_by_code(IS_SINK(pad) ? fmt->code : 0, - IS_SRC(pad) ? fmt->code : 0); - if (!vpix) - fmt->code = fmt_default.code; - - /* Exclude JPEG if BT656 bus is selected */ - if (vpix && vpix->code_sink == MEDIA_BUS_FMT_JPEG_1X8 && - par->ved.bus_type == V4L2_MBUS_BT656) - fmt->code = fmt_default.code; - - fmt->width = clamp_t(u32, fmt->width, DCMIPP_FRAME_MIN_WIDTH, - DCMIPP_FRAME_MAX_WIDTH) & ~1; - fmt->height = clamp_t(u32, fmt->height, DCMIPP_FRAME_MIN_HEIGHT, - DCMIPP_FRAME_MAX_HEIGHT) & ~1; - - if (fmt->field == V4L2_FIELD_ANY || fmt->field == V4L2_FIELD_ALTERNATE) - fmt->field = fmt_default.field; - - dcmipp_colorimetry_clamp(fmt); -} - -static int dcmipp_par_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct dcmipp_par_device *par = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *mf; - - if (par->streaming) - return -EBUSY; - - mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); - - /* Set the new format */ - dcmipp_par_adjust_fmt(par, &fmt->format, fmt->pad); - - dev_dbg(par->dev, "%s: format update: old:%dx%d (0x%x, %d, %d, %d, %d) new:%dx%d (0x%x, %d, %d, %d, %d)\n", - par->sd.name, - /* old */ - mf->width, mf->height, mf->code, - mf->colorspace, mf->quantization, - mf->xfer_func, mf->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); - - *mf = fmt->format; - - /* When setting the sink format, report that format on the src pad */ - if (IS_SINK(fmt->pad)) { - mf = v4l2_subdev_state_get_format(sd_state, 1); - *mf = fmt->format; - dcmipp_par_adjust_fmt(par, mf, 1); - } - - return 0; -} - -static const struct v4l2_subdev_pad_ops dcmipp_par_pad_ops = { - .enum_mbus_code = dcmipp_par_enum_mbus_code, - .enum_frame_size = dcmipp_par_enum_frame_size, - .get_fmt = v4l2_subdev_get_fmt, - .set_fmt = dcmipp_par_set_fmt, -}; - -static int dcmipp_par_configure(struct dcmipp_par_device *par) -{ - u32 val = 0; - const struct dcmipp_par_pix_map *vpix; - struct v4l2_subdev_state *state; - struct v4l2_mbus_framefmt *sink_fmt; - struct v4l2_mbus_framefmt *src_fmt; - - /* Set vertical synchronization polarity */ - if (par->ved.bus.flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) - val |= DCMIPP_PRCR_VSPOL; - - /* Set horizontal synchronization polarity */ - if (par->ved.bus.flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) - val |= DCMIPP_PRCR_HSPOL; - - /* Set pixel clock polarity */ - if (par->ved.bus.flags & V4L2_MBUS_PCLK_SAMPLE_RISING) - val |= DCMIPP_PRCR_PCKPOL; - - /* - * BT656 embedded synchronisation bus mode. - * - * Default SAV/EAV mode is supported here with default codes - * SAV=0xff000080 & EAV=0xff00009d. - * With DCMIPP this means LSC=SAV=0x80 & LEC=EAV=0x9d. - */ - if (par->ved.bus_type == V4L2_MBUS_BT656) { - val |= DCMIPP_PRCR_ESS; - - /* Unmask all codes */ - reg_write(par, DCMIPP_PRESUR, 0xffffffff);/* FEC:LEC:LSC:FSC */ - - /* Trig on LSC=0x80 & LEC=0x9d codes, ignore FSC and FEC */ - reg_write(par, DCMIPP_PRESCR, 0xff9d80ff);/* FEC:LEC:LSC:FSC */ - } - - /* Set format */ - state = v4l2_subdev_lock_and_get_active_state(&par->sd); - sink_fmt = v4l2_subdev_state_get_format(state, 0); - src_fmt = v4l2_subdev_state_get_format(state, 1); - v4l2_subdev_unlock_state(state); - - vpix = dcmipp_par_pix_map_by_code(sink_fmt->code, src_fmt->code); - if (!vpix) { - dev_err(par->dev, "Invalid sink/src format configuration\n"); - return -EINVAL; - } - - val |= vpix->prcr_format << DCMIPP_PRCR_FORMAT_SHIFT; - - /* swap cycles */ - if (vpix->prcr_swapcycles) - val |= DCMIPP_PRCR_SWAPCYCLES; - - reg_write(par, DCMIPP_PRCR, val); - - return 0; -} - -static int dcmipp_par_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct dcmipp_par_device *par = - container_of(sd, struct dcmipp_par_device, sd); - struct v4l2_subdev *s_subdev; - struct media_pad *pad; - int ret = 0; - - /* Get source subdev */ - pad = media_pad_remote_pad_first(&sd->entity.pads[0]); - if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) - return -EINVAL; - s_subdev = media_entity_to_v4l2_subdev(pad->entity); - - if (enable) { - ret = dcmipp_par_configure(par); - if (ret) - return ret; - - /* Enable parallel interface */ - reg_set(par, DCMIPP_PRCR, DCMIPP_PRCR_ENABLE); - - ret = v4l2_subdev_call(s_subdev, video, s_stream, enable); - if (ret < 0) { - dev_err(par->dev, - "failed to start source subdev streaming (%d)\n", - ret); - return ret; - } - } else { - ret = v4l2_subdev_call(s_subdev, video, s_stream, enable); - if (ret < 0) { - dev_err(par->dev, - "failed to stop source subdev streaming (%d)\n", - ret); - return ret; - } - - /* Disable parallel interface */ - reg_clear(par, DCMIPP_PRCR, DCMIPP_PRCR_ENABLE); - } - - par->streaming = enable; - - return ret; -} - -static const struct v4l2_subdev_video_ops dcmipp_par_video_ops = { - .s_stream = dcmipp_par_s_stream, -}; - -static const struct v4l2_subdev_ops dcmipp_par_ops = { - .pad = &dcmipp_par_pad_ops, - .video = &dcmipp_par_video_ops, -}; - -static void dcmipp_par_release(struct v4l2_subdev *sd) -{ - struct dcmipp_par_device *par = - container_of(sd, struct dcmipp_par_device, sd); - - kfree(par); -} - -static const struct v4l2_subdev_internal_ops dcmipp_par_int_ops = { - .init_state = dcmipp_par_init_state, - .release = dcmipp_par_release, -}; - -void dcmipp_par_ent_release(struct dcmipp_ent_device *ved) -{ - struct dcmipp_par_device *par = - container_of(ved, struct dcmipp_par_device, ved); - - dcmipp_ent_sd_unregister(ved, &par->sd); -} - -struct dcmipp_ent_device *dcmipp_par_ent_init(struct device *dev, - const char *entity_name, - struct v4l2_device *v4l2_dev, - void __iomem *regs) -{ - struct dcmipp_par_device *par; - const unsigned long pads_flag[] = { - MEDIA_PAD_FL_SINK, MEDIA_PAD_FL_SOURCE, - }; - int ret; - - /* Allocate the par struct */ - par = kzalloc(sizeof(*par), GFP_KERNEL); - if (!par) - return ERR_PTR(-ENOMEM); - - par->regs = regs; - - /* Initialize ved and sd */ - ret = dcmipp_ent_sd_register(&par->ved, &par->sd, v4l2_dev, - entity_name, MEDIA_ENT_F_VID_IF_BRIDGE, - ARRAY_SIZE(pads_flag), pads_flag, - &dcmipp_par_int_ops, &dcmipp_par_ops, - NULL, NULL); - if (ret) { - kfree(par); - return ERR_PTR(ret); - } - - par->dev = dev; - - return &par->ved; -} diff --git a/drivers/media/platform/verisilicon/hantro.h b/drivers/media/platform/verisilicon/hantro.h index 811260dc3c77..edc217eed293 100644 --- a/drivers/media/platform/verisilicon/hantro.h +++ b/drivers/media/platform/verisilicon/hantro.h @@ -227,6 +227,7 @@ struct hantro_dev { * @src_fmt: V4L2 pixel format of active source format. * @vpu_dst_fmt: Descriptor of active destination format. * @dst_fmt: V4L2 pixel format of active destination format. + * @ref_fmt: V4L2 pixel format of the reference frames format. * * @ctrl_handler: Control handler used to register controls. * @jpeg_quality: User-specified JPEG compression quality. @@ -255,6 +256,7 @@ struct hantro_ctx { struct v4l2_pix_format_mplane src_fmt; const struct hantro_fmt *vpu_dst_fmt; struct v4l2_pix_format_mplane dst_fmt; + struct v4l2_pix_format_mplane ref_fmt; struct v4l2_ctrl_handler ctrl_handler; int jpeg_quality; @@ -332,12 +334,19 @@ struct hantro_vp9_decoded_buffer_info { u32 bit_depth : 4; }; +struct hantro_av1_decoded_buffer_info { + /* Info needed when the decoded frame serves as a reference frame. */ + size_t chroma_offset; + size_t mv_offset; +}; + struct hantro_decoded_buffer { /* Must be the first field in this struct. */ struct v4l2_m2m_buffer base; union { struct hantro_vp9_decoded_buffer_info vp9; + struct hantro_av1_decoded_buffer_info av1; }; }; diff --git a/drivers/media/platform/verisilicon/hantro_g2.c b/drivers/media/platform/verisilicon/hantro_g2.c index 5c1d799d8618..aae0b562fabb 100644 --- a/drivers/media/platform/verisilicon/hantro_g2.c +++ b/drivers/media/platform/verisilicon/hantro_g2.c @@ -47,7 +47,7 @@ irqreturn_t hantro_g2_irq(int irq, void *dev_id) size_t hantro_g2_chroma_offset(struct hantro_ctx *ctx) { - return ctx->dst_fmt.width * ctx->dst_fmt.height * ctx->bit_depth / 8; + return ctx->ref_fmt.plane_fmt[0].bytesperline * ctx->ref_fmt.height; } size_t hantro_g2_motion_vectors_offset(struct hantro_ctx *ctx) diff --git a/drivers/media/platform/verisilicon/hantro_postproc.c b/drivers/media/platform/verisilicon/hantro_postproc.c index 232c93eea7ee..c435a393e0cb 100644 --- a/drivers/media/platform/verisilicon/hantro_postproc.c +++ b/drivers/media/platform/verisilicon/hantro_postproc.c @@ -194,35 +194,25 @@ void hantro_postproc_free(struct hantro_ctx *ctx) static unsigned int hantro_postproc_buffer_size(struct hantro_ctx *ctx) { - struct v4l2_pix_format_mplane pix_mp; - const struct hantro_fmt *fmt; unsigned int buf_size; - /* this should always pick native format */ - fmt = hantro_get_default_fmt(ctx, false, ctx->bit_depth, HANTRO_AUTO_POSTPROC); - if (!fmt) - return 0; - - v4l2_fill_pixfmt_mp(&pix_mp, fmt->fourcc, ctx->src_fmt.width, - ctx->src_fmt.height); - - buf_size = pix_mp.plane_fmt[0].sizeimage; + buf_size = ctx->ref_fmt.plane_fmt[0].sizeimage; if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_H264_SLICE) - buf_size += hantro_h264_mv_size(pix_mp.width, - pix_mp.height); + buf_size += hantro_h264_mv_size(ctx->ref_fmt.width, + ctx->ref_fmt.height); else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_VP9_FRAME) - buf_size += hantro_vp9_mv_size(pix_mp.width, - pix_mp.height); + buf_size += hantro_vp9_mv_size(ctx->ref_fmt.width, + ctx->ref_fmt.height); else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_HEVC_SLICE) { - buf_size += hantro_hevc_mv_size(pix_mp.width, - pix_mp.height); + buf_size += hantro_hevc_mv_size(ctx->ref_fmt.width, + ctx->ref_fmt.height); if (ctx->hevc_dec.use_compression) - buf_size += hantro_hevc_compressed_size(pix_mp.width, - pix_mp.height); + buf_size += hantro_hevc_compressed_size(ctx->ref_fmt.width, + ctx->ref_fmt.height); } else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_AV1_FRAME) - buf_size += hantro_av1_mv_size(pix_mp.width, - pix_mp.height); + buf_size += hantro_av1_mv_size(ctx->ref_fmt.width, + ctx->ref_fmt.height); return buf_size; } diff --git a/drivers/media/platform/verisilicon/hantro_v4l2.c b/drivers/media/platform/verisilicon/hantro_v4l2.c index 2513adfbd825..2bce940a5822 100644 --- a/drivers/media/platform/verisilicon/hantro_v4l2.c +++ b/drivers/media/platform/verisilicon/hantro_v4l2.c @@ -126,6 +126,24 @@ hantro_find_format(const struct hantro_ctx *ctx, u32 fourcc) return NULL; } +static int +hantro_set_reference_frames_format(struct hantro_ctx *ctx) +{ + const struct hantro_fmt *fmt; + int dst_bit_depth = hantro_get_format_depth(ctx->vpu_dst_fmt->fourcc); + + fmt = hantro_get_default_fmt(ctx, false, dst_bit_depth, HANTRO_AUTO_POSTPROC); + if (!fmt) + return -EINVAL; + + ctx->ref_fmt.width = ctx->src_fmt.width; + ctx->ref_fmt.height = ctx->src_fmt.height; + + v4l2_apply_frmsize_constraints(&ctx->ref_fmt.width, &ctx->ref_fmt.height, &fmt->frmsize); + return v4l2_fill_pixfmt_mp(&ctx->ref_fmt, fmt->fourcc, + ctx->ref_fmt.width, ctx->ref_fmt.height); +} + const struct hantro_fmt * hantro_get_default_fmt(const struct hantro_ctx *ctx, bool bitstream, int bit_depth, bool need_postproc) @@ -595,6 +613,9 @@ static int hantro_set_fmt_cap(struct hantro_ctx *ctx, ctx->vpu_dst_fmt = hantro_find_format(ctx, pix_mp->pixelformat); ctx->dst_fmt = *pix_mp; + ret = hantro_set_reference_frames_format(ctx); + if (ret) + return ret; /* * Current raw format might have become invalid with newly diff --git a/drivers/media/platform/verisilicon/imx8m_vpu_hw.c b/drivers/media/platform/verisilicon/imx8m_vpu_hw.c index f850d8bddef6..35799da534ed 100644 --- a/drivers/media/platform/verisilicon/imx8m_vpu_hw.c +++ b/drivers/media/platform/verisilicon/imx8m_vpu_hw.c @@ -187,23 +187,23 @@ static const struct hantro_fmt imx8m_vpu_g2_dec_fmts[] = { .frmsize = { .min_width = FMT_MIN_WIDTH, .max_width = FMT_UHD_WIDTH, - .step_width = TILE_MB_DIM, + .step_width = 8, .min_height = FMT_MIN_HEIGHT, .max_height = FMT_UHD_HEIGHT, - .step_height = TILE_MB_DIM, + .step_height = 32, }, }, { - .fourcc = V4L2_PIX_FMT_P010_4L4, + .fourcc = V4L2_PIX_FMT_NV15_4L4, .codec_mode = HANTRO_MODE_NONE, .match_depth = true, .frmsize = { .min_width = FMT_MIN_WIDTH, .max_width = FMT_UHD_WIDTH, - .step_width = TILE_MB_DIM, + .step_width = 8, .min_height = FMT_MIN_HEIGHT, .max_height = FMT_UHD_HEIGHT, - .step_height = TILE_MB_DIM, + .step_height = 32, }, }, { diff --git a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c index e54f5fac325b..69b5d9e12926 100644 --- a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c +++ b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c @@ -686,8 +686,6 @@ rockchip_vpu981_av1_dec_set_ref(struct hantro_ctx *ctx, int ref, int idx, struct hantro_dev *vpu = ctx->dev; struct hantro_decoded_buffer *dst; dma_addr_t luma_addr, chroma_addr, mv_addr = 0; - size_t cr_offset = rockchip_vpu981_av1_dec_luma_size(ctx); - size_t mv_offset = rockchip_vpu981_av1_dec_chroma_size(ctx); int cur_width = frame->frame_width_minus_1 + 1; int cur_height = frame->frame_height_minus_1 + 1; int scale_width = @@ -744,8 +742,8 @@ rockchip_vpu981_av1_dec_set_ref(struct hantro_ctx *ctx, int ref, int idx, dst = vb2_to_hantro_decoded_buf(&av1_dec->frame_refs[idx].vb2_ref->vb2_buf); luma_addr = hantro_get_dec_buf_addr(ctx, &dst->base.vb.vb2_buf); - chroma_addr = luma_addr + cr_offset; - mv_addr = luma_addr + mv_offset; + chroma_addr = luma_addr + dst->av1.chroma_offset; + mv_addr = luma_addr + dst->av1.mv_offset; hantro_write_addr(vpu, AV1_REFERENCE_Y(ref), luma_addr); hantro_write_addr(vpu, AV1_REFERENCE_CB(ref), chroma_addr); @@ -2089,6 +2087,9 @@ rockchip_vpu981_av1_dec_set_output_buffer(struct hantro_ctx *ctx) chroma_addr = luma_addr + cr_offset; mv_addr = luma_addr + mv_offset; + dst->av1.chroma_offset = cr_offset; + dst->av1.mv_offset = mv_offset; + hantro_write_addr(vpu, AV1_TILE_OUT_LU, luma_addr); hantro_write_addr(vpu, AV1_TILE_OUT_CH, chroma_addr); hantro_write_addr(vpu, AV1_TILE_OUT_MV, mv_addr); diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index d52eccdc7eb9..72776d08046a 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -221,10 +221,6 @@ config USB_RAREMONO source "drivers/media/radio/si470x/Kconfig" source "drivers/media/radio/si4713/Kconfig" -# TI's ST based wl128x FM radio - -source "drivers/media/radio/wl128x/Kconfig" - # # ISA drivers configuration # diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile index cfb6af7d3bc3..1ff46f3a6ed3 100644 --- a/drivers/media/radio/Makefile +++ b/drivers/media/radio/Makefile @@ -31,7 +31,6 @@ obj-$(CONFIG_RADIO_TIMBERDALE) += radio-timb.o obj-$(CONFIG_RADIO_TRUST) += radio-trust.o obj-$(CONFIG_RADIO_TYPHOON) += radio-typhoon.o obj-$(CONFIG_RADIO_WL1273) += radio-wl1273.o -obj-$(CONFIG_RADIO_WL128X) += wl128x/ obj-$(CONFIG_RADIO_ZOLTRIX) += radio-zoltrix.o obj-$(CONFIG_USB_DSBR) += dsbr100.o diff --git a/drivers/media/radio/wl128x/Kconfig b/drivers/media/radio/wl128x/Kconfig deleted file mode 100644 index 3e7713872e3f..000000000000 --- a/drivers/media/radio/wl128x/Kconfig +++ /dev/null @@ -1,15 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# TI's wl128x FM driver based on TI's ST driver. -# -config RADIO_WL128X - tristate "Texas Instruments WL128x FM Radio" - depends on VIDEO_DEV && RFKILL && TTY && TI_ST - depends on GPIOLIB || COMPILE_TEST - help - Choose Y here if you have this FM radio chip. - - In order to control your radio card, you will need to use programs - that are compatible with the Video For Linux 2 API. Information on - this API and pointers to "v4l2" programs may be found at - <file:Documentation/userspace-api/media/index.rst>. diff --git a/drivers/media/radio/wl128x/Makefile b/drivers/media/radio/wl128x/Makefile deleted file mode 100644 index 4396ca416cfa..000000000000 --- a/drivers/media/radio/wl128x/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Makefile for TI's shared transport driver based wl128x -# FM radio. -# -obj-$(CONFIG_RADIO_WL128X) += fm_drv.o -fm_drv-objs := fmdrv_common.o fmdrv_rx.o fmdrv_tx.o fmdrv_v4l2.o diff --git a/drivers/media/radio/wl128x/fmdrv.h b/drivers/media/radio/wl128x/fmdrv.h deleted file mode 100644 index 03117a41dbd4..000000000000 --- a/drivers/media/radio/wl128x/fmdrv.h +++ /dev/null @@ -1,229 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * FM Driver for Connectivity chip of Texas Instruments. - * - * Common header for all FM driver sub-modules. - * - * Copyright (C) 2011 Texas Instruments - */ - -#ifndef _FM_DRV_H -#define _FM_DRV_H - -#include <linux/skbuff.h> -#include <linux/interrupt.h> -#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> -#include <media/v4l2-ctrls.h> - -#define FM_DRV_VERSION "0.1.1" -#define FM_DRV_NAME "ti_fmdrv" -#define FM_DRV_CARD_SHORT_NAME "TI FM Radio" -#define FM_DRV_CARD_LONG_NAME "Texas Instruments FM Radio" - -/* Flag info */ -#define FM_INTTASK_RUNNING 0 -#define FM_INTTASK_SCHEDULE_PENDING 1 -#define FM_FW_DW_INPROGRESS 2 -#define FM_CORE_READY 3 -#define FM_CORE_TRANSPORT_READY 4 -#define FM_AF_SWITCH_INPROGRESS 5 -#define FM_CORE_TX_XMITING 6 - -#define FM_TUNE_COMPLETE 0x1 -#define FM_BAND_LIMIT 0x2 - -#define FM_DRV_TX_TIMEOUT (5*HZ) /* 5 seconds */ -#define FM_DRV_RX_SEEK_TIMEOUT (20*HZ) /* 20 seconds */ - -#define fmerr(format, ...) \ - printk(KERN_ERR "fmdrv: " format, ## __VA_ARGS__) -#define fmwarn(format, ...) \ - printk(KERN_WARNING "fmdrv: " format, ##__VA_ARGS__) -#ifdef DEBUG -#define fmdbg(format, ...) \ - printk(KERN_DEBUG "fmdrv: " format, ## __VA_ARGS__) -#else /* DEBUG */ -#define fmdbg(format, ...) do {} while(0) -#endif -enum { - FM_MODE_OFF, - FM_MODE_TX, - FM_MODE_RX, - FM_MODE_ENTRY_MAX -}; - -#define FM_RX_RDS_INFO_FIELD_MAX 8 /* 4 Group * 2 Bytes */ - -/* RX RDS data format */ -struct fm_rdsdata_format { - union { - struct { - u8 buff[FM_RX_RDS_INFO_FIELD_MAX]; - } groupdatabuff; - struct { - u16 pidata; - u8 blk_b[2]; - u8 blk_c[2]; - u8 blk_d[2]; - } groupgeneral; - struct { - u16 pidata; - u8 blk_b[2]; - u8 af[2]; - u8 ps[2]; - } group0A; - struct { - u16 pi[2]; - u8 blk_b[2]; - u8 ps[2]; - } group0B; - } data; -}; - -/* FM region (Europe/US, Japan) info */ -struct region_info { - u32 chanl_space; - u32 bot_freq; - u32 top_freq; - u8 fm_band; -}; -struct fmdev; -typedef void (*int_handler_prototype) (struct fmdev *); - -/* FM Interrupt processing related info */ -struct fm_irq { - u8 stage; - u16 flag; /* FM interrupt flag */ - u16 mask; /* FM interrupt mask */ - /* Interrupt process timeout handler */ - struct timer_list timer; - u8 retry; - int_handler_prototype *handlers; -}; - -/* RDS info */ -struct fm_rds { - u8 flag; /* RX RDS on/off status */ - u8 last_blk_idx; /* Last received RDS block */ - - /* RDS buffer */ - wait_queue_head_t read_queue; - u32 buf_size; /* Size is always multiple of 3 */ - u32 wr_idx; - u32 rd_idx; - u8 *buff; -}; - -#define FM_RDS_MAX_AF_LIST 25 - -/* - * Current RX channel Alternate Frequency cache. - * This info is used to switch to other freq (AF) - * when current channel signal strength is below RSSI threshold. - */ -struct tuned_station_info { - u16 picode; - u32 af_cache[FM_RDS_MAX_AF_LIST]; - u8 afcache_size; - u8 af_list_max; -}; - -/* FM RX mode info */ -struct fm_rx { - struct region_info region; /* Current selected band */ - u32 freq; /* Current RX frquency */ - u8 mute_mode; /* Current mute mode */ - u8 deemphasis_mode; /* Current deemphasis mode */ - /* RF dependent soft mute mode */ - u8 rf_depend_mute; - u16 volume; /* Current volume level */ - u16 rssi_threshold; /* Current RSSI threshold level */ - /* Holds the index of the current AF jump */ - u8 afjump_idx; - /* Will hold the frequency before the jump */ - u32 freq_before_jump; - u8 rds_mode; /* RDS operation mode (RDS/RDBS) */ - u8 af_mode; /* Alternate frequency on/off */ - struct tuned_station_info stat_info; - struct fm_rds rds; -}; - -#define FMTX_RDS_TXT_STR_SIZE 25 -/* - * FM TX RDS data - * - * @ text_type: is the text following PS or RT - * @ text: radio text string which could either be PS or RT - * @ af_freq: alternate frequency for Tx - * TODO: to be declared in application - */ -struct tx_rds { - u8 text_type; - u8 text[FMTX_RDS_TXT_STR_SIZE]; - u8 flag; - u32 af_freq; -}; -/* - * FM TX global data - * - * @ pwr_lvl: Power Level of the Transmission from mixer control - * @ xmit_state: Transmission state = Updated locally upon Start/Stop - * @ audio_io: i2S/Analog - * @ tx_frq: Transmission frequency - */ -struct fmtx_data { - u8 pwr_lvl; - u8 xmit_state; - u8 audio_io; - u8 region; - u16 aud_mode; - u32 preemph; - u32 tx_frq; - struct tx_rds rds; -}; - -/* FM driver operation structure */ -struct fmdev { - struct video_device *radio_dev; /* V4L2 video device pointer */ - struct v4l2_device v4l2_dev; /* V4L2 top level struct */ - struct snd_card *card; /* Card which holds FM mixer controls */ - u16 asci_id; - spinlock_t rds_buff_lock; /* To protect access to RDS buffer */ - spinlock_t resp_skb_lock; /* To protect access to received SKB */ - - long flag; /* FM driver state machine info */ - int streg_cbdata; /* status of ST registration */ - - struct sk_buff_head rx_q; /* RX queue */ - struct work_struct rx_bh_work; /* RX BH Work */ - - struct sk_buff_head tx_q; /* TX queue */ - 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 */ - - struct sk_buff *resp_skb; /* Response from the chip */ - /* Main task completion handler */ - struct completion maintask_comp; - /* Opcode of last command sent to the chip */ - u8 pre_op; - /* Handler used for wakeup when response packet is received */ - struct completion *resp_comp; - struct fm_irq irq_info; - u8 curr_fmmode; /* Current FM chip mode (TX, RX, OFF) */ - struct fm_rx rx; /* FM receiver info */ - struct fmtx_data tx_data; - - /* V4L2 ctrl framework handler*/ - struct v4l2_ctrl_handler ctrl_handler; - - /* For core assisted locking */ - struct mutex mutex; -}; -#endif diff --git a/drivers/media/radio/wl128x/fmdrv_common.c b/drivers/media/radio/wl128x/fmdrv_common.c deleted file mode 100644 index 4d032436691c..000000000000 --- a/drivers/media/radio/wl128x/fmdrv_common.c +++ /dev/null @@ -1,1676 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * FM Driver for Connectivity chip of Texas Instruments. - * - * This sub-module of FM driver is common for FM RX and TX - * functionality. This module is responsible for: - * 1) Forming group of Channel-8 commands to perform particular - * functionality (eg., frequency set require more than - * 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 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) - * - * Copyright (C) 2011 Texas Instruments - * Author: Raja Mani <raja_mani@ti.com> - * Author: Manjunatha Halli <manjunatha_halli@ti.com> - */ - -#include <linux/delay.h> -#include <linux/firmware.h> -#include <linux/module.h> -#include <linux/nospec.h> -#include <linux/jiffies.h> - -#include "fmdrv.h" -#include "fmdrv_v4l2.h" -#include "fmdrv_common.h" -#include <linux/ti_wilink_st.h> -#include "fmdrv_rx.h" -#include "fmdrv_tx.h" - -/* Region info */ -static struct region_info region_configs[] = { - /* Europe/US */ - { - .chanl_space = FM_CHANNEL_SPACING_200KHZ * FM_FREQ_MUL, - .bot_freq = 87500, /* 87.5 MHz */ - .top_freq = 108000, /* 108 MHz */ - .fm_band = 0, - }, - /* Japan */ - { - .chanl_space = FM_CHANNEL_SPACING_200KHZ * FM_FREQ_MUL, - .bot_freq = 76000, /* 76 MHz */ - .top_freq = 90000, /* 90 MHz */ - .fm_band = 1, - }, -}; - -/* Band selection */ -static u8 default_radio_region; /* Europe/US */ -module_param(default_radio_region, byte, 0); -MODULE_PARM_DESC(default_radio_region, "Region: 0=Europe/US, 1=Japan"); - -/* RDS buffer blocks */ -static u32 default_rds_buf = 300; -module_param(default_rds_buf, uint, 0444); -MODULE_PARM_DESC(default_rds_buf, "RDS buffer entries"); - -/* Radio Nr */ -static u32 radio_nr = -1; -module_param(radio_nr, int, 0444); -MODULE_PARM_DESC(radio_nr, "Radio Nr"); - -/* FM irq handlers forward declaration */ -static void fm_irq_send_flag_getcmd(struct fmdev *); -static void fm_irq_handle_flag_getcmd_resp(struct fmdev *); -static void fm_irq_handle_hw_malfunction(struct fmdev *); -static void fm_irq_handle_rds_start(struct fmdev *); -static void fm_irq_send_rdsdata_getcmd(struct fmdev *); -static void fm_irq_handle_rdsdata_getcmd_resp(struct fmdev *); -static void fm_irq_handle_rds_finish(struct fmdev *); -static void fm_irq_handle_tune_op_ended(struct fmdev *); -static void fm_irq_handle_power_enb(struct fmdev *); -static void fm_irq_handle_low_rssi_start(struct fmdev *); -static void fm_irq_afjump_set_pi(struct fmdev *); -static void fm_irq_handle_set_pi_resp(struct fmdev *); -static void fm_irq_afjump_set_pimask(struct fmdev *); -static void fm_irq_handle_set_pimask_resp(struct fmdev *); -static void fm_irq_afjump_setfreq(struct fmdev *); -static void fm_irq_handle_setfreq_resp(struct fmdev *); -static void fm_irq_afjump_enableint(struct fmdev *); -static void fm_irq_afjump_enableint_resp(struct fmdev *); -static void fm_irq_start_afjump(struct fmdev *); -static void fm_irq_handle_start_afjump_resp(struct fmdev *); -static void fm_irq_afjump_rd_freq(struct fmdev *); -static void fm_irq_afjump_rd_freq_resp(struct fmdev *); -static void fm_irq_handle_low_rssi_finish(struct fmdev *); -static void fm_irq_send_intmsk_cmd(struct fmdev *); -static void fm_irq_handle_intmsk_cmd_resp(struct fmdev *); - -/* - * When FM common module receives interrupt packet, following handlers - * will be executed one after another to service the interrupt(s) - */ -enum fmc_irq_handler_index { - FM_SEND_FLAG_GETCMD_IDX, - FM_HANDLE_FLAG_GETCMD_RESP_IDX, - - /* HW malfunction irq handler */ - FM_HW_MAL_FUNC_IDX, - - /* RDS threshold reached irq handler */ - FM_RDS_START_IDX, - FM_RDS_SEND_RDS_GETCMD_IDX, - FM_RDS_HANDLE_RDS_GETCMD_RESP_IDX, - FM_RDS_FINISH_IDX, - - /* Tune operation ended irq handler */ - FM_HW_TUNE_OP_ENDED_IDX, - - /* TX power enable irq handler */ - FM_HW_POWER_ENB_IDX, - - /* Low RSSI irq handler */ - FM_LOW_RSSI_START_IDX, - FM_AF_JUMP_SETPI_IDX, - FM_AF_JUMP_HANDLE_SETPI_RESP_IDX, - FM_AF_JUMP_SETPI_MASK_IDX, - FM_AF_JUMP_HANDLE_SETPI_MASK_RESP_IDX, - FM_AF_JUMP_SET_AF_FREQ_IDX, - FM_AF_JUMP_HANDLE_SET_AFFREQ_RESP_IDX, - FM_AF_JUMP_ENABLE_INT_IDX, - FM_AF_JUMP_ENABLE_INT_RESP_IDX, - FM_AF_JUMP_START_AFJUMP_IDX, - FM_AF_JUMP_HANDLE_START_AFJUMP_RESP_IDX, - FM_AF_JUMP_RD_FREQ_IDX, - FM_AF_JUMP_RD_FREQ_RESP_IDX, - FM_LOW_RSSI_FINISH_IDX, - - /* Interrupt process post action */ - FM_SEND_INTMSK_CMD_IDX, - FM_HANDLE_INTMSK_CMD_RESP_IDX, -}; - -/* FM interrupt handler table */ -static int_handler_prototype int_handler_table[] = { - fm_irq_send_flag_getcmd, - fm_irq_handle_flag_getcmd_resp, - fm_irq_handle_hw_malfunction, - fm_irq_handle_rds_start, /* RDS threshold reached irq handler */ - fm_irq_send_rdsdata_getcmd, - fm_irq_handle_rdsdata_getcmd_resp, - fm_irq_handle_rds_finish, - fm_irq_handle_tune_op_ended, - fm_irq_handle_power_enb, /* TX power enable irq handler */ - fm_irq_handle_low_rssi_start, - fm_irq_afjump_set_pi, - fm_irq_handle_set_pi_resp, - fm_irq_afjump_set_pimask, - fm_irq_handle_set_pimask_resp, - fm_irq_afjump_setfreq, - fm_irq_handle_setfreq_resp, - fm_irq_afjump_enableint, - fm_irq_afjump_enableint_resp, - fm_irq_start_afjump, - fm_irq_handle_start_afjump_resp, - fm_irq_afjump_rd_freq, - fm_irq_afjump_rd_freq_resp, - fm_irq_handle_low_rssi_finish, - fm_irq_send_intmsk_cmd, /* Interrupt process post action */ - fm_irq_handle_intmsk_cmd_resp -}; - -static long (*g_st_write) (struct sk_buff *skb); -static struct completion wait_for_fmdrv_reg_comp; - -static inline void fm_irq_call(struct fmdev *fmdev) -{ - fmdev->irq_info.handlers[fmdev->irq_info.stage](fmdev); -} - -/* Continue next function in interrupt handler table */ -static inline void fm_irq_call_stage(struct fmdev *fmdev, u8 stage) -{ - fmdev->irq_info.stage = stage; - fm_irq_call(fmdev); -} - -static inline void fm_irq_timeout_stage(struct fmdev *fmdev, u8 stage) -{ - fmdev->irq_info.stage = stage; - mod_timer(&fmdev->irq_info.timer, jiffies + FM_DRV_TX_TIMEOUT); -} - -#ifdef FM_DUMP_TXRX_PKT - /* To dump outgoing FM Channel-8 packets */ -inline void dump_tx_skb_data(struct sk_buff *skb) -{ - int len, len_org; - u8 index; - struct fm_cmd_msg_hdr *cmd_hdr; - - cmd_hdr = (struct fm_cmd_msg_hdr *)skb->data; - printk(KERN_INFO "<<%shdr:%02x len:%02x opcode:%02x type:%s dlen:%02x", - fm_cb(skb)->completion ? " " : "*", cmd_hdr->hdr, - cmd_hdr->len, cmd_hdr->op, - cmd_hdr->rd_wr ? "RD" : "WR", cmd_hdr->dlen); - - len_org = skb->len - FM_CMD_MSG_HDR_SIZE; - if (len_org > 0) { - printk(KERN_CONT "\n data(%d): ", cmd_hdr->dlen); - len = min(len_org, 14); - for (index = 0; index < len; index++) - printk(KERN_CONT "%x ", - skb->data[FM_CMD_MSG_HDR_SIZE + index]); - printk(KERN_CONT "%s", (len_org > 14) ? ".." : ""); - } - printk(KERN_CONT "\n"); -} - - /* To dump incoming FM Channel-8 packets */ -inline void dump_rx_skb_data(struct sk_buff *skb) -{ - int len, len_org; - u8 index; - struct fm_event_msg_hdr *evt_hdr; - - evt_hdr = (struct fm_event_msg_hdr *)skb->data; - printk(KERN_INFO ">> hdr:%02x len:%02x sts:%02x numhci:%02x opcode:%02x type:%s dlen:%02x", - evt_hdr->hdr, evt_hdr->len, - evt_hdr->status, evt_hdr->num_fm_hci_cmds, evt_hdr->op, - (evt_hdr->rd_wr) ? "RD" : "WR", evt_hdr->dlen); - - len_org = skb->len - FM_EVT_MSG_HDR_SIZE; - if (len_org > 0) { - printk(KERN_CONT "\n data(%d): ", evt_hdr->dlen); - len = min(len_org, 14); - for (index = 0; index < len; index++) - printk(KERN_CONT "%x ", - skb->data[FM_EVT_MSG_HDR_SIZE + index]); - printk(KERN_CONT "%s", (len_org > 14) ? ".." : ""); - } - printk(KERN_CONT "\n"); -} -#endif - -void fmc_update_region_info(struct fmdev *fmdev, u8 region_to_set) -{ - fmdev->rx.region = region_configs[region_to_set]; -} - -/* - * FM common sub-module will queue this bh work whenever it receives - * FM packet from ST driver. - */ -static void recv_bh_work(struct work_struct *t) -{ - struct fmdev *fmdev; - struct fm_irq *irq_info; - struct fm_event_msg_hdr *evt_hdr; - struct sk_buff *skb; - u8 num_fm_hci_cmds; - unsigned long flags; - - 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))) { - if (skb->len < sizeof(struct fm_event_msg_hdr)) { - fmerr("skb(%p) has only %d bytes, at least need %zu bytes to decode\n", - skb, - skb->len, sizeof(struct fm_event_msg_hdr)); - kfree_skb(skb); - continue; - } - - evt_hdr = (void *)skb->data; - num_fm_hci_cmds = evt_hdr->num_fm_hci_cmds; - - /* FM interrupt packet? */ - if (evt_hdr->op == FM_INTERRUPT) { - /* FM interrupt handler started already? */ - if (!test_bit(FM_INTTASK_RUNNING, &fmdev->flag)) { - set_bit(FM_INTTASK_RUNNING, &fmdev->flag); - if (irq_info->stage != 0) { - fmerr("Inval stage resetting to zero\n"); - irq_info->stage = 0; - } - - /* - * Execute first function in interrupt handler - * table. - */ - irq_info->handlers[irq_info->stage](fmdev); - } else { - set_bit(FM_INTTASK_SCHEDULE_PENDING, &fmdev->flag); - } - kfree_skb(skb); - } - /* Anyone waiting for this with completion handler? */ - else if (evt_hdr->op == fmdev->pre_op && fmdev->resp_comp != NULL) { - - spin_lock_irqsave(&fmdev->resp_skb_lock, flags); - fmdev->resp_skb = skb; - spin_unlock_irqrestore(&fmdev->resp_skb_lock, flags); - complete(fmdev->resp_comp); - - fmdev->resp_comp = NULL; - atomic_set(&fmdev->tx_cnt, 1); - } - /* Is this for interrupt handler? */ - else if (evt_hdr->op == fmdev->pre_op && fmdev->resp_comp == NULL) { - if (fmdev->resp_skb != NULL) - fmerr("Response SKB ptr not NULL\n"); - - spin_lock_irqsave(&fmdev->resp_skb_lock, flags); - fmdev->resp_skb = skb; - spin_unlock_irqrestore(&fmdev->resp_skb_lock, flags); - - /* Execute interrupt handler where state index points */ - irq_info->handlers[irq_info->stage](fmdev); - - kfree_skb(skb); - atomic_set(&fmdev->tx_cnt, 1); - } else { - fmerr("Nobody claimed SKB(%p),purging\n", skb); - } - - /* - * Check flow control field. If Num_FM_HCI_Commands field is - * not zero, queue FM TX bh work. - */ - if (num_fm_hci_cmds && atomic_read(&fmdev->tx_cnt)) - if (!skb_queue_empty(&fmdev->tx_q)) - queue_work(system_bh_wq, &fmdev->tx_bh_work); - } -} - -/* 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_work(fmdev, t, tx_bh_work); - - if (!atomic_read(&fmdev->tx_cnt)) - return; - - /* Check, is there any timeout happened to last transmitted packet */ - if (time_is_before_jiffies(fmdev->last_tx_jiffies + FM_DRV_TX_TIMEOUT)) { - fmerr("TX timeout occurred\n"); - atomic_set(&fmdev->tx_cnt, 1); - } - - /* Send queued FM TX packets */ - skb = skb_dequeue(&fmdev->tx_q); - if (!skb) - return; - - atomic_dec(&fmdev->tx_cnt); - fmdev->pre_op = fm_cb(skb)->fm_op; - - if (fmdev->resp_comp != NULL) - fmerr("Response completion handler is not NULL\n"); - - fmdev->resp_comp = fm_cb(skb)->completion; - - /* Write FM packet to ST driver */ - len = g_st_write(skb); - if (len < 0) { - kfree_skb(skb); - fmdev->resp_comp = NULL; - fmerr("TX bh work failed to send skb(%p)\n", skb); - atomic_set(&fmdev->tx_cnt, 1); - } else { - fmdev->last_tx_jiffies = jiffies; - } -} - -/* - * 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, - int payload_len, struct completion *wait_completion) -{ - struct sk_buff *skb; - struct fm_cmd_msg_hdr *hdr; - int size; - - if (fm_op >= FM_INTERRUPT) { - fmerr("Invalid fm opcode - %d\n", fm_op); - return -EINVAL; - } - if (test_bit(FM_FW_DW_INPROGRESS, &fmdev->flag) && payload == NULL) { - fmerr("Payload data is NULL during fw download\n"); - return -EINVAL; - } - if (!test_bit(FM_FW_DW_INPROGRESS, &fmdev->flag)) - size = - FM_CMD_MSG_HDR_SIZE + ((payload == NULL) ? 0 : payload_len); - else - size = payload_len; - - skb = alloc_skb(size, GFP_ATOMIC); - if (!skb) { - fmerr("No memory to create new SKB\n"); - return -ENOMEM; - } - /* - * Don't fill FM header info for the commands which come from - * FM firmware file. - */ - if (!test_bit(FM_FW_DW_INPROGRESS, &fmdev->flag) || - test_bit(FM_INTTASK_RUNNING, &fmdev->flag)) { - /* Fill command header info */ - hdr = skb_put(skb, FM_CMD_MSG_HDR_SIZE); - hdr->hdr = FM_PKT_LOGICAL_CHAN_NUMBER; /* 0x08 */ - - /* 3 (fm_opcode,rd_wr,dlen) + payload len) */ - hdr->len = ((payload == NULL) ? 0 : payload_len) + 3; - - /* FM opcode */ - hdr->op = fm_op; - - /* read/write type */ - hdr->rd_wr = type; - hdr->dlen = payload_len; - fm_cb(skb)->fm_op = fm_op; - - /* - * If firmware download has finished and the command is - * not a read command then payload is != NULL - a write - * command with u16 payload - convert to be16 - */ - if (payload != NULL) - *(__be16 *)payload = cpu_to_be16(*(u16 *)payload); - - } else if (payload != NULL) { - fm_cb(skb)->fm_op = *((u8 *)payload + 2); - } - if (payload != NULL) - skb_put_data(skb, payload, payload_len); - - fm_cb(skb)->completion = wait_completion; - skb_queue_tail(&fmdev->tx_q, skb); - queue_work(system_bh_wq, &fmdev->tx_bh_work); - - return 0; -} - -/* Sends FM Channel-8 command to the chip and waits for the response */ -int fmc_send_cmd(struct fmdev *fmdev, u8 fm_op, u16 type, void *payload, - unsigned int payload_len, void *response, int *response_len) -{ - struct sk_buff *skb; - struct fm_event_msg_hdr *evt_hdr; - unsigned long flags; - int ret; - - init_completion(&fmdev->maintask_comp); - ret = fm_send_cmd(fmdev, fm_op, type, payload, payload_len, - &fmdev->maintask_comp); - if (ret) - return ret; - - if (!wait_for_completion_timeout(&fmdev->maintask_comp, - FM_DRV_TX_TIMEOUT)) { - fmerr("Timeout(%d sec),didn't get regcompletion signal from RX bh work\n", - jiffies_to_msecs(FM_DRV_TX_TIMEOUT) / 1000); - return -ETIMEDOUT; - } - spin_lock_irqsave(&fmdev->resp_skb_lock, flags); - if (!fmdev->resp_skb) { - spin_unlock_irqrestore(&fmdev->resp_skb_lock, flags); - fmerr("Response SKB is missing\n"); - return -EFAULT; - } - skb = fmdev->resp_skb; - fmdev->resp_skb = NULL; - spin_unlock_irqrestore(&fmdev->resp_skb_lock, flags); - - evt_hdr = (void *)skb->data; - if (evt_hdr->status != 0) { - fmerr("Received event pkt status(%d) is not zero\n", - evt_hdr->status); - kfree_skb(skb); - return -EIO; - } - /* Send response data to caller */ - if (response != NULL && response_len != NULL && evt_hdr->dlen && - evt_hdr->dlen <= payload_len) { - /* Skip header info and copy only response data */ - skb_pull(skb, sizeof(struct fm_event_msg_hdr)); - memcpy(response, skb->data, evt_hdr->dlen); - *response_len = evt_hdr->dlen; - } else if (response_len != NULL && evt_hdr->dlen == 0) { - *response_len = 0; - } - kfree_skb(skb); - - return 0; -} - -/* --- Helper functions used in FM interrupt handlers ---*/ -static inline int check_cmdresp_status(struct fmdev *fmdev, - struct sk_buff **skb) -{ - struct fm_event_msg_hdr *fm_evt_hdr; - unsigned long flags; - - del_timer(&fmdev->irq_info.timer); - - spin_lock_irqsave(&fmdev->resp_skb_lock, flags); - *skb = fmdev->resp_skb; - fmdev->resp_skb = NULL; - spin_unlock_irqrestore(&fmdev->resp_skb_lock, flags); - - fm_evt_hdr = (void *)(*skb)->data; - if (fm_evt_hdr->status != 0) { - fmerr("irq: opcode %x response status is not zero Initiating irq recovery process\n", - fm_evt_hdr->op); - - mod_timer(&fmdev->irq_info.timer, jiffies + FM_DRV_TX_TIMEOUT); - return -1; - } - - return 0; -} - -static inline void fm_irq_common_cmd_resp_helper(struct fmdev *fmdev, u8 stage) -{ - struct sk_buff *skb; - - if (!check_cmdresp_status(fmdev, &skb)) - fm_irq_call_stage(fmdev, stage); -} - -/* - * Interrupt process timeout handler. - * One of the irq handler did not get proper response from the chip. So take - * recovery action here. FM interrupts are disabled in the beginning of - * interrupt process. Therefore reset stage index to re-enable default - * interrupts. So that next interrupt will be processed as usual. - */ -static void int_timeout_handler(struct timer_list *t) -{ - struct fmdev *fmdev; - struct fm_irq *fmirq; - - fmdbg("irq: timeout,trying to re-enable fm interrupts\n"); - fmdev = from_timer(fmdev, t, irq_info.timer); - fmirq = &fmdev->irq_info; - fmirq->retry++; - - if (fmirq->retry > FM_IRQ_TIMEOUT_RETRY_MAX) { - /* Stop recovery action (interrupt reenable process) and - * reset stage index & retry count values */ - fmirq->stage = 0; - fmirq->retry = 0; - fmerr("Recovery action failed duringirq processing, max retry reached\n"); - return; - } - fm_irq_call_stage(fmdev, FM_SEND_INTMSK_CMD_IDX); -} - -/* --------- FM interrupt handlers ------------*/ -static void fm_irq_send_flag_getcmd(struct fmdev *fmdev) -{ - u16 flag; - - /* Send FLAG_GET command , to know the source of interrupt */ - if (!fm_send_cmd(fmdev, FLAG_GET, REG_RD, NULL, sizeof(flag), NULL)) - fm_irq_timeout_stage(fmdev, FM_HANDLE_FLAG_GETCMD_RESP_IDX); -} - -static void fm_irq_handle_flag_getcmd_resp(struct fmdev *fmdev) -{ - struct sk_buff *skb; - struct fm_event_msg_hdr *fm_evt_hdr; - - if (check_cmdresp_status(fmdev, &skb)) - return; - - fm_evt_hdr = (void *)skb->data; - if (fm_evt_hdr->dlen > sizeof(fmdev->irq_info.flag)) - return; - - /* Skip header info and copy only response data */ - skb_pull(skb, sizeof(struct fm_event_msg_hdr)); - memcpy(&fmdev->irq_info.flag, skb->data, fm_evt_hdr->dlen); - - fmdev->irq_info.flag = be16_to_cpu((__force __be16)fmdev->irq_info.flag); - fmdbg("irq: flag register(0x%x)\n", fmdev->irq_info.flag); - - /* Continue next function in interrupt handler table */ - fm_irq_call_stage(fmdev, FM_HW_MAL_FUNC_IDX); -} - -static void fm_irq_handle_hw_malfunction(struct fmdev *fmdev) -{ - if (fmdev->irq_info.flag & FM_MAL_EVENT & fmdev->irq_info.mask) - fmerr("irq: HW MAL int received - do nothing\n"); - - /* Continue next function in interrupt handler table */ - fm_irq_call_stage(fmdev, FM_RDS_START_IDX); -} - -static void fm_irq_handle_rds_start(struct fmdev *fmdev) -{ - if (fmdev->irq_info.flag & FM_RDS_EVENT & fmdev->irq_info.mask) { - fmdbg("irq: rds threshold reached\n"); - fmdev->irq_info.stage = FM_RDS_SEND_RDS_GETCMD_IDX; - } else { - /* Continue next function in interrupt handler table */ - fmdev->irq_info.stage = FM_HW_TUNE_OP_ENDED_IDX; - } - - fm_irq_call(fmdev); -} - -static void fm_irq_send_rdsdata_getcmd(struct fmdev *fmdev) -{ - /* Send the command to read RDS data from the chip */ - if (!fm_send_cmd(fmdev, RDS_DATA_GET, REG_RD, NULL, - (FM_RX_RDS_FIFO_THRESHOLD * 3), NULL)) - fm_irq_timeout_stage(fmdev, FM_RDS_HANDLE_RDS_GETCMD_RESP_IDX); -} - -/* Keeps track of current RX channel AF (Alternate Frequency) */ -static void fm_rx_update_af_cache(struct fmdev *fmdev, u8 af) -{ - struct tuned_station_info *stat_info = &fmdev->rx.stat_info; - u8 reg_idx = fmdev->rx.region.fm_band; - u8 index; - u32 freq; - - /* First AF indicates the number of AF follows. Reset the list */ - if ((af >= FM_RDS_1_AF_FOLLOWS) && (af <= FM_RDS_25_AF_FOLLOWS)) { - fmdev->rx.stat_info.af_list_max = (af - FM_RDS_1_AF_FOLLOWS + 1); - fmdev->rx.stat_info.afcache_size = 0; - fmdbg("No of expected AF : %d\n", fmdev->rx.stat_info.af_list_max); - return; - } - - if (af < FM_RDS_MIN_AF) - return; - if (reg_idx == FM_BAND_EUROPE_US && af > FM_RDS_MAX_AF) - return; - if (reg_idx == FM_BAND_JAPAN && af > FM_RDS_MAX_AF_JAPAN) - return; - - freq = fmdev->rx.region.bot_freq + (af * 100); - if (freq == fmdev->rx.freq) { - fmdbg("Current freq(%d) is matching with received AF(%d)\n", - fmdev->rx.freq, freq); - return; - } - /* Do check in AF cache */ - for (index = 0; index < stat_info->afcache_size; index++) { - if (stat_info->af_cache[index] == freq) - break; - } - /* Reached the limit of the list - ignore the next AF */ - if (index == stat_info->af_list_max) { - fmdbg("AF cache is full\n"); - return; - } - /* - * If we reached the end of the list then this AF is not - * in the list - add it. - */ - if (index == stat_info->afcache_size) { - fmdbg("Storing AF %d to cache index %d\n", freq, index); - stat_info->af_cache[index] = freq; - stat_info->afcache_size++; - } -} - -/* - * Converts RDS buffer data from big endian format - * to little endian format. - */ -static void fm_rdsparse_swapbytes(struct fmdev *fmdev, - struct fm_rdsdata_format *rds_format) -{ - u8 index = 0; - u8 *rds_buff; - - /* - * Since in Orca the 2 RDS Data bytes are in little endian and - * in Dolphin they are in big endian, the parsing of the RDS data - * is chip dependent - */ - if (fmdev->asci_id != 0x6350) { - rds_buff = &rds_format->data.groupdatabuff.buff[0]; - while (index + 1 < FM_RX_RDS_INFO_FIELD_MAX) { - swap(rds_buff[index], rds_buff[index + 1]); - index += 2; - } - } -} - -static void fm_irq_handle_rdsdata_getcmd_resp(struct fmdev *fmdev) -{ - struct sk_buff *skb; - struct fm_rdsdata_format rds_fmt; - struct fm_rds *rds = &fmdev->rx.rds; - unsigned long group_idx, flags; - u8 *rds_data, meta_data, tmpbuf[FM_RDS_BLK_SIZE]; - u8 type, blk_idx, idx; - u16 cur_picode; - u32 rds_len; - - if (check_cmdresp_status(fmdev, &skb)) - return; - - /* Skip header info */ - skb_pull(skb, sizeof(struct fm_event_msg_hdr)); - rds_data = skb->data; - rds_len = skb->len; - - /* Parse the RDS data */ - while (rds_len >= FM_RDS_BLK_SIZE) { - meta_data = rds_data[2]; - /* Get the type: 0=A, 1=B, 2=C, 3=C', 4=D, 5=E */ - type = (meta_data & 0x07); - - /* Transform the blk type into index sequence (0, 1, 2, 3, 4) */ - blk_idx = (type <= FM_RDS_BLOCK_C ? type : (type - 1)); - fmdbg("Block index:%d(%s)\n", blk_idx, - (meta_data & FM_RDS_STATUS_ERR_MASK) ? "Bad" : "Ok"); - - if ((meta_data & FM_RDS_STATUS_ERR_MASK) != 0) - break; - - if (blk_idx > FM_RDS_BLK_IDX_D) { - fmdbg("Block sequence mismatch\n"); - rds->last_blk_idx = -1; - break; - } - - /* Skip checkword (control) byte and copy only data byte */ - idx = array_index_nospec(blk_idx * (FM_RDS_BLK_SIZE - 1), - FM_RX_RDS_INFO_FIELD_MAX - (FM_RDS_BLK_SIZE - 1)); - - memcpy(&rds_fmt.data.groupdatabuff.buff[idx], rds_data, - FM_RDS_BLK_SIZE - 1); - - rds->last_blk_idx = blk_idx; - - /* If completed a whole group then handle it */ - if (blk_idx == FM_RDS_BLK_IDX_D) { - fmdbg("Good block received\n"); - fm_rdsparse_swapbytes(fmdev, &rds_fmt); - - /* - * Extract PI code and store in local cache. - * We need this during AF switch processing. - */ - cur_picode = be16_to_cpu((__force __be16)rds_fmt.data.groupgeneral.pidata); - if (fmdev->rx.stat_info.picode != cur_picode) - fmdev->rx.stat_info.picode = cur_picode; - - fmdbg("picode:%d\n", cur_picode); - - group_idx = (rds_fmt.data.groupgeneral.blk_b[0] >> 3); - fmdbg("(fmdrv):Group:%ld%s\n", group_idx/2, - (group_idx % 2) ? "B" : "A"); - - group_idx = 1 << (rds_fmt.data.groupgeneral.blk_b[0] >> 3); - if (group_idx == FM_RDS_GROUP_TYPE_MASK_0A) { - fm_rx_update_af_cache(fmdev, rds_fmt.data.group0A.af[0]); - fm_rx_update_af_cache(fmdev, rds_fmt.data.group0A.af[1]); - } - } - rds_len -= FM_RDS_BLK_SIZE; - rds_data += FM_RDS_BLK_SIZE; - } - - /* Copy raw rds data to internal rds buffer */ - rds_data = skb->data; - rds_len = skb->len; - - spin_lock_irqsave(&fmdev->rds_buff_lock, flags); - while (rds_len > 0) { - /* - * Fill RDS buffer as per V4L2 specification. - * Store control byte - */ - type = (rds_data[2] & 0x07); - blk_idx = (type <= FM_RDS_BLOCK_C ? type : (type - 1)); - tmpbuf[2] = blk_idx; /* Offset name */ - tmpbuf[2] |= blk_idx << 3; /* Received offset */ - - /* Store data byte */ - tmpbuf[0] = rds_data[0]; - tmpbuf[1] = rds_data[1]; - - memcpy(&rds->buff[rds->wr_idx], &tmpbuf, FM_RDS_BLK_SIZE); - rds->wr_idx = (rds->wr_idx + FM_RDS_BLK_SIZE) % rds->buf_size; - - /* Check for overflow & start over */ - if (rds->wr_idx == rds->rd_idx) { - fmdbg("RDS buffer overflow\n"); - rds->wr_idx = 0; - rds->rd_idx = 0; - break; - } - rds_len -= FM_RDS_BLK_SIZE; - rds_data += FM_RDS_BLK_SIZE; - } - spin_unlock_irqrestore(&fmdev->rds_buff_lock, flags); - - /* Wakeup read queue */ - if (rds->wr_idx != rds->rd_idx) - wake_up_interruptible(&rds->read_queue); - - fm_irq_call_stage(fmdev, FM_RDS_FINISH_IDX); -} - -static void fm_irq_handle_rds_finish(struct fmdev *fmdev) -{ - fm_irq_call_stage(fmdev, FM_HW_TUNE_OP_ENDED_IDX); -} - -static void fm_irq_handle_tune_op_ended(struct fmdev *fmdev) -{ - if (fmdev->irq_info.flag & (FM_FR_EVENT | FM_BL_EVENT) & fmdev-> - irq_info.mask) { - fmdbg("irq: tune ended/bandlimit reached\n"); - if (test_and_clear_bit(FM_AF_SWITCH_INPROGRESS, &fmdev->flag)) { - fmdev->irq_info.stage = FM_AF_JUMP_RD_FREQ_IDX; - } else { - complete(&fmdev->maintask_comp); - fmdev->irq_info.stage = FM_HW_POWER_ENB_IDX; - } - } else - fmdev->irq_info.stage = FM_HW_POWER_ENB_IDX; - - fm_irq_call(fmdev); -} - -static void fm_irq_handle_power_enb(struct fmdev *fmdev) -{ - if (fmdev->irq_info.flag & FM_POW_ENB_EVENT) { - fmdbg("irq: Power Enabled/Disabled\n"); - complete(&fmdev->maintask_comp); - } - - fm_irq_call_stage(fmdev, FM_LOW_RSSI_START_IDX); -} - -static void fm_irq_handle_low_rssi_start(struct fmdev *fmdev) -{ - if ((fmdev->rx.af_mode == FM_RX_RDS_AF_SWITCH_MODE_ON) && - (fmdev->irq_info.flag & FM_LEV_EVENT & fmdev->irq_info.mask) && - (fmdev->rx.freq != FM_UNDEFINED_FREQ) && - (fmdev->rx.stat_info.afcache_size != 0)) { - fmdbg("irq: rssi level has fallen below threshold level\n"); - - /* Disable further low RSSI interrupts */ - fmdev->irq_info.mask &= ~FM_LEV_EVENT; - - fmdev->rx.afjump_idx = 0; - fmdev->rx.freq_before_jump = fmdev->rx.freq; - fmdev->irq_info.stage = FM_AF_JUMP_SETPI_IDX; - } else { - /* Continue next function in interrupt handler table */ - fmdev->irq_info.stage = FM_SEND_INTMSK_CMD_IDX; - } - - fm_irq_call(fmdev); -} - -static void fm_irq_afjump_set_pi(struct fmdev *fmdev) -{ - u16 payload; - - /* Set PI code - must be updated if the AF list is not empty */ - payload = fmdev->rx.stat_info.picode; - if (!fm_send_cmd(fmdev, RDS_PI_SET, REG_WR, &payload, sizeof(payload), NULL)) - fm_irq_timeout_stage(fmdev, FM_AF_JUMP_HANDLE_SETPI_RESP_IDX); -} - -static void fm_irq_handle_set_pi_resp(struct fmdev *fmdev) -{ - fm_irq_common_cmd_resp_helper(fmdev, FM_AF_JUMP_SETPI_MASK_IDX); -} - -/* - * Set PI mask. - * 0xFFFF = Enable PI code matching - * 0x0000 = Disable PI code matching - */ -static void fm_irq_afjump_set_pimask(struct fmdev *fmdev) -{ - u16 payload; - - payload = 0x0000; - if (!fm_send_cmd(fmdev, RDS_PI_MASK_SET, REG_WR, &payload, sizeof(payload), NULL)) - fm_irq_timeout_stage(fmdev, FM_AF_JUMP_HANDLE_SETPI_MASK_RESP_IDX); -} - -static void fm_irq_handle_set_pimask_resp(struct fmdev *fmdev) -{ - fm_irq_common_cmd_resp_helper(fmdev, FM_AF_JUMP_SET_AF_FREQ_IDX); -} - -static void fm_irq_afjump_setfreq(struct fmdev *fmdev) -{ - u16 frq_index; - u16 payload; - - fmdbg("Switch to %d KHz\n", fmdev->rx.stat_info.af_cache[fmdev->rx.afjump_idx]); - frq_index = (fmdev->rx.stat_info.af_cache[fmdev->rx.afjump_idx] - - fmdev->rx.region.bot_freq) / FM_FREQ_MUL; - - payload = frq_index; - if (!fm_send_cmd(fmdev, AF_FREQ_SET, REG_WR, &payload, sizeof(payload), NULL)) - fm_irq_timeout_stage(fmdev, FM_AF_JUMP_HANDLE_SET_AFFREQ_RESP_IDX); -} - -static void fm_irq_handle_setfreq_resp(struct fmdev *fmdev) -{ - fm_irq_common_cmd_resp_helper(fmdev, FM_AF_JUMP_ENABLE_INT_IDX); -} - -static void fm_irq_afjump_enableint(struct fmdev *fmdev) -{ - u16 payload; - - /* Enable FR (tuning operation ended) interrupt */ - payload = FM_FR_EVENT; - if (!fm_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, sizeof(payload), NULL)) - fm_irq_timeout_stage(fmdev, FM_AF_JUMP_ENABLE_INT_RESP_IDX); -} - -static void fm_irq_afjump_enableint_resp(struct fmdev *fmdev) -{ - fm_irq_common_cmd_resp_helper(fmdev, FM_AF_JUMP_START_AFJUMP_IDX); -} - -static void fm_irq_start_afjump(struct fmdev *fmdev) -{ - u16 payload; - - payload = FM_TUNER_AF_JUMP_MODE; - if (!fm_send_cmd(fmdev, TUNER_MODE_SET, REG_WR, &payload, - sizeof(payload), NULL)) - fm_irq_timeout_stage(fmdev, FM_AF_JUMP_HANDLE_START_AFJUMP_RESP_IDX); -} - -static void fm_irq_handle_start_afjump_resp(struct fmdev *fmdev) -{ - struct sk_buff *skb; - - if (check_cmdresp_status(fmdev, &skb)) - return; - - fmdev->irq_info.stage = FM_SEND_FLAG_GETCMD_IDX; - set_bit(FM_AF_SWITCH_INPROGRESS, &fmdev->flag); - clear_bit(FM_INTTASK_RUNNING, &fmdev->flag); -} - -static void fm_irq_afjump_rd_freq(struct fmdev *fmdev) -{ - u16 payload; - - if (!fm_send_cmd(fmdev, FREQ_SET, REG_RD, NULL, sizeof(payload), NULL)) - fm_irq_timeout_stage(fmdev, FM_AF_JUMP_RD_FREQ_RESP_IDX); -} - -static void fm_irq_afjump_rd_freq_resp(struct fmdev *fmdev) -{ - struct sk_buff *skb; - u16 read_freq; - u32 curr_freq, jumped_freq; - - if (check_cmdresp_status(fmdev, &skb)) - return; - - /* Skip header info and copy only response data */ - skb_pull(skb, sizeof(struct fm_event_msg_hdr)); - memcpy(&read_freq, skb->data, sizeof(read_freq)); - read_freq = be16_to_cpu((__force __be16)read_freq); - curr_freq = fmdev->rx.region.bot_freq + ((u32)read_freq * FM_FREQ_MUL); - - jumped_freq = fmdev->rx.stat_info.af_cache[fmdev->rx.afjump_idx]; - - /* If the frequency was changed the jump succeeded */ - if ((curr_freq != fmdev->rx.freq_before_jump) && (curr_freq == jumped_freq)) { - fmdbg("Successfully switched to alternate freq %d\n", curr_freq); - fmdev->rx.freq = curr_freq; - fm_rx_reset_rds_cache(fmdev); - - /* AF feature is on, enable low level RSSI interrupt */ - if (fmdev->rx.af_mode == FM_RX_RDS_AF_SWITCH_MODE_ON) - fmdev->irq_info.mask |= FM_LEV_EVENT; - - fmdev->irq_info.stage = FM_LOW_RSSI_FINISH_IDX; - } else { /* jump to the next freq in the AF list */ - fmdev->rx.afjump_idx++; - - /* If we reached the end of the list - stop searching */ - if (fmdev->rx.afjump_idx >= fmdev->rx.stat_info.afcache_size) { - fmdbg("AF switch processing failed\n"); - fmdev->irq_info.stage = FM_LOW_RSSI_FINISH_IDX; - } else { /* AF List is not over - try next one */ - - fmdbg("Trying next freq in AF cache\n"); - fmdev->irq_info.stage = FM_AF_JUMP_SETPI_IDX; - } - } - fm_irq_call(fmdev); -} - -static void fm_irq_handle_low_rssi_finish(struct fmdev *fmdev) -{ - fm_irq_call_stage(fmdev, FM_SEND_INTMSK_CMD_IDX); -} - -static void fm_irq_send_intmsk_cmd(struct fmdev *fmdev) -{ - u16 payload; - - /* Re-enable FM interrupts */ - payload = fmdev->irq_info.mask; - - if (!fm_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, - sizeof(payload), NULL)) - fm_irq_timeout_stage(fmdev, FM_HANDLE_INTMSK_CMD_RESP_IDX); -} - -static void fm_irq_handle_intmsk_cmd_resp(struct fmdev *fmdev) -{ - struct sk_buff *skb; - - if (check_cmdresp_status(fmdev, &skb)) - return; - /* - * This is last function in interrupt table to be executed. - * So, reset stage index to 0. - */ - fmdev->irq_info.stage = FM_SEND_FLAG_GETCMD_IDX; - - /* Start processing any pending interrupt */ - if (test_and_clear_bit(FM_INTTASK_SCHEDULE_PENDING, &fmdev->flag)) - fmdev->irq_info.handlers[fmdev->irq_info.stage](fmdev); - else - clear_bit(FM_INTTASK_RUNNING, &fmdev->flag); -} - -/* Returns availability of RDS data in internal buffer */ -int fmc_is_rds_data_available(struct fmdev *fmdev, struct file *file, - struct poll_table_struct *pts) -{ - poll_wait(file, &fmdev->rx.rds.read_queue, pts); - if (fmdev->rx.rds.rd_idx != fmdev->rx.rds.wr_idx) - return 0; - - return -EAGAIN; -} - -/* Copies RDS data from internal buffer to user buffer */ -int fmc_transfer_rds_from_internal_buff(struct fmdev *fmdev, struct file *file, - u8 __user *buf, size_t count) -{ - u32 block_count; - u8 tmpbuf[FM_RDS_BLK_SIZE]; - unsigned long flags; - int ret; - - if (fmdev->rx.rds.wr_idx == fmdev->rx.rds.rd_idx) { - if (file->f_flags & O_NONBLOCK) - return -EWOULDBLOCK; - - ret = wait_event_interruptible(fmdev->rx.rds.read_queue, - (fmdev->rx.rds.wr_idx != fmdev->rx.rds.rd_idx)); - if (ret) - return -EINTR; - } - - /* Calculate block count from byte count */ - count /= FM_RDS_BLK_SIZE; - block_count = 0; - ret = 0; - - while (block_count < count) { - spin_lock_irqsave(&fmdev->rds_buff_lock, flags); - - if (fmdev->rx.rds.wr_idx == fmdev->rx.rds.rd_idx) { - spin_unlock_irqrestore(&fmdev->rds_buff_lock, flags); - break; - } - memcpy(tmpbuf, &fmdev->rx.rds.buff[fmdev->rx.rds.rd_idx], - FM_RDS_BLK_SIZE); - fmdev->rx.rds.rd_idx += FM_RDS_BLK_SIZE; - if (fmdev->rx.rds.rd_idx >= fmdev->rx.rds.buf_size) - fmdev->rx.rds.rd_idx = 0; - - spin_unlock_irqrestore(&fmdev->rds_buff_lock, flags); - - if (copy_to_user(buf, tmpbuf, FM_RDS_BLK_SIZE)) - break; - - block_count++; - buf += FM_RDS_BLK_SIZE; - ret += FM_RDS_BLK_SIZE; - } - return ret; -} - -int fmc_set_freq(struct fmdev *fmdev, u32 freq_to_set) -{ - switch (fmdev->curr_fmmode) { - case FM_MODE_RX: - return fm_rx_set_freq(fmdev, freq_to_set); - - case FM_MODE_TX: - return fm_tx_set_freq(fmdev, freq_to_set); - - default: - return -EINVAL; - } -} - -int fmc_get_freq(struct fmdev *fmdev, u32 *cur_tuned_frq) -{ - if (fmdev->rx.freq == FM_UNDEFINED_FREQ) { - fmerr("RX frequency is not set\n"); - return -EPERM; - } - if (cur_tuned_frq == NULL) { - fmerr("Invalid memory\n"); - return -ENOMEM; - } - - switch (fmdev->curr_fmmode) { - case FM_MODE_RX: - *cur_tuned_frq = fmdev->rx.freq; - return 0; - - case FM_MODE_TX: - *cur_tuned_frq = 0; /* TODO : Change this later */ - return 0; - - default: - return -EINVAL; - } - -} - -int fmc_set_region(struct fmdev *fmdev, u8 region_to_set) -{ - switch (fmdev->curr_fmmode) { - case FM_MODE_RX: - return fm_rx_set_region(fmdev, region_to_set); - - case FM_MODE_TX: - return fm_tx_set_region(fmdev, region_to_set); - - default: - return -EINVAL; - } -} - -int fmc_set_mute_mode(struct fmdev *fmdev, u8 mute_mode_toset) -{ - switch (fmdev->curr_fmmode) { - case FM_MODE_RX: - return fm_rx_set_mute_mode(fmdev, mute_mode_toset); - - case FM_MODE_TX: - return fm_tx_set_mute_mode(fmdev, mute_mode_toset); - - default: - return -EINVAL; - } -} - -int fmc_set_stereo_mono(struct fmdev *fmdev, u16 mode) -{ - switch (fmdev->curr_fmmode) { - case FM_MODE_RX: - return fm_rx_set_stereo_mono(fmdev, mode); - - case FM_MODE_TX: - return fm_tx_set_stereo_mono(fmdev, mode); - - default: - return -EINVAL; - } -} - -int fmc_set_rds_mode(struct fmdev *fmdev, u8 rds_en_dis) -{ - switch (fmdev->curr_fmmode) { - case FM_MODE_RX: - return fm_rx_set_rds_mode(fmdev, rds_en_dis); - - case FM_MODE_TX: - return fm_tx_set_rds_mode(fmdev, rds_en_dis); - - default: - return -EINVAL; - } -} - -/* Sends power off command to the chip */ -static int fm_power_down(struct fmdev *fmdev) -{ - u16 payload; - int ret; - - if (!test_bit(FM_CORE_READY, &fmdev->flag)) { - fmerr("FM core is not ready\n"); - return -EPERM; - } - if (fmdev->curr_fmmode == FM_MODE_OFF) { - fmdbg("FM chip is already in OFF state\n"); - return 0; - } - - payload = 0x0; - ret = fmc_send_cmd(fmdev, FM_POWER_MODE, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - return fmc_release(fmdev); -} - -/* Reads init command from FM firmware file and loads to the chip */ -static int fm_download_firmware(struct fmdev *fmdev, const u8 *fw_name) -{ - const struct firmware *fw_entry; - struct bts_header *fw_header; - struct bts_action *action; - struct bts_action_delay *delay; - u8 *fw_data; - int ret, fw_len; - - set_bit(FM_FW_DW_INPROGRESS, &fmdev->flag); - - ret = request_firmware(&fw_entry, fw_name, - &fmdev->radio_dev->dev); - if (ret < 0) { - fmerr("Unable to read firmware(%s) content\n", fw_name); - return ret; - } - fmdbg("Firmware(%s) length : %zu bytes\n", fw_name, fw_entry->size); - - fw_data = (void *)fw_entry->data; - fw_len = fw_entry->size; - - fw_header = (struct bts_header *)fw_data; - if (fw_header->magic != FM_FW_FILE_HEADER_MAGIC) { - fmerr("%s not a legal TI firmware file\n", fw_name); - ret = -EINVAL; - goto rel_fw; - } - fmdbg("FW(%s) magic number : 0x%x\n", fw_name, fw_header->magic); - - /* Skip file header info , we already verified it */ - fw_data += sizeof(struct bts_header); - fw_len -= sizeof(struct bts_header); - - while (fw_data && fw_len > 0) { - action = (struct bts_action *)fw_data; - - switch (action->type) { - case ACTION_SEND_COMMAND: /* Send */ - ret = fmc_send_cmd(fmdev, 0, 0, action->data, - action->size, NULL, NULL); - if (ret) - goto rel_fw; - - break; - - case ACTION_DELAY: /* Delay */ - delay = (struct bts_action_delay *)action->data; - mdelay(delay->msec); - break; - } - - fw_data += (sizeof(struct bts_action) + (action->size)); - fw_len -= (sizeof(struct bts_action) + (action->size)); - } - fmdbg("Transferred only %d of %d bytes of the firmware to chip\n", - fw_entry->size - fw_len, fw_entry->size); -rel_fw: - release_firmware(fw_entry); - clear_bit(FM_FW_DW_INPROGRESS, &fmdev->flag); - - return ret; -} - -/* Loads default RX configuration to the chip */ -static int load_default_rx_configuration(struct fmdev *fmdev) -{ - int ret; - - ret = fm_rx_set_volume(fmdev, FM_DEFAULT_RX_VOLUME); - if (ret < 0) - return ret; - - return fm_rx_set_rssi_threshold(fmdev, FM_DEFAULT_RSSI_THRESHOLD); -} - -/* Does FM power on sequence */ -static int fm_power_up(struct fmdev *fmdev, u8 mode) -{ - u16 payload; - __be16 asic_id = 0, asic_ver = 0; - int resp_len, ret; - u8 fw_name[50]; - - if (mode >= FM_MODE_ENTRY_MAX) { - fmerr("Invalid firmware download option\n"); - return -EINVAL; - } - - /* - * Initialize FM common module. FM GPIO toggling is - * taken care in Shared Transport driver. - */ - ret = fmc_prepare(fmdev); - if (ret < 0) { - fmerr("Unable to prepare FM Common\n"); - return ret; - } - - payload = FM_ENABLE; - if (fmc_send_cmd(fmdev, FM_POWER_MODE, REG_WR, &payload, - sizeof(payload), NULL, NULL)) - goto rel; - - /* Allow the chip to settle down in Channel-8 mode */ - msleep(20); - - if (fmc_send_cmd(fmdev, ASIC_ID_GET, REG_RD, NULL, - sizeof(asic_id), &asic_id, &resp_len)) - goto rel; - - if (fmc_send_cmd(fmdev, ASIC_VER_GET, REG_RD, NULL, - sizeof(asic_ver), &asic_ver, &resp_len)) - goto rel; - - fmdbg("ASIC ID: 0x%x , ASIC Version: %d\n", - be16_to_cpu(asic_id), be16_to_cpu(asic_ver)); - - sprintf(fw_name, "%s_%x.%d.bts", FM_FMC_FW_FILE_START, - be16_to_cpu(asic_id), be16_to_cpu(asic_ver)); - - ret = fm_download_firmware(fmdev, fw_name); - if (ret < 0) { - fmdbg("Failed to download firmware file %s\n", fw_name); - goto rel; - } - sprintf(fw_name, "%s_%x.%d.bts", (mode == FM_MODE_RX) ? - FM_RX_FW_FILE_START : FM_TX_FW_FILE_START, - be16_to_cpu(asic_id), be16_to_cpu(asic_ver)); - - ret = fm_download_firmware(fmdev, fw_name); - if (ret < 0) { - fmdbg("Failed to download firmware file %s\n", fw_name); - goto rel; - } else - return ret; -rel: - return fmc_release(fmdev); -} - -/* Set FM Modes(TX, RX, OFF) */ -int fmc_set_mode(struct fmdev *fmdev, u8 fm_mode) -{ - int ret = 0; - - if (fm_mode >= FM_MODE_ENTRY_MAX) { - fmerr("Invalid FM mode\n"); - return -EINVAL; - } - if (fmdev->curr_fmmode == fm_mode) { - fmdbg("Already fm is in mode(%d)\n", fm_mode); - return ret; - } - - switch (fm_mode) { - case FM_MODE_OFF: /* OFF Mode */ - ret = fm_power_down(fmdev); - if (ret < 0) { - fmerr("Failed to set OFF mode\n"); - return ret; - } - break; - - case FM_MODE_TX: /* TX Mode */ - case FM_MODE_RX: /* RX Mode */ - /* Power down before switching to TX or RX mode */ - if (fmdev->curr_fmmode != FM_MODE_OFF) { - ret = fm_power_down(fmdev); - if (ret < 0) { - fmerr("Failed to set OFF mode\n"); - return ret; - } - msleep(30); - } - ret = fm_power_up(fmdev, fm_mode); - if (ret < 0) { - fmerr("Failed to load firmware\n"); - return ret; - } - } - fmdev->curr_fmmode = fm_mode; - - /* Set default configuration */ - if (fmdev->curr_fmmode == FM_MODE_RX) { - fmdbg("Loading default rx configuration..\n"); - ret = load_default_rx_configuration(fmdev); - if (ret < 0) - fmerr("Failed to load default values\n"); - } - - return ret; -} - -/* Returns current FM mode (TX, RX, OFF) */ -int fmc_get_mode(struct fmdev *fmdev, u8 *fmmode) -{ - if (!test_bit(FM_CORE_READY, &fmdev->flag)) { - fmerr("FM core is not ready\n"); - return -EPERM; - } - if (fmmode == NULL) { - fmerr("Invalid memory\n"); - return -ENOMEM; - } - - *fmmode = fmdev->curr_fmmode; - return 0; -} - -/* Called by ST layer when FM packet is available */ -static long fm_st_receive(void *arg, struct sk_buff *skb) -{ - struct fmdev *fmdev; - - fmdev = arg; - - if (skb == NULL) { - fmerr("Invalid SKB received from ST\n"); - return -EFAULT; - } - - if (skb->cb[0] != FM_PKT_LOGICAL_CHAN_NUMBER) { - fmerr("Received SKB (%p) is not FM Channel 8 pkt\n", skb); - return -EINVAL; - } - - memcpy(skb_push(skb, 1), &skb->cb[0], 1); - skb_queue_tail(&fmdev->rx_q, skb); - queue_work(system_bh_wq, &fmdev->rx_bh_work); - - return 0; -} - -/* - * Called by ST layer to indicate protocol registration completion - * status. - */ -static void fm_st_reg_comp_cb(void *arg, int data) -{ - struct fmdev *fmdev; - - fmdev = (struct fmdev *)arg; - fmdev->streg_cbdata = data; - complete(&wait_for_fmdrv_reg_comp); -} - -/* - * This function will be called from FM V4L2 open function. - * Register with ST driver and initialize driver data. - */ -int fmc_prepare(struct fmdev *fmdev) -{ - static struct st_proto_s fm_st_proto; - int ret; - - if (test_bit(FM_CORE_READY, &fmdev->flag)) { - fmdbg("FM Core is already up\n"); - return 0; - } - - memset(&fm_st_proto, 0, sizeof(fm_st_proto)); - fm_st_proto.recv = fm_st_receive; - fm_st_proto.match_packet = NULL; - fm_st_proto.reg_complete_cb = fm_st_reg_comp_cb; - fm_st_proto.write = NULL; /* TI ST driver will fill write pointer */ - fm_st_proto.priv_data = fmdev; - fm_st_proto.chnl_id = 0x08; - fm_st_proto.max_frame_size = 0xff; - fm_st_proto.hdr_len = 1; - fm_st_proto.offset_len_in_hdr = 0; - fm_st_proto.len_size = 1; - fm_st_proto.reserve = 1; - - ret = st_register(&fm_st_proto); - if (ret == -EINPROGRESS) { - init_completion(&wait_for_fmdrv_reg_comp); - fmdev->streg_cbdata = -EINPROGRESS; - fmdbg("%s waiting for ST reg completion signal\n", __func__); - - if (!wait_for_completion_timeout(&wait_for_fmdrv_reg_comp, - FM_ST_REG_TIMEOUT)) { - fmerr("Timeout(%d sec), didn't get reg completion signal from ST\n", - jiffies_to_msecs(FM_ST_REG_TIMEOUT) / 1000); - return -ETIMEDOUT; - } - if (fmdev->streg_cbdata != 0) { - fmerr("ST reg comp CB called with error status %d\n", - fmdev->streg_cbdata); - return -EAGAIN; - } - - ret = 0; - } else if (ret < 0) { - fmerr("st_register failed %d\n", ret); - return -EAGAIN; - } - - if (fm_st_proto.write != NULL) { - g_st_write = fm_st_proto.write; - } else { - fmerr("Failed to get ST write func pointer\n"); - ret = st_unregister(&fm_st_proto); - if (ret < 0) - fmerr("st_unregister failed %d\n", ret); - return -EAGAIN; - } - - spin_lock_init(&fmdev->rds_buff_lock); - spin_lock_init(&fmdev->resp_skb_lock); - - /* Initialize TX queue and TX bh work */ - skb_queue_head_init(&fmdev->tx_q); - INIT_WORK(&fmdev->tx_bh_work, send_bh_work); - - /* Initialize RX Queue and RX bh work */ - skb_queue_head_init(&fmdev->rx_q); - INIT_WORK(&fmdev->rx_bh_work, recv_bh_work); - - fmdev->irq_info.stage = 0; - atomic_set(&fmdev->tx_cnt, 1); - fmdev->resp_comp = NULL; - - timer_setup(&fmdev->irq_info.timer, int_timeout_handler, 0); - /*TODO: add FM_STIC_EVENT later */ - fmdev->irq_info.mask = FM_MAL_EVENT; - - /* Region info */ - fmdev->rx.region = region_configs[default_radio_region]; - - fmdev->rx.mute_mode = FM_MUTE_OFF; - fmdev->rx.rf_depend_mute = FM_RX_RF_DEPENDENT_MUTE_OFF; - fmdev->rx.rds.flag = FM_RDS_DISABLE; - fmdev->rx.freq = FM_UNDEFINED_FREQ; - fmdev->rx.rds_mode = FM_RDS_SYSTEM_RDS; - fmdev->rx.af_mode = FM_RX_RDS_AF_SWITCH_MODE_OFF; - fmdev->irq_info.retry = 0; - - fm_rx_reset_rds_cache(fmdev); - init_waitqueue_head(&fmdev->rx.rds.read_queue); - - fm_rx_reset_station_info(fmdev); - set_bit(FM_CORE_READY, &fmdev->flag); - - return ret; -} - -/* - * This function will be called from FM V4L2 release function. - * Unregister from ST driver. - */ -int fmc_release(struct fmdev *fmdev) -{ - static struct st_proto_s fm_st_proto; - int ret; - - if (!test_bit(FM_CORE_READY, &fmdev->flag)) { - fmdbg("FM Core is already down\n"); - return 0; - } - /* Service pending read */ - wake_up_interruptible(&fmdev->rx.rds.read_queue); - - 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); - - fmdev->resp_comp = NULL; - fmdev->rx.freq = 0; - - memset(&fm_st_proto, 0, sizeof(fm_st_proto)); - fm_st_proto.chnl_id = 0x08; - - ret = st_unregister(&fm_st_proto); - - if (ret < 0) - fmerr("Failed to de-register FM from ST %d\n", ret); - else - fmdbg("Successfully unregistered from ST\n"); - - clear_bit(FM_CORE_READY, &fmdev->flag); - return ret; -} - -/* - * Module init function. Ask FM V4L module to register video device. - * Allocate memory for FM driver context and RX RDS buffer. - */ -static int __init fm_drv_init(void) -{ - struct fmdev *fmdev = NULL; - int ret = -ENOMEM; - - fmdbg("FM driver version %s\n", FM_DRV_VERSION); - - fmdev = kzalloc(sizeof(struct fmdev), GFP_KERNEL); - if (NULL == fmdev) { - fmerr("Can't allocate operation structure memory\n"); - return ret; - } - fmdev->rx.rds.buf_size = default_rds_buf * FM_RDS_BLK_SIZE; - fmdev->rx.rds.buff = kzalloc(fmdev->rx.rds.buf_size, GFP_KERNEL); - if (NULL == fmdev->rx.rds.buff) { - fmerr("Can't allocate rds ring buffer\n"); - goto rel_dev; - } - - ret = fm_v4l2_init_video_device(fmdev, radio_nr); - if (ret < 0) - goto rel_rdsbuf; - - fmdev->irq_info.handlers = int_handler_table; - fmdev->curr_fmmode = FM_MODE_OFF; - fmdev->tx_data.pwr_lvl = FM_PWR_LVL_DEF; - fmdev->tx_data.preemph = FM_TX_PREEMPH_50US; - return ret; - -rel_rdsbuf: - kfree(fmdev->rx.rds.buff); -rel_dev: - kfree(fmdev); - - return ret; -} - -/* Module exit function. Ask FM V4L module to unregister video device */ -static void __exit fm_drv_exit(void) -{ - struct fmdev *fmdev = NULL; - - fmdev = fm_v4l2_deinit_video_device(); - if (fmdev != NULL) { - kfree(fmdev->rx.rds.buff); - kfree(fmdev); - } -} - -module_init(fm_drv_init); -module_exit(fm_drv_exit); - -/* ------------- Module Info ------------- */ -MODULE_AUTHOR("Manjunatha Halli <manjunatha_halli@ti.com>"); -MODULE_DESCRIPTION("FM Driver for TI's Connectivity chip. " FM_DRV_VERSION); -MODULE_VERSION(FM_DRV_VERSION); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/radio/wl128x/fmdrv_common.h b/drivers/media/radio/wl128x/fmdrv_common.h deleted file mode 100644 index 6a287eadae75..000000000000 --- a/drivers/media/radio/wl128x/fmdrv_common.h +++ /dev/null @@ -1,389 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * FM Driver for Connectivity chip of Texas Instruments. - * FM Common module header file - * - * Copyright (C) 2011 Texas Instruments - */ - -#ifndef _FMDRV_COMMON_H -#define _FMDRV_COMMON_H - -#define FM_ST_REG_TIMEOUT msecs_to_jiffies(6000) /* 6 sec */ -#define FM_PKT_LOGICAL_CHAN_NUMBER 0x08 /* Logical channel 8 */ - -#define REG_RD 0x1 -#define REG_WR 0x0 - -struct fm_reg_table { - u8 opcode; - u8 type; - u8 *name; -}; - -#define STEREO_GET 0 -#define RSSI_LVL_GET 1 -#define IF_COUNT_GET 2 -#define FLAG_GET 3 -#define RDS_SYNC_GET 4 -#define RDS_DATA_GET 5 -#define FREQ_SET 10 -#define AF_FREQ_SET 11 -#define MOST_MODE_SET 12 -#define MOST_BLEND_SET 13 -#define DEMPH_MODE_SET 14 -#define SEARCH_LVL_SET 15 -#define BAND_SET 16 -#define MUTE_STATUS_SET 17 -#define RDS_PAUSE_LVL_SET 18 -#define RDS_PAUSE_DUR_SET 19 -#define RDS_MEM_SET 20 -#define RDS_BLK_B_SET 21 -#define RDS_MSK_B_SET 22 -#define RDS_PI_MASK_SET 23 -#define RDS_PI_SET 24 -#define RDS_SYSTEM_SET 25 -#define INT_MASK_SET 26 -#define SEARCH_DIR_SET 27 -#define VOLUME_SET 28 -#define AUDIO_ENABLE_SET 29 -#define PCM_MODE_SET 30 -#define I2S_MODE_CONFIG_SET 31 -#define POWER_SET 32 -#define INTX_CONFIG_SET 33 -#define PULL_EN_SET 34 -#define HILO_SET 35 -#define SWITCH2FREF 36 -#define FREQ_DRIFT_REPORT 37 - -#define PCE_GET 40 -#define FIRM_VER_GET 41 -#define ASIC_VER_GET 42 -#define ASIC_ID_GET 43 -#define MAN_ID_GET 44 -#define TUNER_MODE_SET 45 -#define STOP_SEARCH 46 -#define RDS_CNTRL_SET 47 - -#define WRITE_HARDWARE_REG 100 -#define CODE_DOWNLOAD 101 -#define RESET 102 - -#define FM_POWER_MODE 254 -#define FM_INTERRUPT 255 - -/* Transmitter API */ - -#define CHANL_SET 55 -#define CHANL_BW_SET 56 -#define REF_SET 57 -#define POWER_ENB_SET 90 -#define POWER_ATT_SET 58 -#define POWER_LEV_SET 59 -#define AUDIO_DEV_SET 60 -#define PILOT_DEV_SET 61 -#define RDS_DEV_SET 62 -#define TX_BAND_SET 65 -#define PUPD_SET 91 -#define AUDIO_IO_SET 63 -#define PREMPH_SET 64 -#define MONO_SET 66 -#define MUTE 92 -#define MPX_LMT_ENABLE 67 -#define PI_SET 93 -#define ECC_SET 69 -#define PTY 70 -#define AF 71 -#define DISPLAY_MODE 74 -#define RDS_REP_SET 77 -#define RDS_CONFIG_DATA_SET 98 -#define RDS_DATA_SET 99 -#define RDS_DATA_ENB 94 -#define TA_SET 78 -#define TP_SET 79 -#define DI_SET 80 -#define MS_SET 81 -#define PS_SCROLL_SPEED 82 -#define TX_AUDIO_LEVEL_TEST 96 -#define TX_AUDIO_LEVEL_TEST_THRESHOLD 73 -#define TX_AUDIO_INPUT_LEVEL_RANGE_SET 54 -#define RX_ANTENNA_SELECT 87 -#define I2C_DEV_ADDR_SET 86 -#define REF_ERR_CALIB_PARAM_SET 88 -#define REF_ERR_CALIB_PERIODICITY_SET 89 -#define SOC_INT_TRIGGER 52 -#define SOC_AUDIO_PATH_SET 83 -#define SOC_PCMI_OVERRIDE 84 -#define SOC_I2S_OVERRIDE 85 -#define RSSI_BLOCK_SCAN_FREQ_SET 95 -#define RSSI_BLOCK_SCAN_START 97 -#define RSSI_BLOCK_SCAN_DATA_GET 5 -#define READ_FMANT_TUNE_VALUE 104 - -/* SKB helpers */ -struct fm_skb_cb { - __u8 fm_op; - struct completion *completion; -}; - -#define fm_cb(skb) ((struct fm_skb_cb *)(skb->cb)) - -/* FM Channel-8 command message format */ -struct fm_cmd_msg_hdr { - __u8 hdr; /* Logical Channel-8 */ - __u8 len; /* Number of bytes follows */ - __u8 op; /* FM Opcode */ - __u8 rd_wr; /* Read/Write command */ - __u8 dlen; /* Length of payload */ -} __attribute__ ((packed)); - -#define FM_CMD_MSG_HDR_SIZE 5 /* sizeof(struct fm_cmd_msg_hdr) */ - -/* FM Channel-8 event messgage format */ -struct fm_event_msg_hdr { - __u8 header; /* Logical Channel-8 */ - __u8 len; /* Number of bytes follows */ - __u8 status; /* Event status */ - __u8 num_fm_hci_cmds; /* Number of pkts the host allowed to send */ - __u8 op; /* FM Opcode */ - __u8 rd_wr; /* Read/Write command */ - __u8 dlen; /* Length of payload */ -} __attribute__ ((packed)); - -#define FM_EVT_MSG_HDR_SIZE 7 /* sizeof(struct fm_event_msg_hdr) */ - -/* TI's magic number in firmware file */ -#define FM_FW_FILE_HEADER_MAGIC 0x42535442 - -#define FM_ENABLE 1 -#define FM_DISABLE 0 - -/* FLAG_GET register bits */ -#define FM_FR_EVENT BIT(0) -#define FM_BL_EVENT BIT(1) -#define FM_RDS_EVENT BIT(2) -#define FM_BBLK_EVENT BIT(3) -#define FM_LSYNC_EVENT BIT(4) -#define FM_LEV_EVENT BIT(5) -#define FM_IFFR_EVENT BIT(6) -#define FM_PI_EVENT BIT(7) -#define FM_PD_EVENT BIT(8) -#define FM_STIC_EVENT BIT(9) -#define FM_MAL_EVENT BIT(10) -#define FM_POW_ENB_EVENT BIT(11) - -/* - * Firmware files of FM. ASIC ID and ASIC version will be appened to this, - * later. - */ -#define FM_FMC_FW_FILE_START ("fmc_ch8") -#define FM_RX_FW_FILE_START ("fm_rx_ch8") -#define FM_TX_FW_FILE_START ("fm_tx_ch8") - -#define FM_UNDEFINED_FREQ 0xFFFFFFFF - -/* Band types */ -#define FM_BAND_EUROPE_US 0 -#define FM_BAND_JAPAN 1 - -/* Seek directions */ -#define FM_SEARCH_DIRECTION_DOWN 0 -#define FM_SEARCH_DIRECTION_UP 1 - -/* Tunner modes */ -#define FM_TUNER_STOP_SEARCH_MODE 0 -#define FM_TUNER_PRESET_MODE 1 -#define FM_TUNER_AUTONOMOUS_SEARCH_MODE 2 -#define FM_TUNER_AF_JUMP_MODE 3 - -/* Min and Max volume */ -#define FM_RX_VOLUME_MIN 0 -#define FM_RX_VOLUME_MAX 70 - -/* Volume gain step */ -#define FM_RX_VOLUME_GAIN_STEP 0x370 - -/* Mute modes */ -#define FM_MUTE_ON 0 -#define FM_MUTE_OFF 1 -#define FM_MUTE_ATTENUATE 2 - -#define FM_RX_UNMUTE_MODE 0x00 -#define FM_RX_RF_DEP_MODE 0x01 -#define FM_RX_AC_MUTE_MODE 0x02 -#define FM_RX_HARD_MUTE_LEFT_MODE 0x04 -#define FM_RX_HARD_MUTE_RIGHT_MODE 0x08 -#define FM_RX_SOFT_MUTE_FORCE_MODE 0x10 - -/* RF dependent mute mode */ -#define FM_RX_RF_DEPENDENT_MUTE_ON 1 -#define FM_RX_RF_DEPENDENT_MUTE_OFF 0 - -/* RSSI threshold min and max */ -#define FM_RX_RSSI_THRESHOLD_MIN -128 -#define FM_RX_RSSI_THRESHOLD_MAX 127 - -/* Stereo/Mono mode */ -#define FM_STEREO_MODE 0 -#define FM_MONO_MODE 1 -#define FM_STEREO_SOFT_BLEND 1 - -/* FM RX De-emphasis filter modes */ -#define FM_RX_EMPHASIS_FILTER_50_USEC 0 -#define FM_RX_EMPHASIS_FILTER_75_USEC 1 - -/* FM RDS modes */ -#define FM_RDS_DISABLE 0 -#define FM_RDS_ENABLE 1 - -#define FM_NO_PI_CODE 0 - -/* FM and RX RDS block enable/disable */ -#define FM_RX_PWR_SET_FM_ON_RDS_OFF 0x1 -#define FM_RX_PWR_SET_FM_AND_RDS_BLK_ON 0x3 -#define FM_RX_PWR_SET_FM_AND_RDS_BLK_OFF 0x0 - -/* RX RDS */ -#define FM_RX_RDS_FLUSH_FIFO 0x1 -#define FM_RX_RDS_FIFO_THRESHOLD 64 /* tuples */ -#define FM_RDS_BLK_SIZE 3 /* 3 bytes */ - -/* RDS block types */ -#define FM_RDS_BLOCK_A 0 -#define FM_RDS_BLOCK_B 1 -#define FM_RDS_BLOCK_C 2 -#define FM_RDS_BLOCK_Ctag 3 -#define FM_RDS_BLOCK_D 4 -#define FM_RDS_BLOCK_E 5 - -#define FM_RDS_BLK_IDX_A 0 -#define FM_RDS_BLK_IDX_B 1 -#define FM_RDS_BLK_IDX_C 2 -#define FM_RDS_BLK_IDX_D 3 -#define FM_RDS_BLK_IDX_UNKNOWN 0xF0 - -#define FM_RDS_STATUS_ERR_MASK 0x18 - -/* - * Represents an RDS group type & version. - * There are 15 groups, each group has 2 versions: A and B. - */ -#define FM_RDS_GROUP_TYPE_MASK_0A BIT(0) -#define FM_RDS_GROUP_TYPE_MASK_0B BIT(1) -#define FM_RDS_GROUP_TYPE_MASK_1A BIT(2) -#define FM_RDS_GROUP_TYPE_MASK_1B BIT(3) -#define FM_RDS_GROUP_TYPE_MASK_2A BIT(4) -#define FM_RDS_GROUP_TYPE_MASK_2B BIT(5) -#define FM_RDS_GROUP_TYPE_MASK_3A BIT(6) -#define FM_RDS_GROUP_TYPE_MASK_3B BIT(7) -#define FM_RDS_GROUP_TYPE_MASK_4A BIT(8) -#define FM_RDS_GROUP_TYPE_MASK_4B BIT(9) -#define FM_RDS_GROUP_TYPE_MASK_5A BIT(10) -#define FM_RDS_GROUP_TYPE_MASK_5B BIT(11) -#define FM_RDS_GROUP_TYPE_MASK_6A BIT(12) -#define FM_RDS_GROUP_TYPE_MASK_6B BIT(13) -#define FM_RDS_GROUP_TYPE_MASK_7A BIT(14) -#define FM_RDS_GROUP_TYPE_MASK_7B BIT(15) -#define FM_RDS_GROUP_TYPE_MASK_8A BIT(16) -#define FM_RDS_GROUP_TYPE_MASK_8B BIT(17) -#define FM_RDS_GROUP_TYPE_MASK_9A BIT(18) -#define FM_RDS_GROUP_TYPE_MASK_9B BIT(19) -#define FM_RDS_GROUP_TYPE_MASK_10A BIT(20) -#define FM_RDS_GROUP_TYPE_MASK_10B BIT(21) -#define FM_RDS_GROUP_TYPE_MASK_11A BIT(22) -#define FM_RDS_GROUP_TYPE_MASK_11B BIT(23) -#define FM_RDS_GROUP_TYPE_MASK_12A BIT(24) -#define FM_RDS_GROUP_TYPE_MASK_12B BIT(25) -#define FM_RDS_GROUP_TYPE_MASK_13A BIT(26) -#define FM_RDS_GROUP_TYPE_MASK_13B BIT(27) -#define FM_RDS_GROUP_TYPE_MASK_14A BIT(28) -#define FM_RDS_GROUP_TYPE_MASK_14B BIT(29) -#define FM_RDS_GROUP_TYPE_MASK_15A BIT(30) -#define FM_RDS_GROUP_TYPE_MASK_15B BIT(31) - -/* RX Alternate Frequency info */ -#define FM_RDS_MIN_AF 1 -#define FM_RDS_MAX_AF 204 -#define FM_RDS_MAX_AF_JAPAN 140 -#define FM_RDS_1_AF_FOLLOWS 225 -#define FM_RDS_25_AF_FOLLOWS 249 - -/* RDS system type (RDS/RBDS) */ -#define FM_RDS_SYSTEM_RDS 0 -#define FM_RDS_SYSTEM_RBDS 1 - -/* AF on/off */ -#define FM_RX_RDS_AF_SWITCH_MODE_ON 1 -#define FM_RX_RDS_AF_SWITCH_MODE_OFF 0 - -/* Retry count when interrupt process goes wrong */ -#define FM_IRQ_TIMEOUT_RETRY_MAX 5 /* 5 times */ - -/* Audio IO set values */ -#define FM_RX_AUDIO_ENABLE_I2S 0x01 -#define FM_RX_AUDIO_ENABLE_ANALOG 0x02 -#define FM_RX_AUDIO_ENABLE_I2S_AND_ANALOG 0x03 -#define FM_RX_AUDIO_ENABLE_DISABLE 0x00 - -/* HI/LO set values */ -#define FM_RX_IFFREQ_TO_HI_SIDE 0x0 -#define FM_RX_IFFREQ_TO_LO_SIDE 0x1 -#define FM_RX_IFFREQ_HILO_AUTOMATIC 0x2 - -/* - * Default RX mode configuration. Chip will be configured - * with this default values after loading RX firmware. - */ -#define FM_DEFAULT_RX_VOLUME 10 -#define FM_DEFAULT_RSSI_THRESHOLD 3 - -/* Range for TX power level in units for dB/uV */ -#define FM_PWR_LVL_LOW 91 -#define FM_PWR_LVL_HIGH 122 - -/* Chip specific default TX power level value */ -#define FM_PWR_LVL_DEF 4 - -/* FM TX Pre-emphasis filter values */ -#define FM_TX_PREEMPH_OFF 1 -#define FM_TX_PREEMPH_50US 0 -#define FM_TX_PREEMPH_75US 2 - -/* FM TX antenna impedance values */ -#define FM_TX_ANT_IMP_50 0 -#define FM_TX_ANT_IMP_200 1 -#define FM_TX_ANT_IMP_500 2 - -/* Functions exported by FM common sub-module */ -int fmc_prepare(struct fmdev *); -int fmc_release(struct fmdev *); - -void fmc_update_region_info(struct fmdev *, u8); -int fmc_send_cmd(struct fmdev *, u8, u16, - void *, unsigned int, void *, int *); -int fmc_is_rds_data_available(struct fmdev *, struct file *, - struct poll_table_struct *); -int fmc_transfer_rds_from_internal_buff(struct fmdev *, struct file *, - u8 __user *, size_t); - -int fmc_set_freq(struct fmdev *, u32); -int fmc_set_mode(struct fmdev *, u8); -int fmc_set_region(struct fmdev *, u8); -int fmc_set_mute_mode(struct fmdev *, u8); -int fmc_set_stereo_mono(struct fmdev *, u16); -int fmc_set_rds_mode(struct fmdev *, u8); - -int fmc_get_freq(struct fmdev *, u32 *); -int fmc_get_region(struct fmdev *, u8 *); -int fmc_get_mode(struct fmdev *, u8 *); - -/* - * channel spacing - */ -#define FM_CHANNEL_SPACING_50KHZ 1 -#define FM_CHANNEL_SPACING_100KHZ 2 -#define FM_CHANNEL_SPACING_200KHZ 4 -#define FM_FREQ_MUL 50 - -#endif - diff --git a/drivers/media/radio/wl128x/fmdrv_rx.c b/drivers/media/radio/wl128x/fmdrv_rx.c deleted file mode 100644 index 419cf2e03bcf..000000000000 --- a/drivers/media/radio/wl128x/fmdrv_rx.c +++ /dev/null @@ -1,820 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * FM Driver for Connectivity chip of Texas Instruments. - * This sub-module of FM driver implements FM RX functionality. - * - * Copyright (C) 2011 Texas Instruments - * Author: Raja Mani <raja_mani@ti.com> - * Author: Manjunatha Halli <manjunatha_halli@ti.com> - */ - -#include "fmdrv.h" -#include "fmdrv_common.h" -#include "fmdrv_rx.h" - -void fm_rx_reset_rds_cache(struct fmdev *fmdev) -{ - fmdev->rx.rds.flag = FM_RDS_DISABLE; - fmdev->rx.rds.last_blk_idx = 0; - fmdev->rx.rds.wr_idx = 0; - fmdev->rx.rds.rd_idx = 0; - - if (fmdev->rx.af_mode == FM_RX_RDS_AF_SWITCH_MODE_ON) - fmdev->irq_info.mask |= FM_LEV_EVENT; -} - -void fm_rx_reset_station_info(struct fmdev *fmdev) -{ - fmdev->rx.stat_info.picode = FM_NO_PI_CODE; - fmdev->rx.stat_info.afcache_size = 0; - fmdev->rx.stat_info.af_list_max = 0; -} - -int fm_rx_set_freq(struct fmdev *fmdev, u32 freq) -{ - unsigned long timeleft; - u16 payload, curr_frq, intr_flag; - u32 curr_frq_in_khz; - u32 resp_len; - int ret; - - if (freq < fmdev->rx.region.bot_freq || freq > fmdev->rx.region.top_freq) { - fmerr("Invalid frequency %d\n", freq); - return -EINVAL; - } - - /* Set audio enable */ - payload = FM_RX_AUDIO_ENABLE_I2S_AND_ANALOG; - - ret = fmc_send_cmd(fmdev, AUDIO_ENABLE_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - /* Set hilo to automatic selection */ - payload = FM_RX_IFFREQ_HILO_AUTOMATIC; - ret = fmc_send_cmd(fmdev, HILO_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - /* Calculate frequency index and set*/ - payload = (freq - fmdev->rx.region.bot_freq) / FM_FREQ_MUL; - - ret = fmc_send_cmd(fmdev, FREQ_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - /* Read flags - just to clear any pending interrupts if we had */ - ret = fmc_send_cmd(fmdev, FLAG_GET, REG_RD, NULL, 2, NULL, NULL); - if (ret < 0) - return ret; - - /* Enable FR, BL interrupts */ - intr_flag = fmdev->irq_info.mask; - fmdev->irq_info.mask = (FM_FR_EVENT | FM_BL_EVENT); - payload = fmdev->irq_info.mask; - ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - /* Start tune */ - payload = FM_TUNER_PRESET_MODE; - ret = fmc_send_cmd(fmdev, TUNER_MODE_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - goto exit; - - /* Wait for tune ended interrupt */ - init_completion(&fmdev->maintask_comp); - timeleft = wait_for_completion_timeout(&fmdev->maintask_comp, - FM_DRV_TX_TIMEOUT); - if (!timeleft) { - fmerr("Timeout(%d sec),didn't get tune ended int\n", - jiffies_to_msecs(FM_DRV_TX_TIMEOUT) / 1000); - ret = -ETIMEDOUT; - goto exit; - } - - /* Read freq back to confirm */ - ret = fmc_send_cmd(fmdev, FREQ_SET, REG_RD, NULL, 2, &curr_frq, &resp_len); - if (ret < 0) - goto exit; - - curr_frq = be16_to_cpu((__force __be16)curr_frq); - curr_frq_in_khz = (fmdev->rx.region.bot_freq + ((u32)curr_frq * FM_FREQ_MUL)); - - if (curr_frq_in_khz != freq) { - pr_info("Frequency is set to (%d) but requested freq is (%d)\n", - curr_frq_in_khz, freq); - } - - /* Update local cache */ - fmdev->rx.freq = curr_frq_in_khz; -exit: - /* Re-enable default FM interrupts */ - fmdev->irq_info.mask = intr_flag; - payload = fmdev->irq_info.mask; - ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - /* Reset RDS cache and current station pointers */ - fm_rx_reset_rds_cache(fmdev); - fm_rx_reset_station_info(fmdev); - - return ret; -} - -static int fm_rx_set_channel_spacing(struct fmdev *fmdev, u32 spacing) -{ - u16 payload; - int ret; - - if (spacing > 0 && spacing <= 50000) - spacing = FM_CHANNEL_SPACING_50KHZ; - else if (spacing > 50000 && spacing <= 100000) - spacing = FM_CHANNEL_SPACING_100KHZ; - else - spacing = FM_CHANNEL_SPACING_200KHZ; - - /* set channel spacing */ - payload = spacing; - ret = fmc_send_cmd(fmdev, CHANL_BW_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - fmdev->rx.region.chanl_space = spacing * FM_FREQ_MUL; - - return ret; -} - -int fm_rx_seek(struct fmdev *fmdev, u32 seek_upward, - u32 wrap_around, u32 spacing) -{ - u32 resp_len; - u16 curr_frq, next_frq, last_frq; - u16 payload, int_reason, intr_flag; - u16 offset, space_idx; - unsigned long timeleft; - int ret; - - /* Set channel spacing */ - ret = fm_rx_set_channel_spacing(fmdev, spacing); - if (ret < 0) { - fmerr("Failed to set channel spacing\n"); - return ret; - } - - /* Read the current frequency from chip */ - ret = fmc_send_cmd(fmdev, FREQ_SET, REG_RD, NULL, - sizeof(curr_frq), &curr_frq, &resp_len); - if (ret < 0) - return ret; - - curr_frq = be16_to_cpu((__force __be16)curr_frq); - last_frq = (fmdev->rx.region.top_freq - fmdev->rx.region.bot_freq) / FM_FREQ_MUL; - - /* Check the offset in order to be aligned to the channel spacing*/ - space_idx = fmdev->rx.region.chanl_space / FM_FREQ_MUL; - offset = curr_frq % space_idx; - - next_frq = seek_upward ? curr_frq + space_idx /* Seek Up */ : - curr_frq - space_idx /* Seek Down */ ; - - /* - * Add or subtract offset in order to stay aligned to the channel - * spacing. - */ - if ((short)next_frq < 0) - next_frq = last_frq - offset; - else if (next_frq > last_frq) - next_frq = 0 + offset; - -again: - /* Set calculated next frequency to perform seek */ - payload = next_frq; - ret = fmc_send_cmd(fmdev, FREQ_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - /* Set search direction (0:Seek Down, 1:Seek Up) */ - payload = (seek_upward ? FM_SEARCH_DIRECTION_UP : FM_SEARCH_DIRECTION_DOWN); - ret = fmc_send_cmd(fmdev, SEARCH_DIR_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - /* Read flags - just to clear any pending interrupts if we had */ - ret = fmc_send_cmd(fmdev, FLAG_GET, REG_RD, NULL, 2, NULL, NULL); - if (ret < 0) - return ret; - - /* Enable FR, BL interrupts */ - intr_flag = fmdev->irq_info.mask; - fmdev->irq_info.mask = (FM_FR_EVENT | FM_BL_EVENT); - payload = fmdev->irq_info.mask; - ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - /* Start seek */ - payload = FM_TUNER_AUTONOMOUS_SEARCH_MODE; - ret = fmc_send_cmd(fmdev, TUNER_MODE_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - /* Wait for tune ended/band limit reached interrupt */ - init_completion(&fmdev->maintask_comp); - timeleft = wait_for_completion_timeout(&fmdev->maintask_comp, - FM_DRV_RX_SEEK_TIMEOUT); - if (!timeleft) { - fmerr("Timeout(%d sec),didn't get tune ended int\n", - jiffies_to_msecs(FM_DRV_RX_SEEK_TIMEOUT) / 1000); - return -ENODATA; - } - - int_reason = fmdev->irq_info.flag & (FM_TUNE_COMPLETE | FM_BAND_LIMIT); - - /* Re-enable default FM interrupts */ - fmdev->irq_info.mask = intr_flag; - payload = fmdev->irq_info.mask; - ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - if (int_reason & FM_BL_EVENT) { - if (wrap_around == 0) { - fmdev->rx.freq = seek_upward ? - fmdev->rx.region.top_freq : - fmdev->rx.region.bot_freq; - } else { - fmdev->rx.freq = seek_upward ? - fmdev->rx.region.bot_freq : - fmdev->rx.region.top_freq; - /* Calculate frequency index to write */ - next_frq = (fmdev->rx.freq - - fmdev->rx.region.bot_freq) / FM_FREQ_MUL; - goto again; - } - } else { - /* Read freq to know where operation tune operation stopped */ - ret = fmc_send_cmd(fmdev, FREQ_SET, REG_RD, NULL, 2, - &curr_frq, &resp_len); - if (ret < 0) - return ret; - - curr_frq = be16_to_cpu((__force __be16)curr_frq); - fmdev->rx.freq = (fmdev->rx.region.bot_freq + - ((u32)curr_frq * FM_FREQ_MUL)); - - } - /* Reset RDS cache and current station pointers */ - fm_rx_reset_rds_cache(fmdev); - fm_rx_reset_station_info(fmdev); - - return ret; -} - -int fm_rx_set_volume(struct fmdev *fmdev, u16 vol_to_set) -{ - u16 payload; - int ret; - - if (fmdev->curr_fmmode != FM_MODE_RX) - return -EPERM; - - if (vol_to_set > FM_RX_VOLUME_MAX) { - fmerr("Volume is not within(%d-%d) range\n", - FM_RX_VOLUME_MIN, FM_RX_VOLUME_MAX); - return -EINVAL; - } - vol_to_set *= FM_RX_VOLUME_GAIN_STEP; - - payload = vol_to_set; - ret = fmc_send_cmd(fmdev, VOLUME_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - fmdev->rx.volume = vol_to_set; - return ret; -} - -/* Get volume */ -int fm_rx_get_volume(struct fmdev *fmdev, u16 *curr_vol) -{ - if (fmdev->curr_fmmode != FM_MODE_RX) - return -EPERM; - - if (curr_vol == NULL) { - fmerr("Invalid memory\n"); - return -ENOMEM; - } - - *curr_vol = fmdev->rx.volume / FM_RX_VOLUME_GAIN_STEP; - - return 0; -} - -/* To get current band's bottom and top frequency */ -int fm_rx_get_band_freq_range(struct fmdev *fmdev, u32 *bot_freq, u32 *top_freq) -{ - if (bot_freq != NULL) - *bot_freq = fmdev->rx.region.bot_freq; - - if (top_freq != NULL) - *top_freq = fmdev->rx.region.top_freq; - - return 0; -} - -/* Returns current band index (0-Europe/US; 1-Japan) */ -void fm_rx_get_region(struct fmdev *fmdev, u8 *region) -{ - *region = fmdev->rx.region.fm_band; -} - -/* Sets band (0-Europe/US; 1-Japan) */ -int fm_rx_set_region(struct fmdev *fmdev, u8 region_to_set) -{ - u16 payload; - u32 new_frq = 0; - int ret; - - if (region_to_set != FM_BAND_EUROPE_US && - region_to_set != FM_BAND_JAPAN) { - fmerr("Invalid band\n"); - return -EINVAL; - } - - if (fmdev->rx.region.fm_band == region_to_set) { - fmerr("Requested band is already configured\n"); - return 0; - } - - /* Send cmd to set the band */ - payload = (u16)region_to_set; - ret = fmc_send_cmd(fmdev, BAND_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - fmc_update_region_info(fmdev, region_to_set); - - /* Check whether current RX frequency is within band boundary */ - if (fmdev->rx.freq < fmdev->rx.region.bot_freq) - new_frq = fmdev->rx.region.bot_freq; - else if (fmdev->rx.freq > fmdev->rx.region.top_freq) - new_frq = fmdev->rx.region.top_freq; - - if (new_frq) { - fmdbg("Current freq is not within band limit boundary,switching to %d KHz\n", - new_frq); - /* Current RX frequency is not in range. So, update it */ - ret = fm_rx_set_freq(fmdev, new_frq); - } - - return ret; -} - -/* Reads current mute mode (Mute Off/On/Attenuate)*/ -int fm_rx_get_mute_mode(struct fmdev *fmdev, u8 *curr_mute_mode) -{ - if (fmdev->curr_fmmode != FM_MODE_RX) - return -EPERM; - - if (curr_mute_mode == NULL) { - fmerr("Invalid memory\n"); - return -ENOMEM; - } - - *curr_mute_mode = fmdev->rx.mute_mode; - - return 0; -} - -static int fm_config_rx_mute_reg(struct fmdev *fmdev) -{ - u16 payload, muteval; - int ret; - - muteval = 0; - switch (fmdev->rx.mute_mode) { - case FM_MUTE_ON: - muteval = FM_RX_AC_MUTE_MODE; - break; - - case FM_MUTE_OFF: - muteval = FM_RX_UNMUTE_MODE; - break; - - case FM_MUTE_ATTENUATE: - muteval = FM_RX_SOFT_MUTE_FORCE_MODE; - break; - } - if (fmdev->rx.rf_depend_mute == FM_RX_RF_DEPENDENT_MUTE_ON) - muteval |= FM_RX_RF_DEP_MODE; - else - muteval &= ~FM_RX_RF_DEP_MODE; - - payload = muteval; - ret = fmc_send_cmd(fmdev, MUTE_STATUS_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - return 0; -} - -/* Configures mute mode (Mute Off/On/Attenuate) */ -int fm_rx_set_mute_mode(struct fmdev *fmdev, u8 mute_mode_toset) -{ - u8 org_state; - int ret; - - if (fmdev->rx.mute_mode == mute_mode_toset) - return 0; - - org_state = fmdev->rx.mute_mode; - fmdev->rx.mute_mode = mute_mode_toset; - - ret = fm_config_rx_mute_reg(fmdev); - if (ret < 0) { - fmdev->rx.mute_mode = org_state; - return ret; - } - - return 0; -} - -/* Gets RF dependent soft mute mode enable/disable status */ -int fm_rx_get_rfdepend_softmute(struct fmdev *fmdev, u8 *curr_mute_mode) -{ - if (fmdev->curr_fmmode != FM_MODE_RX) - return -EPERM; - - if (curr_mute_mode == NULL) { - fmerr("Invalid memory\n"); - return -ENOMEM; - } - - *curr_mute_mode = fmdev->rx.rf_depend_mute; - - return 0; -} - -/* Sets RF dependent soft mute mode */ -int fm_rx_set_rfdepend_softmute(struct fmdev *fmdev, u8 rfdepend_mute) -{ - u8 org_state; - int ret; - - if (fmdev->curr_fmmode != FM_MODE_RX) - return -EPERM; - - if (rfdepend_mute != FM_RX_RF_DEPENDENT_MUTE_ON && - rfdepend_mute != FM_RX_RF_DEPENDENT_MUTE_OFF) { - fmerr("Invalid RF dependent soft mute\n"); - return -EINVAL; - } - if (fmdev->rx.rf_depend_mute == rfdepend_mute) - return 0; - - org_state = fmdev->rx.rf_depend_mute; - fmdev->rx.rf_depend_mute = rfdepend_mute; - - ret = fm_config_rx_mute_reg(fmdev); - if (ret < 0) { - fmdev->rx.rf_depend_mute = org_state; - return ret; - } - - return 0; -} - -/* Returns the signal strength level of current channel */ -int fm_rx_get_rssi_level(struct fmdev *fmdev, u16 *rssilvl) -{ - __be16 curr_rssi_lel; - u32 resp_len; - int ret; - - if (rssilvl == NULL) { - fmerr("Invalid memory\n"); - return -ENOMEM; - } - /* Read current RSSI level */ - ret = fmc_send_cmd(fmdev, RSSI_LVL_GET, REG_RD, NULL, 2, - &curr_rssi_lel, &resp_len); - if (ret < 0) - return ret; - - *rssilvl = be16_to_cpu(curr_rssi_lel); - - return 0; -} - -/* - * Sets the signal strength level that once reached - * will stop the auto search process - */ -int fm_rx_set_rssi_threshold(struct fmdev *fmdev, short rssi_lvl_toset) -{ - u16 payload; - int ret; - - if (rssi_lvl_toset < FM_RX_RSSI_THRESHOLD_MIN || - rssi_lvl_toset > FM_RX_RSSI_THRESHOLD_MAX) { - fmerr("Invalid RSSI threshold level\n"); - return -EINVAL; - } - payload = (u16)rssi_lvl_toset; - ret = fmc_send_cmd(fmdev, SEARCH_LVL_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - fmdev->rx.rssi_threshold = rssi_lvl_toset; - - return 0; -} - -/* Returns current RX RSSI threshold value */ -int fm_rx_get_rssi_threshold(struct fmdev *fmdev, short *curr_rssi_lvl) -{ - if (fmdev->curr_fmmode != FM_MODE_RX) - return -EPERM; - - if (curr_rssi_lvl == NULL) { - fmerr("Invalid memory\n"); - return -ENOMEM; - } - - *curr_rssi_lvl = fmdev->rx.rssi_threshold; - - return 0; -} - -/* Sets RX stereo/mono modes */ -int fm_rx_set_stereo_mono(struct fmdev *fmdev, u16 mode) -{ - u16 payload; - int ret; - - if (mode != FM_STEREO_MODE && mode != FM_MONO_MODE) { - fmerr("Invalid mode\n"); - return -EINVAL; - } - - /* Set stereo/mono mode */ - payload = (u16)mode; - ret = fmc_send_cmd(fmdev, MOST_MODE_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - /* Set stereo blending mode */ - payload = FM_STEREO_SOFT_BLEND; - ret = fmc_send_cmd(fmdev, MOST_BLEND_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - return 0; -} - -/* Gets current RX stereo/mono mode */ -int fm_rx_get_stereo_mono(struct fmdev *fmdev, u16 *mode) -{ - __be16 curr_mode; - u32 resp_len; - int ret; - - if (mode == NULL) { - fmerr("Invalid memory\n"); - return -ENOMEM; - } - - ret = fmc_send_cmd(fmdev, MOST_MODE_SET, REG_RD, NULL, 2, - &curr_mode, &resp_len); - if (ret < 0) - return ret; - - *mode = be16_to_cpu(curr_mode); - - return 0; -} - -/* Choose RX de-emphasis filter mode (50us/75us) */ -int fm_rx_set_deemphasis_mode(struct fmdev *fmdev, u16 mode) -{ - u16 payload; - int ret; - - if (fmdev->curr_fmmode != FM_MODE_RX) - return -EPERM; - - if (mode != FM_RX_EMPHASIS_FILTER_50_USEC && - mode != FM_RX_EMPHASIS_FILTER_75_USEC) { - fmerr("Invalid rx de-emphasis mode (%d)\n", mode); - return -EINVAL; - } - - payload = mode; - ret = fmc_send_cmd(fmdev, DEMPH_MODE_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - fmdev->rx.deemphasis_mode = mode; - - return 0; -} - -/* Gets current RX de-emphasis filter mode */ -int fm_rx_get_deemph_mode(struct fmdev *fmdev, u16 *curr_deemphasis_mode) -{ - if (fmdev->curr_fmmode != FM_MODE_RX) - return -EPERM; - - if (curr_deemphasis_mode == NULL) { - fmerr("Invalid memory\n"); - return -ENOMEM; - } - - *curr_deemphasis_mode = fmdev->rx.deemphasis_mode; - - return 0; -} - -/* Enable/Disable RX RDS */ -int fm_rx_set_rds_mode(struct fmdev *fmdev, u8 rds_en_dis) -{ - u16 payload; - int ret; - - if (rds_en_dis != FM_RDS_ENABLE && rds_en_dis != FM_RDS_DISABLE) { - fmerr("Invalid rds option\n"); - return -EINVAL; - } - - if (rds_en_dis == FM_RDS_ENABLE - && fmdev->rx.rds.flag == FM_RDS_DISABLE) { - /* Turn on RX RDS and RDS circuit */ - payload = FM_RX_PWR_SET_FM_AND_RDS_BLK_ON; - ret = fmc_send_cmd(fmdev, POWER_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - /* Clear and reset RDS FIFO */ - payload = FM_RX_RDS_FLUSH_FIFO; - ret = fmc_send_cmd(fmdev, RDS_CNTRL_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - /* Read flags - just to clear any pending interrupts. */ - ret = fmc_send_cmd(fmdev, FLAG_GET, REG_RD, NULL, 2, - NULL, NULL); - if (ret < 0) - return ret; - - /* Set RDS FIFO threshold value */ - payload = FM_RX_RDS_FIFO_THRESHOLD; - ret = fmc_send_cmd(fmdev, RDS_MEM_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - /* Enable RDS interrupt */ - fmdev->irq_info.mask |= FM_RDS_EVENT; - payload = fmdev->irq_info.mask; - ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) { - fmdev->irq_info.mask &= ~FM_RDS_EVENT; - return ret; - } - - /* Update our local flag */ - fmdev->rx.rds.flag = FM_RDS_ENABLE; - } else if (rds_en_dis == FM_RDS_DISABLE - && fmdev->rx.rds.flag == FM_RDS_ENABLE) { - /* Turn off RX RDS */ - payload = FM_RX_PWR_SET_FM_ON_RDS_OFF; - ret = fmc_send_cmd(fmdev, POWER_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - /* Reset RDS pointers */ - fmdev->rx.rds.last_blk_idx = 0; - fmdev->rx.rds.wr_idx = 0; - fmdev->rx.rds.rd_idx = 0; - fm_rx_reset_station_info(fmdev); - - /* Update RDS local cache */ - fmdev->irq_info.mask &= ~(FM_RDS_EVENT); - fmdev->rx.rds.flag = FM_RDS_DISABLE; - } - - return 0; -} - -/* Returns current RX RDS enable/disable status */ -int fm_rx_get_rds_mode(struct fmdev *fmdev, u8 *curr_rds_en_dis) -{ - if (fmdev->curr_fmmode != FM_MODE_RX) - return -EPERM; - - if (curr_rds_en_dis == NULL) { - fmerr("Invalid memory\n"); - return -ENOMEM; - } - - *curr_rds_en_dis = fmdev->rx.rds.flag; - - return 0; -} - -/* Sets RDS operation mode (RDS/RDBS) */ -int fm_rx_set_rds_system(struct fmdev *fmdev, u8 rds_mode) -{ - u16 payload; - int ret; - - if (fmdev->curr_fmmode != FM_MODE_RX) - return -EPERM; - - if (rds_mode != FM_RDS_SYSTEM_RDS && rds_mode != FM_RDS_SYSTEM_RBDS) { - fmerr("Invalid rds mode\n"); - return -EINVAL; - } - /* Set RDS operation mode */ - payload = (u16)rds_mode; - ret = fmc_send_cmd(fmdev, RDS_SYSTEM_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - fmdev->rx.rds_mode = rds_mode; - - return 0; -} - -/* Configures Alternate Frequency switch mode */ -int fm_rx_set_af_switch(struct fmdev *fmdev, u8 af_mode) -{ - u16 payload; - int ret; - - if (fmdev->curr_fmmode != FM_MODE_RX) - return -EPERM; - - if (af_mode != FM_RX_RDS_AF_SWITCH_MODE_ON && - af_mode != FM_RX_RDS_AF_SWITCH_MODE_OFF) { - fmerr("Invalid af mode\n"); - return -EINVAL; - } - /* Enable/disable low RSSI interrupt based on af_mode */ - if (af_mode == FM_RX_RDS_AF_SWITCH_MODE_ON) - fmdev->irq_info.mask |= FM_LEV_EVENT; - else - fmdev->irq_info.mask &= ~FM_LEV_EVENT; - - payload = fmdev->irq_info.mask; - ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - fmdev->rx.af_mode = af_mode; - - return 0; -} - -/* Returns Alternate Frequency switch status */ -int fm_rx_get_af_switch(struct fmdev *fmdev, u8 *af_mode) -{ - if (fmdev->curr_fmmode != FM_MODE_RX) - return -EPERM; - - if (af_mode == NULL) { - fmerr("Invalid memory\n"); - return -ENOMEM; - } - - *af_mode = fmdev->rx.af_mode; - - return 0; -} diff --git a/drivers/media/radio/wl128x/fmdrv_rx.h b/drivers/media/radio/wl128x/fmdrv_rx.h deleted file mode 100644 index 2748e99662c3..000000000000 --- a/drivers/media/radio/wl128x/fmdrv_rx.h +++ /dev/null @@ -1,45 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * FM Driver for Connectivity chip of Texas Instruments. - * FM RX module header. - * - * Copyright (C) 2011 Texas Instruments - */ - -#ifndef _FMDRV_RX_H -#define _FMDRV_RX_H - -int fm_rx_set_freq(struct fmdev *, u32); -int fm_rx_set_mute_mode(struct fmdev *, u8); -int fm_rx_set_stereo_mono(struct fmdev *, u16); -int fm_rx_set_rds_mode(struct fmdev *, u8); -int fm_rx_set_rds_system(struct fmdev *, u8); -int fm_rx_set_volume(struct fmdev *, u16); -int fm_rx_set_rssi_threshold(struct fmdev *, short); -int fm_rx_set_region(struct fmdev *, u8); -int fm_rx_set_rfdepend_softmute(struct fmdev *, u8); -int fm_rx_set_deemphasis_mode(struct fmdev *, u16); -int fm_rx_set_af_switch(struct fmdev *, u8); - -void fm_rx_reset_rds_cache(struct fmdev *); -void fm_rx_reset_station_info(struct fmdev *); - -int fm_rx_seek(struct fmdev *, u32, u32, u32); - -int fm_rx_get_rds_mode(struct fmdev *, u8 *); -int fm_rx_get_mute_mode(struct fmdev *, u8 *); -int fm_rx_get_volume(struct fmdev *, u16 *); -int fm_rx_get_band_freq_range(struct fmdev *, - u32 *, u32 *); -int fm_rx_get_stereo_mono(struct fmdev *, u16 *); -int fm_rx_get_rssi_level(struct fmdev *, u16 *); -int fm_rx_get_rssi_threshold(struct fmdev *, short *); -int fm_rx_get_rfdepend_softmute(struct fmdev *, u8 *); -int fm_rx_get_deemph_mode(struct fmdev *, u16 *); -int fm_rx_get_af_switch(struct fmdev *, u8 *); -void fm_rx_get_region(struct fmdev *, u8 *); - -int fm_rx_set_chanl_spacing(struct fmdev *, u8); -int fm_rx_get_chanl_spacing(struct fmdev *, u8 *); -#endif - diff --git a/drivers/media/radio/wl128x/fmdrv_tx.c b/drivers/media/radio/wl128x/fmdrv_tx.c deleted file mode 100644 index c589de02f4f5..000000000000 --- a/drivers/media/radio/wl128x/fmdrv_tx.c +++ /dev/null @@ -1,413 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * FM Driver for Connectivity chip of Texas Instruments. - * This sub-module of FM driver implements FM TX functionality. - * - * Copyright (C) 2011 Texas Instruments - */ - -#include <linux/delay.h> -#include "fmdrv.h" -#include "fmdrv_common.h" -#include "fmdrv_tx.h" - -int fm_tx_set_stereo_mono(struct fmdev *fmdev, u16 mode) -{ - u16 payload; - int ret; - - if (fmdev->tx_data.aud_mode == mode) - return 0; - - fmdbg("stereo mode: %d\n", mode); - - /* Set Stereo/Mono mode */ - payload = (1 - mode); - ret = fmc_send_cmd(fmdev, MONO_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - fmdev->tx_data.aud_mode = mode; - - return ret; -} - -static int set_rds_text(struct fmdev *fmdev, u8 *rds_text) -{ - u16 payload; - int ret; - - ret = fmc_send_cmd(fmdev, RDS_DATA_SET, REG_WR, rds_text, - strlen(rds_text), NULL, NULL); - if (ret < 0) - return ret; - - /* Scroll mode */ - payload = (u16)0x1; - ret = fmc_send_cmd(fmdev, DISPLAY_MODE, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - return 0; -} - -static int set_rds_data_mode(struct fmdev *fmdev, u8 mode) -{ - u16 payload; - int ret; - - /* Setting unique PI TODO: how unique? */ - payload = (u16)0xcafe; - ret = fmc_send_cmd(fmdev, PI_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - /* Set decoder id */ - payload = (u16)0xa; - ret = fmc_send_cmd(fmdev, DI_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - /* TODO: RDS_MODE_GET? */ - return 0; -} - -static int set_rds_len(struct fmdev *fmdev, u8 type, u16 len) -{ - u16 payload; - int ret; - - len |= type << 8; - payload = len; - ret = fmc_send_cmd(fmdev, RDS_CONFIG_DATA_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - /* TODO: LENGTH_GET? */ - return 0; -} - -int fm_tx_set_rds_mode(struct fmdev *fmdev, u8 rds_en_dis) -{ - u16 payload; - int ret; - u8 rds_text[] = "Zoom2\n"; - - fmdbg("rds_en_dis:%d(E:%d, D:%d)\n", rds_en_dis, - FM_RDS_ENABLE, FM_RDS_DISABLE); - - if (rds_en_dis == FM_RDS_ENABLE) { - /* Set RDS length */ - set_rds_len(fmdev, 0, strlen(rds_text)); - - /* Set RDS text */ - set_rds_text(fmdev, rds_text); - - /* Set RDS mode */ - set_rds_data_mode(fmdev, 0x0); - } - - /* Send command to enable RDS */ - if (rds_en_dis == FM_RDS_ENABLE) - payload = 0x01; - else - payload = 0x00; - - ret = fmc_send_cmd(fmdev, RDS_DATA_ENB, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - if (rds_en_dis == FM_RDS_ENABLE) { - /* Set RDS length */ - set_rds_len(fmdev, 0, strlen(rds_text)); - - /* Set RDS text */ - set_rds_text(fmdev, rds_text); - } - fmdev->tx_data.rds.flag = rds_en_dis; - - return 0; -} - -int fm_tx_set_radio_text(struct fmdev *fmdev, u8 *rds_text, u8 rds_type) -{ - u16 payload; - int ret; - - if (fmdev->curr_fmmode != FM_MODE_TX) - return -EPERM; - - fm_tx_set_rds_mode(fmdev, 0); - - /* Set RDS length */ - set_rds_len(fmdev, rds_type, strlen(rds_text)); - - /* Set RDS text */ - set_rds_text(fmdev, rds_text); - - /* Set RDS mode */ - set_rds_data_mode(fmdev, 0x0); - - payload = 1; - ret = fmc_send_cmd(fmdev, RDS_DATA_ENB, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - return 0; -} - -int fm_tx_set_af(struct fmdev *fmdev, u32 af) -{ - u16 payload; - int ret; - - if (fmdev->curr_fmmode != FM_MODE_TX) - return -EPERM; - - fmdbg("AF: %d\n", af); - - af = (af - 87500) / 100; - payload = (u16)af; - ret = fmc_send_cmd(fmdev, TA_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - return 0; -} - -int fm_tx_set_region(struct fmdev *fmdev, u8 region) -{ - u16 payload; - int ret; - - if (region != FM_BAND_EUROPE_US && region != FM_BAND_JAPAN) { - fmerr("Invalid band\n"); - return -EINVAL; - } - - /* Send command to set the band */ - payload = (u16)region; - ret = fmc_send_cmd(fmdev, TX_BAND_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - return 0; -} - -int fm_tx_set_mute_mode(struct fmdev *fmdev, u8 mute_mode_toset) -{ - u16 payload; - int ret; - - fmdbg("tx: mute mode %d\n", mute_mode_toset); - - payload = mute_mode_toset; - ret = fmc_send_cmd(fmdev, MUTE, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - return 0; -} - -/* Set TX Audio I/O */ -static int set_audio_io(struct fmdev *fmdev) -{ - struct fmtx_data *tx = &fmdev->tx_data; - u16 payload; - int ret; - - /* Set Audio I/O Enable */ - payload = tx->audio_io; - ret = fmc_send_cmd(fmdev, AUDIO_IO_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - /* TODO: is audio set? */ - return 0; -} - -/* Start TX Transmission */ -static int enable_xmit(struct fmdev *fmdev, u8 new_xmit_state) -{ - struct fmtx_data *tx = &fmdev->tx_data; - unsigned long timeleft; - u16 payload; - int ret; - - /* Enable POWER_ENB interrupts */ - payload = FM_POW_ENB_EVENT; - ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - /* Set Power Enable */ - payload = new_xmit_state; - ret = fmc_send_cmd(fmdev, POWER_ENB_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - /* Wait for Power Enabled */ - init_completion(&fmdev->maintask_comp); - timeleft = wait_for_completion_timeout(&fmdev->maintask_comp, - FM_DRV_TX_TIMEOUT); - if (!timeleft) { - fmerr("Timeout(%d sec),didn't get tune ended interrupt\n", - jiffies_to_msecs(FM_DRV_TX_TIMEOUT) / 1000); - return -ETIMEDOUT; - } - - set_bit(FM_CORE_TX_XMITING, &fmdev->flag); - tx->xmit_state = new_xmit_state; - - return 0; -} - -/* Set TX power level */ -int fm_tx_set_pwr_lvl(struct fmdev *fmdev, u8 new_pwr_lvl) -{ - u16 payload; - struct fmtx_data *tx = &fmdev->tx_data; - int ret; - - if (fmdev->curr_fmmode != FM_MODE_TX) - return -EPERM; - fmdbg("tx: pwr_level_to_set %ld\n", (long int)new_pwr_lvl); - - /* If the core isn't ready update global variable */ - if (!test_bit(FM_CORE_READY, &fmdev->flag)) { - tx->pwr_lvl = new_pwr_lvl; - return 0; - } - - /* Set power level: Application will specify power level value in - * units of dB/uV, whereas range and step are specific to FM chip. - * For TI's WL chips, convert application specified power level value - * to chip specific value by subtracting 122 from it. Refer to TI FM - * data sheet for details. - * */ - - payload = (FM_PWR_LVL_HIGH - new_pwr_lvl); - ret = fmc_send_cmd(fmdev, POWER_LEV_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - /* TODO: is the power level set? */ - tx->pwr_lvl = new_pwr_lvl; - - return 0; -} - -/* - * Sets FM TX pre-emphasis filter value (OFF, 50us, or 75us) - * Convert V4L2 specified filter values to chip specific filter values. - */ -int fm_tx_set_preemph_filter(struct fmdev *fmdev, u32 preemphasis) -{ - struct fmtx_data *tx = &fmdev->tx_data; - u16 payload; - int ret; - - if (fmdev->curr_fmmode != FM_MODE_TX) - return -EPERM; - - switch (preemphasis) { - case V4L2_PREEMPHASIS_DISABLED: - payload = FM_TX_PREEMPH_OFF; - break; - case V4L2_PREEMPHASIS_50_uS: - payload = FM_TX_PREEMPH_50US; - break; - case V4L2_PREEMPHASIS_75_uS: - payload = FM_TX_PREEMPH_75US; - break; - } - - ret = fmc_send_cmd(fmdev, PREMPH_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - tx->preemph = payload; - - return ret; -} - -/* Get the TX tuning capacitor value.*/ -int fm_tx_get_tune_cap_val(struct fmdev *fmdev) -{ - u16 curr_val; - u32 resp_len; - int ret; - - if (fmdev->curr_fmmode != FM_MODE_TX) - return -EPERM; - - ret = fmc_send_cmd(fmdev, READ_FMANT_TUNE_VALUE, REG_RD, - NULL, sizeof(curr_val), &curr_val, &resp_len); - if (ret < 0) - return ret; - - curr_val = be16_to_cpu((__force __be16)curr_val); - - return curr_val; -} - -/* Set TX Frequency */ -int fm_tx_set_freq(struct fmdev *fmdev, u32 freq_to_set) -{ - struct fmtx_data *tx = &fmdev->tx_data; - u16 payload, chanl_index; - int ret; - - if (test_bit(FM_CORE_TX_XMITING, &fmdev->flag)) { - enable_xmit(fmdev, 0); - clear_bit(FM_CORE_TX_XMITING, &fmdev->flag); - } - - /* Enable FR, BL interrupts */ - payload = (FM_FR_EVENT | FM_BL_EVENT); - ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - tx->tx_frq = (unsigned long)freq_to_set; - fmdbg("tx: freq_to_set %ld\n", (long int)tx->tx_frq); - - chanl_index = freq_to_set / 10; - - /* Set current tuner channel */ - payload = chanl_index; - ret = fmc_send_cmd(fmdev, CHANL_SET, REG_WR, &payload, - sizeof(payload), NULL, NULL); - if (ret < 0) - return ret; - - fm_tx_set_pwr_lvl(fmdev, tx->pwr_lvl); - fm_tx_set_preemph_filter(fmdev, tx->preemph); - - tx->audio_io = 0x01; /* I2S */ - set_audio_io(fmdev); - - enable_xmit(fmdev, 0x01); /* Enable transmission */ - - tx->aud_mode = FM_STEREO_MODE; - tx->rds.flag = FM_RDS_DISABLE; - - return 0; -} - diff --git a/drivers/media/radio/wl128x/fmdrv_tx.h b/drivers/media/radio/wl128x/fmdrv_tx.h deleted file mode 100644 index aebdadf9e99b..000000000000 --- a/drivers/media/radio/wl128x/fmdrv_tx.h +++ /dev/null @@ -1,24 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * FM Driver for Connectivity chip of Texas Instruments. - * FM TX module header. - * - * Copyright (C) 2011 Texas Instruments - */ - -#ifndef _FMDRV_TX_H -#define _FMDRV_TX_H - -int fm_tx_set_freq(struct fmdev *, u32); -int fm_tx_set_pwr_lvl(struct fmdev *, u8); -int fm_tx_set_region(struct fmdev *, u8); -int fm_tx_set_mute_mode(struct fmdev *, u8); -int fm_tx_set_stereo_mono(struct fmdev *, u16); -int fm_tx_set_rds_mode(struct fmdev *, u8); -int fm_tx_set_radio_text(struct fmdev *, u8 *, u8); -int fm_tx_set_af(struct fmdev *, u32); -int fm_tx_set_preemph_filter(struct fmdev *, u32); -int fm_tx_get_tune_cap_val(struct fmdev *); - -#endif - diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.c b/drivers/media/radio/wl128x/fmdrv_v4l2.c deleted file mode 100644 index 1c146d14dbbd..000000000000 --- a/drivers/media/radio/wl128x/fmdrv_v4l2.c +++ /dev/null @@ -1,604 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * FM Driver for Connectivity chip of Texas Instruments. - * This file provides interfaces to V4L2 subsystem. - * - * This module registers with V4L2 subsystem as Radio - * data system interface (/dev/radio). During the registration, - * it will expose two set of function pointers. - * - * 1) File operation related API (open, close, read, write, poll...etc). - * 2) Set of V4L2 IOCTL complaint API. - * - * Copyright (C) 2011 Texas Instruments - * Author: Raja Mani <raja_mani@ti.com> - * Author: Manjunatha Halli <manjunatha_halli@ti.com> - */ - -#include <linux/export.h> - -#include "fmdrv.h" -#include "fmdrv_v4l2.h" -#include "fmdrv_common.h" -#include "fmdrv_rx.h" -#include "fmdrv_tx.h" - -static struct video_device gradio_dev; -static u8 radio_disconnected; - -/* -- V4L2 RADIO (/dev/radioX) device file operation interfaces --- */ - -/* Read RX RDS data */ -static ssize_t fm_v4l2_fops_read(struct file *file, char __user * buf, - size_t count, loff_t *ppos) -{ - u8 rds_mode; - int ret; - struct fmdev *fmdev; - - fmdev = video_drvdata(file); - - if (!radio_disconnected) { - fmerr("FM device is already disconnected\n"); - return -EIO; - } - - if (mutex_lock_interruptible(&fmdev->mutex)) - return -ERESTARTSYS; - - /* Turn on RDS mode if it is disabled */ - ret = fm_rx_get_rds_mode(fmdev, &rds_mode); - if (ret < 0) { - fmerr("Unable to read current rds mode\n"); - goto read_unlock; - } - - if (rds_mode == FM_RDS_DISABLE) { - ret = fmc_set_rds_mode(fmdev, FM_RDS_ENABLE); - if (ret < 0) { - fmerr("Failed to enable rds mode\n"); - goto read_unlock; - } - } - - /* Copy RDS data from internal buffer to user buffer */ - ret = fmc_transfer_rds_from_internal_buff(fmdev, file, buf, count); -read_unlock: - mutex_unlock(&fmdev->mutex); - return ret; -} - -/* Write TX RDS data */ -static ssize_t fm_v4l2_fops_write(struct file *file, const char __user * buf, - size_t count, loff_t *ppos) -{ - struct tx_rds rds; - int ret; - struct fmdev *fmdev; - - ret = copy_from_user(&rds, buf, sizeof(rds)); - rds.text[sizeof(rds.text) - 1] = '\0'; - fmdbg("(%d)type: %d, text %s, af %d\n", - ret, rds.text_type, rds.text, rds.af_freq); - if (ret) - return -EFAULT; - - fmdev = video_drvdata(file); - if (mutex_lock_interruptible(&fmdev->mutex)) - return -ERESTARTSYS; - fm_tx_set_radio_text(fmdev, rds.text, rds.text_type); - fm_tx_set_af(fmdev, rds.af_freq); - mutex_unlock(&fmdev->mutex); - - return sizeof(rds); -} - -static __poll_t fm_v4l2_fops_poll(struct file *file, struct poll_table_struct *pts) -{ - int ret; - struct fmdev *fmdev; - - fmdev = video_drvdata(file); - mutex_lock(&fmdev->mutex); - ret = fmc_is_rds_data_available(fmdev, file, pts); - mutex_unlock(&fmdev->mutex); - if (ret < 0) - return EPOLLIN | EPOLLRDNORM; - - return 0; -} - -/* - * Handle open request for "/dev/radioX" device. - * Start with FM RX mode as default. - */ -static int fm_v4l2_fops_open(struct file *file) -{ - int ret; - struct fmdev *fmdev = NULL; - - /* Don't allow multiple open */ - if (radio_disconnected) { - fmerr("FM device is already opened\n"); - return -EBUSY; - } - - fmdev = video_drvdata(file); - - if (mutex_lock_interruptible(&fmdev->mutex)) - return -ERESTARTSYS; - ret = fmc_prepare(fmdev); - if (ret < 0) { - fmerr("Unable to prepare FM CORE\n"); - goto open_unlock; - } - - fmdbg("Load FM RX firmware..\n"); - - ret = fmc_set_mode(fmdev, FM_MODE_RX); - if (ret < 0) { - fmerr("Unable to load FM RX firmware\n"); - goto open_unlock; - } - radio_disconnected = 1; - -open_unlock: - mutex_unlock(&fmdev->mutex); - return ret; -} - -static int fm_v4l2_fops_release(struct file *file) -{ - int ret; - struct fmdev *fmdev; - - fmdev = video_drvdata(file); - if (!radio_disconnected) { - fmdbg("FM device is already closed\n"); - return 0; - } - - mutex_lock(&fmdev->mutex); - ret = fmc_set_mode(fmdev, FM_MODE_OFF); - if (ret < 0) { - fmerr("Unable to turn off the chip\n"); - goto release_unlock; - } - - ret = fmc_release(fmdev); - if (ret < 0) { - fmerr("FM CORE release failed\n"); - goto release_unlock; - } - radio_disconnected = 0; - -release_unlock: - mutex_unlock(&fmdev->mutex); - return ret; -} - -/* V4L2 RADIO (/dev/radioX) device IOCTL interfaces */ -static int fm_v4l2_vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *capability) -{ - strscpy(capability->driver, FM_DRV_NAME, sizeof(capability->driver)); - strscpy(capability->card, FM_DRV_CARD_SHORT_NAME, - sizeof(capability->card)); - sprintf(capability->bus_info, "UART"); - return 0; -} - -static int fm_g_volatile_ctrl(struct v4l2_ctrl *ctrl) -{ - struct fmdev *fmdev = container_of(ctrl->handler, - struct fmdev, ctrl_handler); - - switch (ctrl->id) { - case V4L2_CID_TUNE_ANTENNA_CAPACITOR: - ctrl->val = fm_tx_get_tune_cap_val(fmdev); - break; - default: - fmwarn("%s: Unknown IOCTL: %d\n", __func__, ctrl->id); - break; - } - - return 0; -} - -static int fm_v4l2_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct fmdev *fmdev = container_of(ctrl->handler, - struct fmdev, ctrl_handler); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_VOLUME: /* set volume */ - return fm_rx_set_volume(fmdev, (u16)ctrl->val); - - case V4L2_CID_AUDIO_MUTE: /* set mute */ - return fmc_set_mute_mode(fmdev, (u8)ctrl->val); - - case V4L2_CID_TUNE_POWER_LEVEL: - /* set TX power level - ext control */ - return fm_tx_set_pwr_lvl(fmdev, (u8)ctrl->val); - - case V4L2_CID_TUNE_PREEMPHASIS: - return fm_tx_set_preemph_filter(fmdev, (u8) ctrl->val); - - default: - return -EINVAL; - } -} - -static int fm_v4l2_vidioc_g_audio(struct file *file, void *priv, - struct v4l2_audio *audio) -{ - memset(audio, 0, sizeof(*audio)); - strscpy(audio->name, "Radio", sizeof(audio->name)); - audio->capability = V4L2_AUDCAP_STEREO; - - return 0; -} - -static int fm_v4l2_vidioc_s_audio(struct file *file, void *priv, - const struct v4l2_audio *audio) -{ - if (audio->index != 0) - return -EINVAL; - - return 0; -} - -/* Get tuner attributes. If current mode is NOT RX, return error */ -static int fm_v4l2_vidioc_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *tuner) -{ - struct fmdev *fmdev = video_drvdata(file); - u32 bottom_freq; - u32 top_freq; - u16 stereo_mono_mode; - u16 rssilvl; - int ret; - - if (tuner->index != 0) - return -EINVAL; - - if (fmdev->curr_fmmode != FM_MODE_RX) - return -EPERM; - - ret = fm_rx_get_band_freq_range(fmdev, &bottom_freq, &top_freq); - if (ret != 0) - return ret; - - ret = fm_rx_get_stereo_mono(fmdev, &stereo_mono_mode); - if (ret != 0) - return ret; - - ret = fm_rx_get_rssi_level(fmdev, &rssilvl); - if (ret != 0) - return ret; - - strscpy(tuner->name, "FM", sizeof(tuner->name)); - tuner->type = V4L2_TUNER_RADIO; - /* Store rangelow and rangehigh freq in unit of 62.5 Hz */ - tuner->rangelow = bottom_freq * 16; - tuner->rangehigh = top_freq * 16; - tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO | - ((fmdev->rx.rds.flag == FM_RDS_ENABLE) ? V4L2_TUNER_SUB_RDS : 0); - tuner->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS | - V4L2_TUNER_CAP_LOW | - V4L2_TUNER_CAP_HWSEEK_BOUNDED | - V4L2_TUNER_CAP_HWSEEK_WRAP; - tuner->audmode = (stereo_mono_mode ? - V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO); - - /* - * Actual rssi value lies in between -128 to +127. - * Convert this range from 0 to 255 by adding +128 - */ - rssilvl += 128; - - /* - * Return signal strength value should be within 0 to 65535. - * Find out correct signal radio by multiplying (65535/255) = 257 - */ - tuner->signal = rssilvl * 257; - tuner->afc = 0; - - return ret; -} - -/* - * Set tuner attributes. If current mode is NOT RX, set to RX. - * Currently, we set only audio mode (mono/stereo) and RDS state (on/off). - * Should we set other tuner attributes, too? - */ -static int fm_v4l2_vidioc_s_tuner(struct file *file, void *priv, - const struct v4l2_tuner *tuner) -{ - struct fmdev *fmdev = video_drvdata(file); - u16 aud_mode; - u8 rds_mode; - int ret; - - if (tuner->index != 0) - return -EINVAL; - - aud_mode = (tuner->audmode == V4L2_TUNER_MODE_STEREO) ? - FM_STEREO_MODE : FM_MONO_MODE; - rds_mode = (tuner->rxsubchans & V4L2_TUNER_SUB_RDS) ? - FM_RDS_ENABLE : FM_RDS_DISABLE; - - if (fmdev->curr_fmmode != FM_MODE_RX) { - ret = fmc_set_mode(fmdev, FM_MODE_RX); - if (ret < 0) { - fmerr("Failed to set RX mode\n"); - return ret; - } - } - - ret = fmc_set_stereo_mono(fmdev, aud_mode); - if (ret < 0) { - fmerr("Failed to set RX stereo/mono mode\n"); - return ret; - } - - ret = fmc_set_rds_mode(fmdev, rds_mode); - if (ret < 0) - fmerr("Failed to set RX RDS mode\n"); - - return ret; -} - -/* Get tuner or modulator radio frequency */ -static int fm_v4l2_vidioc_g_freq(struct file *file, void *priv, - struct v4l2_frequency *freq) -{ - struct fmdev *fmdev = video_drvdata(file); - int ret; - - ret = fmc_get_freq(fmdev, &freq->frequency); - if (ret < 0) { - fmerr("Failed to get frequency\n"); - return ret; - } - - /* Frequency unit of 62.5 Hz*/ - freq->frequency = (u32) freq->frequency * 16; - - return 0; -} - -/* Set tuner or modulator radio frequency */ -static int fm_v4l2_vidioc_s_freq(struct file *file, void *priv, - const struct v4l2_frequency *freq) -{ - struct fmdev *fmdev = video_drvdata(file); - - /* - * As V4L2_TUNER_CAP_LOW is set 1 user sends the frequency - * in units of 62.5 Hz. - */ - return fmc_set_freq(fmdev, freq->frequency / 16); -} - -/* Set hardware frequency seek. If current mode is NOT RX, set it RX. */ -static int fm_v4l2_vidioc_s_hw_freq_seek(struct file *file, void *priv, - const struct v4l2_hw_freq_seek *seek) -{ - struct fmdev *fmdev = video_drvdata(file); - int ret; - - if (file->f_flags & O_NONBLOCK) - return -EWOULDBLOCK; - - if (fmdev->curr_fmmode != FM_MODE_RX) { - ret = fmc_set_mode(fmdev, FM_MODE_RX); - if (ret != 0) { - fmerr("Failed to set RX mode\n"); - return ret; - } - } - - ret = fm_rx_seek(fmdev, seek->seek_upward, seek->wrap_around, - seek->spacing); - if (ret < 0) - fmerr("RX seek failed - %d\n", ret); - - return ret; -} -/* Get modulator attributes. If mode is not TX, return no attributes. */ -static int fm_v4l2_vidioc_g_modulator(struct file *file, void *priv, - struct v4l2_modulator *mod) -{ - struct fmdev *fmdev = video_drvdata(file); - - if (mod->index != 0) - return -EINVAL; - - if (fmdev->curr_fmmode != FM_MODE_TX) - return -EPERM; - - mod->txsubchans = ((fmdev->tx_data.aud_mode == FM_STEREO_MODE) ? - V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO) | - ((fmdev->tx_data.rds.flag == FM_RDS_ENABLE) ? - V4L2_TUNER_SUB_RDS : 0); - - mod->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS | - V4L2_TUNER_CAP_LOW; - - return 0; -} - -/* Set modulator attributes. If mode is not TX, set to TX. */ -static int fm_v4l2_vidioc_s_modulator(struct file *file, void *priv, - const struct v4l2_modulator *mod) -{ - struct fmdev *fmdev = video_drvdata(file); - u8 rds_mode; - u16 aud_mode; - int ret; - - if (mod->index != 0) - return -EINVAL; - - if (fmdev->curr_fmmode != FM_MODE_TX) { - ret = fmc_set_mode(fmdev, FM_MODE_TX); - if (ret != 0) { - fmerr("Failed to set TX mode\n"); - return ret; - } - } - - aud_mode = (mod->txsubchans & V4L2_TUNER_SUB_STEREO) ? - FM_STEREO_MODE : FM_MONO_MODE; - rds_mode = (mod->txsubchans & V4L2_TUNER_SUB_RDS) ? - FM_RDS_ENABLE : FM_RDS_DISABLE; - ret = fm_tx_set_stereo_mono(fmdev, aud_mode); - if (ret < 0) { - fmerr("Failed to set mono/stereo mode for TX\n"); - return ret; - } - ret = fm_tx_set_rds_mode(fmdev, rds_mode); - if (ret < 0) - fmerr("Failed to set rds mode for TX\n"); - - return ret; -} - -static const struct v4l2_file_operations fm_drv_fops = { - .owner = THIS_MODULE, - .read = fm_v4l2_fops_read, - .write = fm_v4l2_fops_write, - .poll = fm_v4l2_fops_poll, - .unlocked_ioctl = video_ioctl2, - .open = fm_v4l2_fops_open, - .release = fm_v4l2_fops_release, -}; - -static const struct v4l2_ctrl_ops fm_ctrl_ops = { - .s_ctrl = fm_v4l2_s_ctrl, - .g_volatile_ctrl = fm_g_volatile_ctrl, -}; -static const struct v4l2_ioctl_ops fm_drv_ioctl_ops = { - .vidioc_querycap = fm_v4l2_vidioc_querycap, - .vidioc_g_audio = fm_v4l2_vidioc_g_audio, - .vidioc_s_audio = fm_v4l2_vidioc_s_audio, - .vidioc_g_tuner = fm_v4l2_vidioc_g_tuner, - .vidioc_s_tuner = fm_v4l2_vidioc_s_tuner, - .vidioc_g_frequency = fm_v4l2_vidioc_g_freq, - .vidioc_s_frequency = fm_v4l2_vidioc_s_freq, - .vidioc_s_hw_freq_seek = fm_v4l2_vidioc_s_hw_freq_seek, - .vidioc_g_modulator = fm_v4l2_vidioc_g_modulator, - .vidioc_s_modulator = fm_v4l2_vidioc_s_modulator -}; - -/* V4L2 RADIO device parent structure */ -static const struct video_device fm_viddev_template = { - .fops = &fm_drv_fops, - .ioctl_ops = &fm_drv_ioctl_ops, - .name = FM_DRV_NAME, - .release = video_device_release_empty, - /* - * To ensure both the tuner and modulator ioctls are accessible we - * set the vfl_dir to M2M to indicate this. - * - * It is not really a mem2mem device of course, but it can both receive - * and transmit using the same radio device. It's the only radio driver - * that does this and it should really be split in two radio devices, - * but that would affect applications using this driver. - */ - .vfl_dir = VFL_DIR_M2M, - .device_caps = V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_TUNER | V4L2_CAP_RADIO | - V4L2_CAP_MODULATOR | V4L2_CAP_AUDIO | - V4L2_CAP_READWRITE | V4L2_CAP_RDS_CAPTURE, -}; - -int fm_v4l2_init_video_device(struct fmdev *fmdev, int radio_nr) -{ - struct v4l2_ctrl *ctrl; - int ret; - - strscpy(fmdev->v4l2_dev.name, FM_DRV_NAME, - sizeof(fmdev->v4l2_dev.name)); - ret = v4l2_device_register(NULL, &fmdev->v4l2_dev); - if (ret < 0) - return ret; - - /* Init mutex for core locking */ - mutex_init(&fmdev->mutex); - - /* Setup FM driver's V4L2 properties */ - gradio_dev = fm_viddev_template; - - video_set_drvdata(&gradio_dev, fmdev); - - gradio_dev.lock = &fmdev->mutex; - gradio_dev.v4l2_dev = &fmdev->v4l2_dev; - - /* Register with V4L2 subsystem as RADIO device */ - if (video_register_device(&gradio_dev, VFL_TYPE_RADIO, radio_nr)) { - v4l2_device_unregister(&fmdev->v4l2_dev); - fmerr("Could not register video device\n"); - return -ENOMEM; - } - - fmdev->radio_dev = &gradio_dev; - - /* Register to v4l2 ctrl handler framework */ - fmdev->radio_dev->ctrl_handler = &fmdev->ctrl_handler; - - ret = v4l2_ctrl_handler_init(&fmdev->ctrl_handler, 5); - if (ret < 0) { - fmerr("(fmdev): Can't init ctrl handler\n"); - v4l2_ctrl_handler_free(&fmdev->ctrl_handler); - video_unregister_device(fmdev->radio_dev); - v4l2_device_unregister(&fmdev->v4l2_dev); - return -EBUSY; - } - - /* - * Following controls are handled by V4L2 control framework. - * Added in ascending ID order. - */ - v4l2_ctrl_new_std(&fmdev->ctrl_handler, &fm_ctrl_ops, - V4L2_CID_AUDIO_VOLUME, FM_RX_VOLUME_MIN, - FM_RX_VOLUME_MAX, 1, FM_RX_VOLUME_MAX); - - v4l2_ctrl_new_std(&fmdev->ctrl_handler, &fm_ctrl_ops, - V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); - - v4l2_ctrl_new_std_menu(&fmdev->ctrl_handler, &fm_ctrl_ops, - V4L2_CID_TUNE_PREEMPHASIS, V4L2_PREEMPHASIS_75_uS, - 0, V4L2_PREEMPHASIS_75_uS); - - v4l2_ctrl_new_std(&fmdev->ctrl_handler, &fm_ctrl_ops, - V4L2_CID_TUNE_POWER_LEVEL, FM_PWR_LVL_LOW, - FM_PWR_LVL_HIGH, 1, FM_PWR_LVL_HIGH); - - ctrl = v4l2_ctrl_new_std(&fmdev->ctrl_handler, &fm_ctrl_ops, - V4L2_CID_TUNE_ANTENNA_CAPACITOR, 0, - 255, 1, 255); - - if (ctrl) - ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; - - return 0; -} - -void *fm_v4l2_deinit_video_device(void) -{ - struct fmdev *fmdev; - - - fmdev = video_get_drvdata(&gradio_dev); - - /* Unregister to v4l2 ctrl handler framework*/ - v4l2_ctrl_handler_free(&fmdev->ctrl_handler); - - /* Unregister RADIO device from V4L2 subsystem */ - video_unregister_device(&gradio_dev); - - v4l2_device_unregister(&fmdev->v4l2_dev); - - return fmdev; -} diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.h b/drivers/media/radio/wl128x/fmdrv_v4l2.h deleted file mode 100644 index 963214e9d6f2..000000000000 --- a/drivers/media/radio/wl128x/fmdrv_v4l2.h +++ /dev/null @@ -1,20 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * FM Driver for Connectivity chip of Texas Instruments. - * - * FM V4L2 module header. - * - * Copyright (C) 2011 Texas Instruments - */ - -#ifndef _FMDRV_V4L2_H -#define _FMDRV_V4L2_H - -#include <media/v4l2-ioctl.h> -#include <media/v4l2-common.h> -#include <media/v4l2-ctrls.h> - -int fm_v4l2_init_video_device(struct fmdev *, int); -void *fm_v4l2_deinit_video_device(void); - -#endif diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c index 276bf3c8a8cb..8af94246e591 100644 --- a/drivers/media/rc/iguanair.c +++ b/drivers/media/rc/iguanair.c @@ -194,8 +194,10 @@ static int iguanair_send(struct iguanair *ir, unsigned size) if (rc) return rc; - if (wait_for_completion_timeout(&ir->completion, TIMEOUT) == 0) + if (wait_for_completion_timeout(&ir->completion, TIMEOUT) == 0) { + usb_kill_urb(ir->urb_out); return -ETIMEDOUT; + } return rc; } diff --git a/drivers/media/rc/imon_raw.c b/drivers/media/rc/imon_raw.c index b02ded52f19e..3a526dea6532 100644 --- a/drivers/media/rc/imon_raw.c +++ b/drivers/media/rc/imon_raw.c @@ -37,7 +37,7 @@ static void imon_ir_data(struct imon *imon) if (packet_no == 0xff) return; - dev_dbg(imon->dev, "data: %*ph", 8, imon->ir_buf); + dev_dbg(imon->dev, "data: %8ph", imon->ir_buf); /* * Only the first 5 bytes contain IR data. Right shift so we move diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index cd7af4d88b7f..044767eb3a38 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -28,7 +28,6 @@ #include <linux/workqueue.h> #include <linux/usb.h> #include <linux/usb/input.h> -#include <linux/pm_wakeup.h> #include <media/rc-core.h> #define DRIVER_VERSION "1.95" @@ -658,8 +657,8 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, u8 *buf, int buf_len, if (len == 2) dev_dbg(dev, "Get hw/sw rev?"); else - dev_dbg(dev, "hw/sw rev %*ph", - 4, &buf[offset + 2]); + dev_dbg(dev, "hw/sw rev %4ph", + &buf[offset + 2]); break; case MCE_CMD_RESUME: dev_dbg(dev, "Device resume requested"); diff --git a/drivers/media/test-drivers/vidtv/vidtv_bridge.c b/drivers/media/test-drivers/vidtv/vidtv_bridge.c index e1dd8adeba46..438483c62fac 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_bridge.c +++ b/drivers/media/test-drivers/vidtv/vidtv_bridge.c @@ -191,10 +191,11 @@ static int vidtv_start_streaming(struct vidtv_dvb *dvb) mux_args.mux_buf_sz = mux_buf_sz; - dvb->streaming = true; dvb->mux = vidtv_mux_init(dvb->fe[0], dev, &mux_args); if (!dvb->mux) return -ENOMEM; + + dvb->streaming = true; vidtv_mux_start_thread(dvb->mux); dev_dbg_ratelimited(dev, "Started streaming\n"); @@ -205,6 +206,11 @@ static int vidtv_stop_streaming(struct vidtv_dvb *dvb) { struct device *dev = &dvb->pdev->dev; + if (!dvb->streaming) { + dev_warn_ratelimited(dev, "No streaming. Skipping.\n"); + return 0; + } + dvb->streaming = false; vidtv_mux_stop_thread(dvb->mux); vidtv_mux_destroy(dvb->mux); diff --git a/drivers/media/tuners/fc0013.c b/drivers/media/tuners/fc0013.c index 1006a2798eef..90d2ef067594 100644 --- a/drivers/media/tuners/fc0013.c +++ b/drivers/media/tuners/fc0013.c @@ -112,70 +112,6 @@ static int fc0013_sleep(struct dvb_frontend *fe) return 0; } -int fc0013_rc_cal_add(struct dvb_frontend *fe, int rc_val) -{ - struct fc0013_priv *priv = fe->tuner_priv; - int ret; - u8 rc_cal; - int val; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ - - /* push rc_cal value, get rc_cal value */ - ret = fc0013_writereg(priv, 0x10, 0x00); - if (ret) - goto error_out; - - /* get rc_cal value */ - ret = fc0013_readreg(priv, 0x10, &rc_cal); - if (ret) - goto error_out; - - rc_cal &= 0x0f; - - val = (int)rc_cal + rc_val; - - /* forcing rc_cal */ - ret = fc0013_writereg(priv, 0x0d, 0x11); - if (ret) - goto error_out; - - /* modify rc_cal value */ - if (val > 15) - ret = fc0013_writereg(priv, 0x10, 0x0f); - else if (val < 0) - ret = fc0013_writereg(priv, 0x10, 0x00); - else - ret = fc0013_writereg(priv, 0x10, (u8)val); - -error_out: - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ - - return ret; -} -EXPORT_SYMBOL(fc0013_rc_cal_add); - -int fc0013_rc_cal_reset(struct dvb_frontend *fe) -{ - struct fc0013_priv *priv = fe->tuner_priv; - int ret; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ - - ret = fc0013_writereg(priv, 0x0d, 0x01); - if (!ret) - ret = fc0013_writereg(priv, 0x10, 0x00); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ - - return ret; -} -EXPORT_SYMBOL(fc0013_rc_cal_reset); - static int fc0013_set_vhf_track(struct fc0013_priv *priv, u32 freq) { int ret; diff --git a/drivers/media/tuners/fc0013.h b/drivers/media/tuners/fc0013.h index 74ce5903f199..47ab36342ee8 100644 --- a/drivers/media/tuners/fc0013.h +++ b/drivers/media/tuners/fc0013.h @@ -16,8 +16,6 @@ extern struct dvb_frontend *fc0013_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_address, int dual_master, enum fc001x_xtal_freq xtal_freq); -extern int fc0013_rc_cal_add(struct dvb_frontend *fe, int rc_val); -extern int fc0013_rc_cal_reset(struct dvb_frontend *fe); #else static inline struct dvb_frontend *fc0013_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, @@ -28,15 +26,6 @@ static inline struct dvb_frontend *fc0013_attach(struct dvb_frontend *fe, return NULL; } -static inline int fc0013_rc_cal_add(struct dvb_frontend *fe, int rc_val) -{ - return 0; -} - -static inline int fc0013_rc_cal_reset(struct dvb_frontend *fe) -{ - return 0; -} #endif #endif diff --git a/drivers/media/usb/cx231xx/cx231xx-avcore.c b/drivers/media/usb/cx231xx/cx231xx-avcore.c index 6139ef5d891d..1cfec76b72f3 100644 --- a/drivers/media/usb/cx231xx/cx231xx-avcore.c +++ b/drivers/media/usb/cx231xx/cx231xx-avcore.c @@ -2704,7 +2704,6 @@ int cx231xx_set_gpio_value(struct cx231xx *dev, int pin_number, int pin_value) dev->gpio_dir = value; status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, dev->gpio_val); - value = 0; } if (pin_value == 0) diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index 0d2c42819d39..218f712f56b1 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -322,13 +322,16 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, ret = -EOPNOTSUPP; } else if ((msg[0].addr == state->af9033_i2c_addr[0]) || (msg[0].addr == state->af9033_i2c_addr[1])) { + /* demod access via firmware interface */ + u32 reg; + if (msg[0].len < 3 || msg[1].len < 1) { ret = -EOPNOTSUPP; goto unlock; } - /* demod access via firmware interface */ - u32 reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 | - msg[0].buf[2]; + + reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 | + msg[0].buf[2]; if (msg[0].addr == state->af9033_i2c_addr[1]) reg |= 0x100000; @@ -385,13 +388,16 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, ret = -EOPNOTSUPP; } else if ((msg[0].addr == state->af9033_i2c_addr[0]) || (msg[0].addr == state->af9033_i2c_addr[1])) { + /* demod access via firmware interface */ + u32 reg; + if (msg[0].len < 3) { ret = -EOPNOTSUPP; goto unlock; } - /* demod access via firmware interface */ - u32 reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 | - msg[0].buf[2]; + + reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 | + msg[0].buf[2]; if (msg[0].addr == state->af9033_i2c_addr[1]) reg |= 0x100000; diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c index 8a34e6c0d6a6..f0537b741d13 100644 --- a/drivers/media/usb/dvb-usb-v2/lmedm04.c +++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c @@ -373,6 +373,7 @@ static int lme2510_int_read(struct dvb_usb_adapter *adap) struct dvb_usb_device *d = adap_to_d(adap); struct lme2510_state *lme_int = adap_to_priv(adap); struct usb_host_endpoint *ep; + int ret; lme_int->lme_urb = usb_alloc_urb(0, GFP_KERNEL); @@ -390,11 +391,20 @@ static int lme2510_int_read(struct dvb_usb_adapter *adap) /* Quirk of pipe reporting PIPE_BULK but behaves as interrupt */ ep = usb_pipe_endpoint(d->udev, lme_int->lme_urb->pipe); + if (!ep) { + usb_free_urb(lme_int->lme_urb); + return -ENODEV; + } if (usb_endpoint_type(&ep->desc) == USB_ENDPOINT_XFER_BULK) lme_int->lme_urb->pipe = usb_rcvbulkpipe(d->udev, 0xa); - usb_submit_urb(lme_int->lme_urb, GFP_KERNEL); + ret = usb_submit_urb(lme_int->lme_urb, GFP_KERNEL); + if (ret) { + usb_free_urb(lme_int->lme_urb); + return ret; + } + info("INT Interrupt Service Started"); return 0; diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index 4fe26e82e3d1..4e58476d305e 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -1579,6 +1579,40 @@ static void uvc_ctrl_send_slave_event(struct uvc_video_chain *chain, uvc_ctrl_send_event(chain, handle, ctrl, mapping, val, changes); } +static void uvc_ctrl_set_handle(struct uvc_fh *handle, struct uvc_control *ctrl, + struct uvc_fh *new_handle) +{ + lockdep_assert_held(&handle->chain->ctrl_mutex); + + if (new_handle) { + if (ctrl->handle) + dev_warn_ratelimited(&handle->stream->dev->udev->dev, + "UVC non compliance: Setting an async control with a pending operation."); + + if (new_handle == ctrl->handle) + return; + + if (ctrl->handle) { + WARN_ON(!ctrl->handle->pending_async_ctrls); + if (ctrl->handle->pending_async_ctrls) + ctrl->handle->pending_async_ctrls--; + } + + ctrl->handle = new_handle; + handle->pending_async_ctrls++; + return; + } + + /* Cannot clear the handle for a control not owned by us.*/ + if (WARN_ON(ctrl->handle != handle)) + return; + + ctrl->handle = NULL; + if (WARN_ON(!handle->pending_async_ctrls)) + return; + handle->pending_async_ctrls--; +} + void uvc_ctrl_status_event(struct uvc_video_chain *chain, struct uvc_control *ctrl, const u8 *data) { @@ -1588,8 +1622,12 @@ void uvc_ctrl_status_event(struct uvc_video_chain *chain, mutex_lock(&chain->ctrl_mutex); + /* Flush the control cache, the data might have changed. */ + ctrl->loaded = 0; + handle = ctrl->handle; - ctrl->handle = NULL; + if (handle) + uvc_ctrl_set_handle(handle, ctrl, NULL); list_for_each_entry(mapping, &ctrl->info.mappings, list) { s32 value = __uvc_ctrl_get_value(mapping, data); @@ -1640,10 +1678,8 @@ bool uvc_ctrl_status_event_async(struct urb *urb, struct uvc_video_chain *chain, struct uvc_device *dev = chain->dev; struct uvc_ctrl_work *w = &dev->async_ctrl; - if (list_empty(&ctrl->info.mappings)) { - ctrl->handle = NULL; + if (list_empty(&ctrl->info.mappings)) return false; - } w->data = data; w->urb = urb; @@ -1673,13 +1709,13 @@ static void uvc_ctrl_send_events(struct uvc_fh *handle, { struct uvc_control_mapping *mapping; struct uvc_control *ctrl; - u32 changes = V4L2_EVENT_CTRL_CH_VALUE; unsigned int i; unsigned int j; for (i = 0; i < xctrls_count; ++i) { - ctrl = uvc_find_control(handle->chain, xctrls[i].id, &mapping); + u32 changes = V4L2_EVENT_CTRL_CH_VALUE; + ctrl = uvc_find_control(handle->chain, xctrls[i].id, &mapping); if (ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS) /* Notification will be sent from an Interrupt event. */ continue; @@ -1811,7 +1847,10 @@ int uvc_ctrl_begin(struct uvc_video_chain *chain) } static int uvc_ctrl_commit_entity(struct uvc_device *dev, - struct uvc_entity *entity, int rollback, struct uvc_control **err_ctrl) + struct uvc_fh *handle, + struct uvc_entity *entity, + int rollback, + struct uvc_control **err_ctrl) { struct uvc_control *ctrl; unsigned int i; @@ -1859,6 +1898,10 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev, *err_ctrl = ctrl; return ret; } + + if (!rollback && handle && + ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS) + uvc_ctrl_set_handle(handle, ctrl, handle); } return 0; @@ -1895,8 +1938,8 @@ int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback, /* Find the control. */ list_for_each_entry(entity, &chain->entities, chain) { - ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback, - &err_ctrl); + ret = uvc_ctrl_commit_entity(chain->dev, handle, entity, + rollback, &err_ctrl); if (ret < 0) { if (ctrls) ctrls->error_idx = @@ -1941,6 +1984,8 @@ int uvc_ctrl_set(struct uvc_fh *handle, s32 max; int ret; + lockdep_assert_held(&chain->ctrl_mutex); + if (__uvc_query_v4l2_class(chain, xctrl->id, 0) >= 0) return -EACCES; @@ -2046,9 +2091,6 @@ int uvc_ctrl_set(struct uvc_fh *handle, mapping->set(mapping, value, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); - if (ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS) - ctrl->handle = handle; - ctrl->dirty = 1; ctrl->modified = 1; return 0; @@ -2377,7 +2419,7 @@ int uvc_ctrl_restore_values(struct uvc_device *dev) ctrl->dirty = 1; } - ret = uvc_ctrl_commit_entity(dev, entity, 0, NULL); + ret = uvc_ctrl_commit_entity(dev, NULL, entity, 0, NULL); if (ret < 0) return ret; } @@ -2770,6 +2812,26 @@ int uvc_ctrl_init_device(struct uvc_device *dev) return 0; } +void uvc_ctrl_cleanup_fh(struct uvc_fh *handle) +{ + struct uvc_entity *entity; + + guard(mutex)(&handle->chain->ctrl_mutex); + + if (!handle->pending_async_ctrls) + return; + + list_for_each_entry(entity, &handle->chain->dev->entities, list) { + for (unsigned int i = 0; i < entity->ncontrols; ++i) { + if (entity->controls[i].handle != handle) + continue; + uvc_ctrl_set_handle(handle, &entity->controls[i], NULL); + } + } + + WARN_ON(handle->pending_async_ctrls); +} + /* * Cleanup device controls. */ diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index b3c8411dc05c..a10d4f4d9f95 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -32,7 +32,7 @@ unsigned int uvc_clock_param = CLOCK_MONOTONIC; unsigned int uvc_hw_timestamps_param; -unsigned int uvc_no_drop_param; +unsigned int uvc_no_drop_param = 1; static unsigned int uvc_quirks_param = -1; unsigned int uvc_dbg_param; unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT; @@ -220,20 +220,127 @@ static struct uvc_streaming *uvc_stream_new(struct uvc_device *dev, * Descriptors parsing */ +static int uvc_parse_frame(struct uvc_device *dev, + struct uvc_streaming *streaming, + struct uvc_format *format, struct uvc_frame *frame, + u32 **intervals, u8 ftype, int width_multiplier, + const unsigned char *buffer, int buflen) +{ + struct usb_host_interface *alts = streaming->intf->cur_altsetting; + unsigned int maxIntervalIndex; + unsigned int interval; + unsigned int i, n; + + if (ftype != UVC_VS_FRAME_FRAME_BASED) + n = buflen > 25 ? buffer[25] : 0; + else + n = buflen > 21 ? buffer[21] : 0; + + n = n ? n : 3; + + if (buflen < 26 + 4 * n) { + uvc_dbg(dev, DESCR, + "device %d videostreaming interface %d FRAME error\n", + dev->udev->devnum, alts->desc.bInterfaceNumber); + return -EINVAL; + } + + frame->bFrameIndex = buffer[3]; + frame->bmCapabilities = buffer[4]; + frame->wWidth = get_unaligned_le16(&buffer[5]) * width_multiplier; + frame->wHeight = get_unaligned_le16(&buffer[7]); + frame->dwMinBitRate = get_unaligned_le32(&buffer[9]); + frame->dwMaxBitRate = get_unaligned_le32(&buffer[13]); + if (ftype != UVC_VS_FRAME_FRAME_BASED) { + frame->dwMaxVideoFrameBufferSize = + get_unaligned_le32(&buffer[17]); + frame->dwDefaultFrameInterval = + get_unaligned_le32(&buffer[21]); + frame->bFrameIntervalType = buffer[25]; + } else { + frame->dwMaxVideoFrameBufferSize = 0; + frame->dwDefaultFrameInterval = + get_unaligned_le32(&buffer[17]); + frame->bFrameIntervalType = buffer[21]; + } + + /* + * Copy the frame intervals. + * + * Some bogus devices report dwMinFrameInterval equal to + * dwMaxFrameInterval and have dwFrameIntervalStep set to zero. Setting + * all null intervals to 1 fixes the problem and some other divisions + * by zero that could happen. + */ + frame->dwFrameInterval = *intervals; + + for (i = 0; i < n; ++i) { + interval = get_unaligned_le32(&buffer[26 + 4 * i]); + (*intervals)[i] = interval ? interval : 1; + } + + /* + * Apply more fixes, quirks and workarounds to handle incorrect or + * broken descriptors. + */ + + /* + * Several UVC chipsets screw up dwMaxVideoFrameBufferSize completely. + * Observed behaviours range from setting the value to 1.1x the actual + * frame size to hardwiring the 16 low bits to 0. This results in a + * higher than necessary memory usage as well as a wrong image size + * information. For uncompressed formats this can be fixed by computing + * the value from the frame size. + */ + if (!(format->flags & UVC_FMT_FLAG_COMPRESSED)) + frame->dwMaxVideoFrameBufferSize = format->bpp * frame->wWidth + * frame->wHeight / 8; + + /* + * Clamp the default frame interval to the boundaries. A zero + * bFrameIntervalType value indicates a continuous frame interval + * range, with dwFrameInterval[0] storing the minimum value and + * dwFrameInterval[1] storing the maximum value. + */ + maxIntervalIndex = frame->bFrameIntervalType ? n - 1 : 1; + frame->dwDefaultFrameInterval = + clamp(frame->dwDefaultFrameInterval, + frame->dwFrameInterval[0], + frame->dwFrameInterval[maxIntervalIndex]); + + /* + * Some devices report frame intervals that are not functional. If the + * corresponding quirk is set, restrict operation to the first interval + * only. + */ + if (dev->quirks & UVC_QUIRK_RESTRICT_FRAME_RATE) { + frame->bFrameIntervalType = 1; + (*intervals)[0] = frame->dwDefaultFrameInterval; + } + + uvc_dbg(dev, DESCR, "- %ux%u (%u.%u fps)\n", + frame->wWidth, frame->wHeight, + 10000000 / frame->dwDefaultFrameInterval, + (100000000 / frame->dwDefaultFrameInterval) % 10); + + *intervals += n; + + return buffer[0]; +} + static int uvc_parse_format(struct uvc_device *dev, struct uvc_streaming *streaming, struct uvc_format *format, struct uvc_frame *frames, u32 **intervals, const unsigned char *buffer, int buflen) { - struct usb_interface *intf = streaming->intf; - struct usb_host_interface *alts = intf->cur_altsetting; + struct usb_host_interface *alts = streaming->intf->cur_altsetting; const struct uvc_format_desc *fmtdesc; struct uvc_frame *frame; const unsigned char *start = buffer; unsigned int width_multiplier = 1; - unsigned int interval; unsigned int i, n; u8 ftype; + int ret; format->type = buffer[2]; format->index = buffer[3]; @@ -371,111 +478,19 @@ static int uvc_parse_format(struct uvc_device *dev, * Parse the frame descriptors. Only uncompressed, MJPEG and frame * based formats have frame descriptors. */ - while (ftype && buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && - buffer[2] == ftype) { - unsigned int maxIntervalIndex; - - frame = &frames[format->nframes]; - if (ftype != UVC_VS_FRAME_FRAME_BASED) - n = buflen > 25 ? buffer[25] : 0; - else - n = buflen > 21 ? buffer[21] : 0; - - n = n ? n : 3; - - if (buflen < 26 + 4*n) { - uvc_dbg(dev, DESCR, - "device %d videostreaming interface %d FRAME error\n", - dev->udev->devnum, - alts->desc.bInterfaceNumber); - return -EINVAL; - } - - frame->bFrameIndex = buffer[3]; - frame->bmCapabilities = buffer[4]; - frame->wWidth = get_unaligned_le16(&buffer[5]) - * width_multiplier; - frame->wHeight = get_unaligned_le16(&buffer[7]); - frame->dwMinBitRate = get_unaligned_le32(&buffer[9]); - frame->dwMaxBitRate = get_unaligned_le32(&buffer[13]); - if (ftype != UVC_VS_FRAME_FRAME_BASED) { - frame->dwMaxVideoFrameBufferSize = - get_unaligned_le32(&buffer[17]); - frame->dwDefaultFrameInterval = - get_unaligned_le32(&buffer[21]); - frame->bFrameIntervalType = buffer[25]; - } else { - frame->dwMaxVideoFrameBufferSize = 0; - frame->dwDefaultFrameInterval = - get_unaligned_le32(&buffer[17]); - frame->bFrameIntervalType = buffer[21]; - } - - /* - * Copy the frame intervals. - * - * Some bogus devices report dwMinFrameInterval equal to - * dwMaxFrameInterval and have dwFrameIntervalStep set to - * zero. Setting all null intervals to 1 fixes the problem and - * some other divisions by zero that could happen. - */ - frame->dwFrameInterval = *intervals; - - for (i = 0; i < n; ++i) { - interval = get_unaligned_le32(&buffer[26+4*i]); - (*intervals)[i] = interval ? interval : 1; - } - - /* - * Apply more fixes, quirks and workarounds to handle incorrect - * or broken descriptors. - */ - - /* - * Several UVC chipsets screw up dwMaxVideoFrameBufferSize - * completely. Observed behaviours range from setting the - * value to 1.1x the actual frame size to hardwiring the - * 16 low bits to 0. This results in a higher than necessary - * memory usage as well as a wrong image size information. For - * uncompressed formats this can be fixed by computing the - * value from the frame size. - */ - if (!(format->flags & UVC_FMT_FLAG_COMPRESSED)) - frame->dwMaxVideoFrameBufferSize = format->bpp - * frame->wWidth * frame->wHeight / 8; - - /* - * Clamp the default frame interval to the boundaries. A zero - * bFrameIntervalType value indicates a continuous frame - * interval range, with dwFrameInterval[0] storing the minimum - * value and dwFrameInterval[1] storing the maximum value. - */ - maxIntervalIndex = frame->bFrameIntervalType ? n - 1 : 1; - frame->dwDefaultFrameInterval = - clamp(frame->dwDefaultFrameInterval, - frame->dwFrameInterval[0], - frame->dwFrameInterval[maxIntervalIndex]); - - /* - * Some devices report frame intervals that are not functional. - * If the corresponding quirk is set, restrict operation to the - * first interval only. - */ - if (dev->quirks & UVC_QUIRK_RESTRICT_FRAME_RATE) { - frame->bFrameIntervalType = 1; - (*intervals)[0] = frame->dwDefaultFrameInterval; + if (ftype) { + while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && + buffer[2] == ftype) { + frame = &frames[format->nframes]; + ret = uvc_parse_frame(dev, streaming, format, frame, + intervals, ftype, width_multiplier, + buffer, buflen); + if (ret < 0) + return ret; + format->nframes++; + buflen -= ret; + buffer += ret; } - - uvc_dbg(dev, DESCR, "- %ux%u (%u.%u fps)\n", - frame->wWidth, frame->wHeight, - 10000000 / frame->dwDefaultFrameInterval, - (100000000 / frame->dwDefaultFrameInterval) % 10); - - format->nframes++; - *intervals += n; - - buflen -= buffer[0]; - buffer += buffer[0]; } if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && @@ -1295,14 +1310,14 @@ static int uvc_gpio_parse(struct uvc_device *dev) struct gpio_desc *gpio_privacy; int irq; - gpio_privacy = devm_gpiod_get_optional(&dev->udev->dev, "privacy", + gpio_privacy = devm_gpiod_get_optional(&dev->intf->dev, "privacy", GPIOD_IN); if (IS_ERR_OR_NULL(gpio_privacy)) return PTR_ERR_OR_ZERO(gpio_privacy); irq = gpiod_to_irq(gpio_privacy); if (irq < 0) - return dev_err_probe(&dev->udev->dev, irq, + return dev_err_probe(&dev->intf->dev, irq, "No IRQ for privacy GPIO\n"); unit = uvc_alloc_new_entity(dev, UVC_EXT_GPIO_UNIT, @@ -1329,15 +1344,27 @@ static int uvc_gpio_parse(struct uvc_device *dev) static int uvc_gpio_init_irq(struct uvc_device *dev) { struct uvc_entity *unit = dev->gpio_unit; + int ret; if (!unit || unit->gpio.irq < 0) return 0; - return devm_request_threaded_irq(&dev->udev->dev, unit->gpio.irq, NULL, - uvc_gpio_irq, - IRQF_ONESHOT | IRQF_TRIGGER_FALLING | - IRQF_TRIGGER_RISING, - "uvc_privacy_gpio", dev); + ret = request_threaded_irq(unit->gpio.irq, NULL, uvc_gpio_irq, + IRQF_ONESHOT | IRQF_TRIGGER_FALLING | + IRQF_TRIGGER_RISING, + "uvc_privacy_gpio", dev); + + unit->gpio.initialized = !ret; + + return ret; +} + +static void uvc_gpio_deinit(struct uvc_device *dev) +{ + if (!dev->gpio_unit || !dev->gpio_unit->gpio.initialized) + return; + + free_irq(dev->gpio_unit->gpio.irq, dev); } /* ------------------------------------------------------------------------ @@ -1934,6 +1961,8 @@ static void uvc_unregister_video(struct uvc_device *dev) { struct uvc_streaming *stream; + uvc_gpio_deinit(dev); + list_for_each_entry(stream, &dev->streams, list) { /* Nothing to do here, continue. */ if (!video_is_registered(&stream->vdev)) @@ -1995,7 +2024,7 @@ int uvc_register_video_device(struct uvc_device *dev, int ret; /* Initialize the video buffers queue. */ - ret = uvc_queue_init(queue, type, !uvc_no_drop_param); + ret = uvc_queue_init(queue, type); if (ret) return ret; @@ -2424,8 +2453,25 @@ module_param_call(clock, uvc_clock_param_set, uvc_clock_param_get, MODULE_PARM_DESC(clock, "Video buffers timestamp clock"); module_param_named(hwtimestamps, uvc_hw_timestamps_param, uint, 0644); MODULE_PARM_DESC(hwtimestamps, "Use hardware timestamps"); -module_param_named(nodrop, uvc_no_drop_param, uint, 0644); + +static int param_set_nodrop(const char *val, const struct kernel_param *kp) +{ + pr_warn_once("uvcvideo: " + DEPRECATED + "nodrop parameter will be eventually removed.\n"); + return param_set_bool(val, kp); +} + +static const struct kernel_param_ops param_ops_nodrop = { + .set = param_set_nodrop, + .get = param_get_uint, +}; + +param_check_uint(nodrop, &uvc_no_drop_param); +module_param_cb(nodrop, ¶m_ops_nodrop, &uvc_no_drop_param, 0644); +__MODULE_PARM_TYPE(nodrop, "uint"); MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames"); + module_param_named(quirks, uvc_quirks_param, uint, 0644); MODULE_PARM_DESC(quirks, "Forced device quirks"); module_param_named(trace, uvc_dbg_param, uint, 0644); @@ -2802,6 +2848,15 @@ static const struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, + /* Sonix Technology Co. Ltd. - 292A IPC AR0330 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0c45, + .idProduct = 0x6366, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_MJPEG_NO_EOF) }, /* MT6227 */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -2830,6 +2885,15 @@ static const struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, + /* Kurokesu C1 PRO */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x16d0, + .idProduct = 0x0ed1, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_MJPEG_NO_EOF) }, /* Syntek (HP Spartan) */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c index 26ee85657fc8..2ee142621042 100644 --- a/drivers/media/usb/uvc/uvc_queue.c +++ b/drivers/media/usb/uvc/uvc_queue.c @@ -208,8 +208,7 @@ static const struct vb2_ops uvc_meta_queue_qops = { .stop_streaming = uvc_stop_streaming, }; -int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type, - int drop_corrupted) +int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type) { int ret; @@ -239,7 +238,6 @@ int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type, mutex_init(&queue->mutex); spin_lock_init(&queue->irqlock); INIT_LIST_HEAD(&queue->irqqueue); - queue->flags = drop_corrupted ? UVC_QUEUE_DROP_CORRUPTED : 0; return 0; } @@ -472,14 +470,15 @@ static void uvc_queue_buffer_complete(struct kref *ref) struct vb2_buffer *vb = &buf->buf.vb2_buf; struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue); - if ((queue->flags & UVC_QUEUE_DROP_CORRUPTED) && buf->error) { + if (buf->error && !uvc_no_drop_param) { uvc_queue_buffer_requeue(queue, buf); return; } buf->state = buf->error ? UVC_BUF_STATE_ERROR : UVC_BUF_STATE_DONE; vb2_set_plane_payload(&buf->buf.vb2_buf, 0, buf->bytesused); - vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE); + vb2_buffer_done(&buf->buf.vb2_buf, buf->error ? VB2_BUF_STATE_ERROR : + VB2_BUF_STATE_DONE); } /* diff --git a/drivers/media/usb/uvc/uvc_status.c b/drivers/media/usb/uvc/uvc_status.c index 06c867510c8f..ee01dce4b783 100644 --- a/drivers/media/usb/uvc/uvc_status.c +++ b/drivers/media/usb/uvc/uvc_status.c @@ -262,8 +262,6 @@ int uvc_status_init(struct uvc_device *dev) if (ep == NULL) return 0; - uvc_input_init(dev); - dev->status = kzalloc(sizeof(*dev->status), GFP_KERNEL); if (!dev->status) return -ENOMEM; @@ -271,6 +269,7 @@ int uvc_status_init(struct uvc_device *dev) dev->int_urb = usb_alloc_urb(0, GFP_KERNEL); if (!dev->int_urb) { kfree(dev->status); + dev->status = NULL; return -ENOMEM; } @@ -289,11 +288,16 @@ int uvc_status_init(struct uvc_device *dev) dev->status, sizeof(*dev->status), uvc_status_complete, dev, interval); + uvc_input_init(dev); + return 0; } void uvc_status_unregister(struct uvc_device *dev) { + if (!dev->status) + return; + uvc_status_suspend(dev); uvc_input_unregister(dev); } diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index 97c5407f6603..93c6cdb23881 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -26,6 +26,8 @@ #include "uvcvideo.h" +static int uvc_acquire_privileges(struct uvc_fh *handle); + static int uvc_control_add_xu_mapping(struct uvc_video_chain *chain, struct uvc_control_mapping *map, const struct uvc_xu_control_mapping *xmap) @@ -361,9 +363,11 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream, return ret; } -static int uvc_v4l2_get_format(struct uvc_streaming *stream, - struct v4l2_format *fmt) +static int uvc_ioctl_g_fmt(struct file *file, void *fh, + struct v4l2_format *fmt) { + struct uvc_fh *handle = fh; + struct uvc_streaming *stream = handle->stream; const struct uvc_format *format; const struct uvc_frame *frame; int ret = 0; @@ -395,14 +399,20 @@ done: return ret; } -static int uvc_v4l2_set_format(struct uvc_streaming *stream, - struct v4l2_format *fmt) +static int uvc_ioctl_s_fmt(struct file *file, void *fh, + struct v4l2_format *fmt) { + struct uvc_fh *handle = fh; + struct uvc_streaming *stream = handle->stream; struct uvc_streaming_control probe; const struct uvc_format *format; const struct uvc_frame *frame; int ret; + ret = uvc_acquire_privileges(handle); + if (ret < 0) + return ret; + if (fmt->type != stream->type) return -EINVAL; @@ -426,10 +436,12 @@ done: return ret; } -static int uvc_v4l2_get_streamparm(struct uvc_streaming *stream, - struct v4l2_streamparm *parm) +static int uvc_ioctl_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *parm) { u32 numerator, denominator; + struct uvc_fh *handle = fh; + struct uvc_streaming *stream = handle->stream; if (parm->type != stream->type) return -EINVAL; @@ -461,9 +473,11 @@ static int uvc_v4l2_get_streamparm(struct uvc_streaming *stream, return 0; } -static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream, - struct v4l2_streamparm *parm) +static int uvc_ioctl_s_parm(struct file *file, void *fh, + struct v4l2_streamparm *parm) { + struct uvc_fh *handle = fh; + struct uvc_streaming *stream = handle->stream; struct uvc_streaming_control probe; struct v4l2_fract timeperframe; const struct uvc_format *format; @@ -472,6 +486,10 @@ static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream, unsigned int i; int ret; + ret = uvc_acquire_privileges(handle); + if (ret < 0) + return ret; + if (parm->type != stream->type) return -EINVAL; @@ -573,6 +591,7 @@ static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream, * - VIDIOC_S_INPUT * - VIDIOC_S_PARM * - VIDIOC_S_FMT + * - VIDIOC_CREATE_BUFS * - VIDIOC_REQBUFS */ static int uvc_acquire_privileges(struct uvc_fh *handle) @@ -652,6 +671,8 @@ static int uvc_v4l2_release(struct file *file) uvc_dbg(stream->dev, CALLS, "%s\n", __func__); + uvc_ctrl_cleanup_fh(handle); + /* Only free resources if this is a privileged handle. */ if (uvc_has_privileges(handle)) uvc_queue_release(&stream->queue); @@ -685,11 +706,13 @@ static int uvc_ioctl_querycap(struct file *file, void *fh, return 0; } -static int uvc_ioctl_enum_fmt(struct uvc_streaming *stream, +static int uvc_ioctl_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *fmt) { - const struct uvc_format *format; + struct uvc_fh *handle = fh; + struct uvc_streaming *stream = handle->stream; enum v4l2_buf_type type = fmt->type; + const struct uvc_format *format; u32 index = fmt->index; if (fmt->type != stream->type || fmt->index >= stream->nformats) @@ -707,82 +730,8 @@ static int uvc_ioctl_enum_fmt(struct uvc_streaming *stream, return 0; } -static int uvc_ioctl_enum_fmt_vid_cap(struct file *file, void *fh, - struct v4l2_fmtdesc *fmt) -{ - struct uvc_fh *handle = fh; - struct uvc_streaming *stream = handle->stream; - - return uvc_ioctl_enum_fmt(stream, fmt); -} - -static int uvc_ioctl_enum_fmt_vid_out(struct file *file, void *fh, - struct v4l2_fmtdesc *fmt) -{ - struct uvc_fh *handle = fh; - struct uvc_streaming *stream = handle->stream; - - return uvc_ioctl_enum_fmt(stream, fmt); -} - -static int uvc_ioctl_g_fmt_vid_cap(struct file *file, void *fh, - struct v4l2_format *fmt) -{ - struct uvc_fh *handle = fh; - struct uvc_streaming *stream = handle->stream; - - return uvc_v4l2_get_format(stream, fmt); -} - -static int uvc_ioctl_g_fmt_vid_out(struct file *file, void *fh, - struct v4l2_format *fmt) -{ - struct uvc_fh *handle = fh; - struct uvc_streaming *stream = handle->stream; - - return uvc_v4l2_get_format(stream, fmt); -} - -static int uvc_ioctl_s_fmt_vid_cap(struct file *file, void *fh, - struct v4l2_format *fmt) -{ - struct uvc_fh *handle = fh; - struct uvc_streaming *stream = handle->stream; - int ret; - - ret = uvc_acquire_privileges(handle); - if (ret < 0) - return ret; - - return uvc_v4l2_set_format(stream, fmt); -} - -static int uvc_ioctl_s_fmt_vid_out(struct file *file, void *fh, - struct v4l2_format *fmt) -{ - struct uvc_fh *handle = fh; - struct uvc_streaming *stream = handle->stream; - int ret; - - ret = uvc_acquire_privileges(handle); - if (ret < 0) - return ret; - - return uvc_v4l2_set_format(stream, fmt); -} - -static int uvc_ioctl_try_fmt_vid_cap(struct file *file, void *fh, - struct v4l2_format *fmt) -{ - struct uvc_fh *handle = fh; - struct uvc_streaming *stream = handle->stream; - struct uvc_streaming_control probe; - - return uvc_v4l2_try_format(stream, fmt, &probe, NULL, NULL); -} - -static int uvc_ioctl_try_fmt_vid_out(struct file *file, void *fh, - struct v4l2_format *fmt) +static int uvc_ioctl_try_fmt(struct file *file, void *fh, + struct v4l2_format *fmt) { struct uvc_fh *handle = fh; struct uvc_streaming *stream = handle->stream; @@ -1212,29 +1161,6 @@ static int uvc_ioctl_g_selection(struct file *file, void *fh, return 0; } -static int uvc_ioctl_g_parm(struct file *file, void *fh, - struct v4l2_streamparm *parm) -{ - struct uvc_fh *handle = fh; - struct uvc_streaming *stream = handle->stream; - - return uvc_v4l2_get_streamparm(stream, parm); -} - -static int uvc_ioctl_s_parm(struct file *file, void *fh, - struct v4l2_streamparm *parm) -{ - struct uvc_fh *handle = fh; - struct uvc_streaming *stream = handle->stream; - int ret; - - ret = uvc_acquire_privileges(handle); - if (ret < 0) - return ret; - - return uvc_v4l2_set_streamparm(stream, parm); -} - static int uvc_ioctl_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize) { @@ -1543,15 +1469,17 @@ static unsigned long uvc_v4l2_get_unmapped_area(struct file *file, #endif const struct v4l2_ioctl_ops uvc_ioctl_ops = { + .vidioc_g_fmt_vid_cap = uvc_ioctl_g_fmt, + .vidioc_g_fmt_vid_out = uvc_ioctl_g_fmt, + .vidioc_s_fmt_vid_cap = uvc_ioctl_s_fmt, + .vidioc_s_fmt_vid_out = uvc_ioctl_s_fmt, + .vidioc_g_parm = uvc_ioctl_g_parm, + .vidioc_s_parm = uvc_ioctl_s_parm, .vidioc_querycap = uvc_ioctl_querycap, - .vidioc_enum_fmt_vid_cap = uvc_ioctl_enum_fmt_vid_cap, - .vidioc_enum_fmt_vid_out = uvc_ioctl_enum_fmt_vid_out, - .vidioc_g_fmt_vid_cap = uvc_ioctl_g_fmt_vid_cap, - .vidioc_g_fmt_vid_out = uvc_ioctl_g_fmt_vid_out, - .vidioc_s_fmt_vid_cap = uvc_ioctl_s_fmt_vid_cap, - .vidioc_s_fmt_vid_out = uvc_ioctl_s_fmt_vid_out, - .vidioc_try_fmt_vid_cap = uvc_ioctl_try_fmt_vid_cap, - .vidioc_try_fmt_vid_out = uvc_ioctl_try_fmt_vid_out, + .vidioc_enum_fmt_vid_cap = uvc_ioctl_enum_fmt, + .vidioc_enum_fmt_vid_out = uvc_ioctl_enum_fmt, + .vidioc_try_fmt_vid_cap = uvc_ioctl_try_fmt, + .vidioc_try_fmt_vid_out = uvc_ioctl_try_fmt, .vidioc_reqbufs = uvc_ioctl_reqbufs, .vidioc_querybuf = uvc_ioctl_querybuf, .vidioc_qbuf = uvc_ioctl_qbuf, @@ -1570,8 +1498,6 @@ const struct v4l2_ioctl_ops uvc_ioctl_ops = { .vidioc_try_ext_ctrls = uvc_ioctl_try_ext_ctrls, .vidioc_querymenu = uvc_ioctl_querymenu, .vidioc_g_selection = uvc_ioctl_g_selection, - .vidioc_g_parm = uvc_ioctl_g_parm, - .vidioc_s_parm = uvc_ioctl_s_parm, .vidioc_enum_framesizes = uvc_ioctl_enum_framesizes, .vidioc_enum_frameintervals = uvc_ioctl_enum_frameintervals, .vidioc_subscribe_event = uvc_ioctl_subscribe_event, diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index e00f38dd07d9..e3567aeb0007 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -20,6 +20,7 @@ #include <linux/atomic.h> #include <linux/unaligned.h> +#include <media/jpeg.h> #include <media/v4l2-common.h> #include "uvcvideo.h" @@ -79,6 +80,27 @@ int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit, if (likely(ret == size)) return 0; + /* + * Some devices return shorter USB control packets than expected if the + * returned value can fit in less bytes. Zero all the bytes that the + * device has not written. + * + * This quirk is applied to all controls, regardless of their data type. + * Most controls are little-endian integers, in which case the missing + * bytes become 0 MSBs. For other data types, a different heuristic + * could be implemented if a device is found needing it. + * + * We exclude UVC_GET_INFO from the quirk. UVC_GET_LEN does not need + * to be excluded because its size is always 1. + */ + if (ret > 0 && query != UVC_GET_INFO) { + memset(data + ret, 0, size - ret); + dev_warn_once(&dev->udev->dev, + "UVC non compliance: %s control %u on unit %u returned %d bytes when we expected %u.\n", + uvc_query_name(query), cs, unit, ret, size); + return 0; + } + if (ret != -EPIPE) { dev_err(&dev->udev->dev, "Failed to query (%s) UVC control %u on unit %u: %d (exp. %u).\n", @@ -96,8 +118,12 @@ int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit, error = *(u8 *)data; *(u8 *)data = tmp; - if (ret != 1) + if (ret != 1) { + dev_err_ratelimited(&dev->udev->dev, + "Failed to query (%s) UVC error code control %u on unit %u: %d (exp. 1).\n", + uvc_query_name(query), cs, unit, ret); return ret < 0 ? ret : -EPIPE; + } uvc_dbg(dev, CONTROL, "Control error %u\n", error); @@ -297,8 +323,9 @@ static int uvc_get_video_ctrl(struct uvc_streaming *stream, goto out; } else if (ret != size) { dev_err(&stream->intf->dev, - "Failed to query (%u) UVC %s control : %d (exp. %u).\n", - query, probe ? "probe" : "commit", ret, size); + "Failed to query (%s) UVC %s control : %d (exp. %u).\n", + uvc_query_name(query), probe ? "probe" : "commit", + ret, size); ret = (ret == -EPROTO) ? -EPROTO : -EIO; goto out; } @@ -1116,6 +1143,7 @@ static void uvc_video_stats_stop(struct uvc_streaming *stream) static int uvc_video_decode_start(struct uvc_streaming *stream, struct uvc_buffer *buf, const u8 *data, int len) { + u8 header_len; u8 fid; /* @@ -1129,6 +1157,7 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, return -EINVAL; } + header_len = data[0]; fid = data[1] & UVC_STREAM_FID; /* @@ -1210,9 +1239,31 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, return -EAGAIN; } + /* + * Some cameras, when running two parallel streams (one MJPEG alongside + * another non-MJPEG stream), are known to lose the EOF packet for a frame. + * We can detect the end of a frame by checking for a new SOI marker, as + * the SOI always lies on the packet boundary between two frames for + * these devices. + */ + if (stream->dev->quirks & UVC_QUIRK_MJPEG_NO_EOF && + (stream->cur_format->fcc == V4L2_PIX_FMT_MJPEG || + stream->cur_format->fcc == V4L2_PIX_FMT_JPEG)) { + const u8 *packet = data + header_len; + + if (len >= header_len + 2 && + packet[0] == 0xff && packet[1] == JPEG_MARKER_SOI && + buf->bytesused != 0) { + buf->state = UVC_BUF_STATE_READY; + buf->error = 1; + stream->last_fid ^= UVC_STREAM_FID; + return -EAGAIN; + } + } + stream->last_fid = fid; - return data[0]; + return header_len; } static inline enum dma_data_direction uvc_stream_dir( diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index 07f9921d83f2..5e388f05f3fc 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -76,6 +76,7 @@ #define UVC_QUIRK_NO_RESET_RESUME 0x00004000 #define UVC_QUIRK_DISABLE_AUTOSUSPEND 0x00008000 #define UVC_QUIRK_INVALID_DEVICE_SOF 0x00010000 +#define UVC_QUIRK_MJPEG_NO_EOF 0x00020000 /* Format flags */ #define UVC_FMT_FLAG_COMPRESSED 0x00000001 @@ -234,6 +235,7 @@ struct uvc_entity { u8 *bmControls; struct gpio_desc *gpio_privacy; int irq; + bool initialized; } gpio; }; @@ -316,7 +318,6 @@ struct uvc_buffer { }; #define UVC_QUEUE_DISCONNECTED (1 << 0) -#define UVC_QUEUE_DROP_CORRUPTED (1 << 1) struct uvc_video_queue { struct vb2_queue queue; @@ -337,7 +338,11 @@ struct uvc_video_chain { struct uvc_entity *processing; /* Processing unit */ struct uvc_entity *selector; /* Selector unit */ - struct mutex ctrl_mutex; /* Protects ctrl.info */ + struct mutex ctrl_mutex; /* + * Protects ctrl.info, + * ctrl.handle and + * uvc_fh.pending_async_ctrls + */ struct v4l2_prio_state prio; /* V4L2 priority state */ u32 caps; /* V4L2 chain-wide caps */ @@ -612,6 +617,7 @@ struct uvc_fh { struct uvc_video_chain *chain; struct uvc_streaming *stream; enum uvc_handle_state state; + unsigned int pending_async_ctrls; }; struct uvc_driver { @@ -674,8 +680,7 @@ extern struct uvc_driver uvc_driver; struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id); /* Video buffers queue management. */ -int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type, - int drop_corrupted); +int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type); void uvc_queue_release(struct uvc_video_queue *queue); int uvc_request_buffers(struct uvc_video_queue *queue, struct v4l2_requestbuffers *rb); @@ -797,6 +802,8 @@ int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id, int uvc_xu_ctrl_query(struct uvc_video_chain *chain, struct uvc_xu_control_query *xqry); +void uvc_ctrl_cleanup_fh(struct uvc_fh *handle); + /* Utility functions */ struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts, u8 epaddr); diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index f19c8adf2c61..cb153ce42c45 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -127,7 +127,7 @@ static int v4l2_fwnode_endpoint_parse_csi2_bus(struct fwnode_handle *fwnode, { struct v4l2_mbus_config_mipi_csi2 *bus = &vep->bus.mipi_csi2; bool have_clk_lane = false, have_data_lanes = false, - have_lane_polarities = false; + have_lane_polarities = false, have_line_orders = false; unsigned int flags = 0, lanes_used = 0; u32 array[1 + V4L2_MBUS_CSI2_MAX_DATA_LANES]; u32 clock_lane = 0; @@ -197,6 +197,17 @@ static int v4l2_fwnode_endpoint_parse_csi2_bus(struct fwnode_handle *fwnode, have_lane_polarities = true; } + rval = fwnode_property_count_u32(fwnode, "line-orders"); + if (rval > 0) { + if (rval != num_data_lanes) { + pr_warn("invalid number of line-orders entries (need %u, got %u)\n", + num_data_lanes, rval); + return -EINVAL; + } + + have_line_orders = true; + } + if (!fwnode_property_read_u32(fwnode, "clock-lanes", &v)) { clock_lane = v; pr_debug("clock lane position %u\n", v); @@ -250,6 +261,36 @@ static int v4l2_fwnode_endpoint_parse_csi2_bus(struct fwnode_handle *fwnode, } else { pr_debug("no lane polarities defined, assuming not inverted\n"); } + + if (have_line_orders) { + fwnode_property_read_u32_array(fwnode, + "line-orders", array, + num_data_lanes); + + for (i = 0; i < num_data_lanes; i++) { + static const char * const orders[] = { + "ABC", "ACB", "BAC", "BCA", "CAB", "CBA" + }; + + if (array[i] >= ARRAY_SIZE(orders)) { + pr_warn("lane %u invalid line-order assuming ABC (got %u)\n", + i, array[i]); + bus->line_orders[i] = + V4L2_MBUS_CSI2_CPHY_LINE_ORDER_ABC; + continue; + } + + bus->line_orders[i] = array[i]; + pr_debug("lane %u line order %s", i, + orders[array[i]]); + } + } else { + for (i = 0; i < num_data_lanes; i++) + bus->line_orders[i] = + V4L2_MBUS_CSI2_CPHY_LINE_ORDER_ABC; + + pr_debug("no line orders defined, assuming ABC\n"); + } } return 0; diff --git a/drivers/media/v4l2-core/v4l2-mc.c b/drivers/media/v4l2-core/v4l2-mc.c index 4bb91359e3a9..937d358697e1 100644 --- a/drivers/media/v4l2-core/v4l2-mc.c +++ b/drivers/media/v4l2-core/v4l2-mc.c @@ -329,7 +329,7 @@ int v4l2_create_fwnode_links_to_pad(struct v4l2_subdev *src_sd, if (!(sink->flags & MEDIA_PAD_FL_SINK)) return -EINVAL; - fwnode_graph_for_each_endpoint(dev_fwnode(src_sd->dev), endpoint) { + fwnode_graph_for_each_endpoint(src_sd->fwnode, endpoint) { struct fwnode_handle *remote_ep; int src_idx, sink_idx, ret; struct media_pad *src; diff --git a/drivers/soc/mediatek/mtk-cmdq-helper.c b/drivers/soc/mediatek/mtk-cmdq-helper.c index 0a05ee87a0fc..455221e8de24 100644 --- a/drivers/soc/mediatek/mtk-cmdq-helper.c +++ b/drivers/soc/mediatek/mtk-cmdq-helper.c @@ -524,23 +524,5 @@ int cmdq_pkt_eoc(struct cmdq_pkt *pkt) } EXPORT_SYMBOL(cmdq_pkt_eoc); -int cmdq_pkt_finalize(struct cmdq_pkt *pkt) -{ - struct cmdq_instruction inst = { {0} }; - int err; - - /* insert EOC and generate IRQ for each command iteration */ - err = cmdq_pkt_eoc(pkt); - if (err < 0) - return err; - - /* JUMP to end */ - inst.op = CMDQ_CODE_JUMP; - inst.value = CMDQ_JUMP_PASS >> - cmdq_get_shift_pa(((struct cmdq_client *)pkt->cl)->chan); - return cmdq_pkt_append_command(pkt, inst); -} -EXPORT_SYMBOL(cmdq_pkt_finalize); - MODULE_DESCRIPTION("MediaTek Command Queue (CMDQ) driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/imx/imx-media-of.c b/drivers/staging/media/imx/imx-media-of.c index 118bff988bc7..bb28daa4d713 100644 --- a/drivers/staging/media/imx/imx-media-of.c +++ b/drivers/staging/media/imx/imx-media-of.c @@ -54,22 +54,18 @@ int imx_media_add_of_subdevs(struct imx_media_dev *imxmd, break; ret = imx_media_of_add_csi(imxmd, csi_np); + of_node_put(csi_np); if (ret) { /* unavailable or already added is not an error */ if (ret == -ENODEV || ret == -EEXIST) { - of_node_put(csi_np); continue; } /* other error, can't continue */ - goto err_out; + return ret; } } return 0; - -err_out: - of_node_put(csi_np); - return ret; } EXPORT_SYMBOL_GPL(imx_media_add_of_subdevs); diff --git a/drivers/staging/media/max96712/max96712.c b/drivers/staging/media/max96712/max96712.c index ede02e8c891c..0751b2e04895 100644 --- a/drivers/staging/media/max96712/max96712.c +++ b/drivers/staging/media/max96712/max96712.c @@ -418,7 +418,6 @@ static int max96712_probe(struct i2c_client *client) priv->info = of_device_get_match_data(&client->dev); priv->client = client; - i2c_set_clientdata(client, priv); priv->regmap = devm_regmap_init_i2c(client, &max96712_i2c_regmap); if (IS_ERR(priv->regmap)) @@ -448,7 +447,8 @@ static int max96712_probe(struct i2c_client *client) static void max96712_remove(struct i2c_client *client) { - struct max96712_priv *priv = i2c_get_clientdata(client); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct max96712_priv *priv = container_of(sd, struct max96712_priv, sd); v4l2_async_unregister_subdev(&priv->sd); |