diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-02-06 11:27:48 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-02-06 11:27:48 -0800 |
commit | 68c5735eaa5e680e701c9a2d1e3c7880bdf5ab66 (patch) | |
tree | 4f584693638bf257b66a1646cc30d823cacc0a58 /drivers/staging | |
parent | 2246edfaf88dc368e8671b04afd54412625df60a (diff) | |
parent | 273caa260035c03d89ad63d72d8cd3d9e5c5e3f1 (diff) | |
download | op-kernel-dev-68c5735eaa5e680e701c9a2d1e3c7880bdf5ab66.zip op-kernel-dev-68c5735eaa5e680e701c9a2d1e3c7880bdf5ab66.tar.gz |
Merge tag 'media/v4.16-2' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab:
- videobuf2 was moved to a media/common dir, as it is now used by the
DVB subsystem too
- Digital TV core memory mapped support interface
- new sensor driver: ov7740
- several improvements at ddbridge driver
- new V4L2 driver: IPU3 CIO2 CSI-2 receiver unit, found on some Intel
SoCs
- new tuner driver: tda18250
- finally got rid of all LIRC staging drivers
- as we don't have old lirc drivers anymore, restruct the lirc device
code
- add support for UVC metadata
- add a new staging driver for NVIDIA Tegra Video Decoder Engine
- DVB kAPI headers moved to include/media
- synchronize the kAPI and uAPI for the DVB subsystem, removing the gap
for non-legacy APIs
- reduce the kAPI gap for V4L2
- lots of other driver enhancements, cleanups, etc.
* tag 'media/v4.16-2' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (407 commits)
media: v4l2-compat-ioctl32.c: make ctrl_is_pointer work for subdevs
media: v4l2-compat-ioctl32.c: refactor compat ioctl32 logic
media: v4l2-compat-ioctl32.c: don't copy back the result for certain errors
media: v4l2-compat-ioctl32.c: drop pr_info for unknown buffer type
media: v4l2-compat-ioctl32.c: copy clip list in put_v4l2_window32
media: v4l2-compat-ioctl32.c: fix ctrl_is_pointer
media: v4l2-compat-ioctl32.c: copy m.userptr in put_v4l2_plane32
media: v4l2-compat-ioctl32.c: avoid sizeof(type)
media: v4l2-compat-ioctl32.c: move 'helper' functions to __get/put_v4l2_format32
media: v4l2-compat-ioctl32.c: fix the indentation
media: v4l2-compat-ioctl32.c: add missing VIDIOC_PREPARE_BUF
media: v4l2-ioctl.c: don't copy back the result for -ENOTTY
media: v4l2-ioctl.c: use check_fmt for enum/g/s/try_fmt
media: vivid: fix module load error when enabling fb and no_error_inj=1
media: dvb_demux: improve debug messages
media: dvb_demux: Better handle discontinuity errors
media: cxusb, dib0700: ignore XC2028_I2C_FLUSH
media: ts2020: avoid integer overflows on 32 bit machines
media: i2c: ov7740: use gpio/consumer.h instead of gpio.h
media: entity: Add a nop variant of media_entity_cleanup
...
Diffstat (limited to 'drivers/staging')
49 files changed, 2281 insertions, 2767 deletions
diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index f8c25ee..e68e1d3 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -1,19 +1,19 @@ menuconfig STAGING_MEDIA - bool "Media staging drivers" - default n - ---help--- - This option allows you to select a number of media drivers that + bool "Media staging drivers" + default n + ---help--- + This option allows you to select a number of media drivers that don't have the "normal" Linux kernel quality level. Most of them don't follow properly the V4L, DVB and/or RC API's, so, they won't likely work fine with the existing applications. That also means that, once fixed, their API's will change to match the existing ones. - If you wish to work on these drivers, to help improve them, or - to report problems you have with them, please use the + If you wish to work on these drivers, to help improve them, or + to report problems you have with them, please use the linux-media@vger.kernel.org mailing list. - If in doubt, say N here. + If in doubt, say N here. if STAGING_MEDIA && MEDIA_SUPPORT @@ -31,7 +31,6 @@ source "drivers/staging/media/imx/Kconfig" source "drivers/staging/media/omap4iss/Kconfig" -# Keep LIRC at the end, as it has sub-menus -source "drivers/staging/media/lirc/Kconfig" +source "drivers/staging/media/tegra-vde/Kconfig" endif diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index be732cf..59a47f6 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -2,7 +2,7 @@ obj-$(CONFIG_I2C_BCM2048) += bcm2048/ obj-$(CONFIG_DVB_CXD2099) += cxd2099/ obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx/ -obj-$(CONFIG_LIRC_STAGING) += lirc/ obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ obj-$(CONFIG_INTEL_ATOMISP) += atomisp/ +obj-$(CONFIG_TEGRA_VDE) += tegra-vde/ diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c index e70d8af..61b7598 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c @@ -1370,13 +1370,9 @@ static int gc0310_probe(struct i2c_client *client) dev->fmt_idx = 0; v4l2_i2c_subdev_init(&(dev->sd), client, &gc0310_ops); - if (ACPI_COMPANION(&client->dev)) - pdata = gmin_camera_platform_data(&dev->sd, - ATOMISP_INPUT_FORMAT_RAW_8, - atomisp_bayer_order_grbg); - else - pdata = client->dev.platform_data; - + pdata = gmin_camera_platform_data(&dev->sd, + ATOMISP_INPUT_FORMAT_RAW_8, + atomisp_bayer_order_grbg); if (!pdata) { ret = -EINVAL; goto out_free; diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c index 85da5fe..d8de46d 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c @@ -1108,9 +1108,7 @@ static int gc2235_probe(struct i2c_client *client) dev->fmt_idx = 0; v4l2_i2c_subdev_init(&(dev->sd), client, &gc2235_ops); - gcpdev = client->dev.platform_data; - if (ACPI_COMPANION(&client->dev)) - gcpdev = gmin_camera_platform_data(&dev->sd, + gcpdev = gmin_camera_platform_data(&dev->sd, ATOMISP_INPUT_FORMAT_RAW_10, atomisp_bayer_order_grbg); @@ -1147,10 +1145,8 @@ static int gc2235_probe(struct i2c_client *client) if (ret) gc2235_remove(client); - if (ACPI_HANDLE(&client->dev)) - ret = atomisp_register_i2c_module(&dev->sd, gcpdev, RAW_CAMERA); + return atomisp_register_i2c_module(&dev->sd, gcpdev, RAW_CAMERA); - return ret; out_free: v4l2_device_unregister_subdev(&dev->sd); kfree(dev); diff --git a/drivers/staging/media/atomisp/i2c/atomisp-lm3554.c b/drivers/staging/media/atomisp/i2c/atomisp-lm3554.c index 4fd9f53..7098bf3 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-lm3554.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-lm3554.c @@ -562,10 +562,10 @@ static const struct v4l2_ctrl_config lm3554_controls[] = { { .ops = &ctrl_ops, .id = V4L2_CID_FLASH_STATUS, - .type = V4L2_CTRL_TYPE_BOOLEAN, + .type = V4L2_CTRL_TYPE_INTEGER, .name = "Flash Status", - .min = 0, - .max = 100, + .min = ATOMISP_FLASH_STATUS_OK, + .max = ATOMISP_FLASH_STATUS_TIMEOUT, .step = 1, .def = ATOMISP_FLASH_STATUS_OK, .flags = 0, @@ -574,10 +574,10 @@ static const struct v4l2_ctrl_config lm3554_controls[] = { { .ops = &ctrl_ops, .id = V4L2_CID_FLASH_STATUS_REGISTER, - .type = V4L2_CTRL_TYPE_BOOLEAN, + .type = V4L2_CTRL_TYPE_INTEGER, .name = "Flash Status Register", .min = 0, - .max = 100, + .max = 255, .step = 1, .def = 0, .flags = 0, @@ -824,22 +824,15 @@ static void *lm3554_platform_data_func(struct i2c_client *client) { static struct lm3554_platform_data platform_data; - if (ACPI_COMPANION(&client->dev)) { - platform_data.gpio_reset = - desc_to_gpio(gpiod_get_index(&(client->dev), + platform_data.gpio_reset = + desc_to_gpio(gpiod_get_index(&client->dev, NULL, 2, GPIOD_OUT_LOW)); - platform_data.gpio_strobe = - desc_to_gpio(gpiod_get_index(&(client->dev), + platform_data.gpio_strobe = + desc_to_gpio(gpiod_get_index(&client->dev, NULL, 0, GPIOD_OUT_LOW)); - platform_data.gpio_torch = - desc_to_gpio(gpiod_get_index(&(client->dev), + platform_data.gpio_torch = + desc_to_gpio(gpiod_get_index(&client->dev, NULL, 1, GPIOD_OUT_LOW)); - } else { - platform_data.gpio_reset = -1; - platform_data.gpio_strobe = -1; - platform_data.gpio_torch = -1; - } - dev_info(&client->dev, "camera pdata: lm3554: reset: %d strobe %d torch %d\n", platform_data.gpio_reset, platform_data.gpio_strobe, platform_data.gpio_torch); @@ -868,10 +861,7 @@ static int lm3554_probe(struct i2c_client *client) if (!flash) return -ENOMEM; - flash->pdata = client->dev.platform_data; - - if (!flash->pdata || ACPI_COMPANION(&client->dev)) - flash->pdata = lm3554_platform_data_func(client); + flash->pdata = lm3554_platform_data_func(client); v4l2_i2c_subdev_init(&flash->sd, client, &lm3554_ops); flash->sd.internal_ops = &lm3554_internal_ops; @@ -914,9 +904,7 @@ static int lm3554_probe(struct i2c_client *client) dev_err(&client->dev, "gpio request/direction_output fail"); goto fail2; } - if (ACPI_HANDLE(&client->dev)) - err = atomisp_register_i2c_module(&flash->sd, NULL, LED_FLASH); - return 0; + return atomisp_register_i2c_module(&flash->sd, NULL, LED_FLASH); fail2: media_entity_cleanup(&flash->sd.entity); v4l2_ctrl_handler_free(&flash->ctrl_handler); diff --git a/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c b/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c index 55882be..df253a5 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c @@ -1844,11 +1844,9 @@ static int mt9m114_probe(struct i2c_client *client) return -ENOMEM; v4l2_i2c_subdev_init(&dev->sd, client, &mt9m114_ops); - pdata = client->dev.platform_data; - if (ACPI_COMPANION(&client->dev)) - pdata = gmin_camera_platform_data(&dev->sd, - ATOMISP_INPUT_FORMAT_RAW_10, - atomisp_bayer_order_grbg); + pdata = gmin_camera_platform_data(&dev->sd, + ATOMISP_INPUT_FORMAT_RAW_10, + atomisp_bayer_order_grbg); if (pdata) ret = mt9m114_s_config(&dev->sd, client->irq, pdata); if (!pdata || ret) { diff --git a/drivers/staging/media/atomisp/i2c/atomisp-ov2680.c b/drivers/staging/media/atomisp/i2c/atomisp-ov2680.c index cd67d38..84f8d33 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-ov2680.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-ov2680.c @@ -1447,13 +1447,9 @@ static int ov2680_probe(struct i2c_client *client) dev->fmt_idx = 0; v4l2_i2c_subdev_init(&(dev->sd), client, &ov2680_ops); - if (ACPI_COMPANION(&client->dev)) - pdata = gmin_camera_platform_data(&dev->sd, - ATOMISP_INPUT_FORMAT_RAW_10, - atomisp_bayer_order_bggr); - else - pdata = client->dev.platform_data; - + pdata = gmin_camera_platform_data(&dev->sd, + ATOMISP_INPUT_FORMAT_RAW_10, + atomisp_bayer_order_bggr); if (!pdata) { ret = -EINVAL; goto out_free; diff --git a/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c b/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c index 4df7eba..2b6ae0f 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c @@ -1259,7 +1259,6 @@ static int ov2722_probe(struct i2c_client *client) struct ov2722_device *dev; void *ovpdev; int ret; - struct acpi_device *adev; dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) @@ -1270,14 +1269,9 @@ static int ov2722_probe(struct i2c_client *client) dev->fmt_idx = 0; v4l2_i2c_subdev_init(&(dev->sd), client, &ov2722_ops); - ovpdev = client->dev.platform_data; - adev = ACPI_COMPANION(&client->dev); - if (adev) { - adev->power.flags.power_resources = 0; - ovpdev = gmin_camera_platform_data(&dev->sd, - ATOMISP_INPUT_FORMAT_RAW_10, - atomisp_bayer_order_grbg); - } + ovpdev = gmin_camera_platform_data(&dev->sd, + ATOMISP_INPUT_FORMAT_RAW_10, + atomisp_bayer_order_grbg); ret = ov2722_s_config(&dev->sd, client->irq, ovpdev); if (ret) @@ -1296,10 +1290,7 @@ static int ov2722_probe(struct i2c_client *client) if (ret) ov2722_remove(client); - if (ACPI_HANDLE(&client->dev)) - ret = atomisp_register_i2c_module(&dev->sd, ovpdev, RAW_CAMERA); - - return ret; + return atomisp_register_i2c_module(&dev->sd, ovpdev, RAW_CAMERA); out_ctrl_handler_free: v4l2_ctrl_handler_free(&dev->ctrl_handler); diff --git a/drivers/staging/media/atomisp/i2c/ov2680.h b/drivers/staging/media/atomisp/i2c/ov2680.h index bf48973..03f75dd 100644 --- a/drivers/staging/media/atomisp/i2c/ov2680.h +++ b/drivers/staging/media/atomisp/i2c/ov2680.h @@ -174,7 +174,6 @@ struct ov2680_format { struct mutex input_lock; struct v4l2_ctrl_handler ctrl_handler; struct camera_sensor_platform_data *platform_data; - struct timespec timestamp_t_focus_abs; int vt_pix_clk_freq_mhz; int fmt_idx; int run_mode; diff --git a/drivers/staging/media/atomisp/i2c/ov5693/atomisp-ov5693.c b/drivers/staging/media/atomisp/i2c/ov5693/atomisp-ov5693.c index 3e7c385..40d01bf 100644 --- a/drivers/staging/media/atomisp/i2c/ov5693/atomisp-ov5693.c +++ b/drivers/staging/media/atomisp/i2c/ov5693/atomisp-ov5693.c @@ -82,6 +82,7 @@ static int ad5823_i2c_write(struct i2c_client *client, u8 reg, u8 val) { struct i2c_msg msg; u8 buf[2]; + buf[0] = reg; buf[1] = val; msg.addr = AD5823_VCM_ADDR; @@ -98,6 +99,7 @@ static int ad5823_i2c_read(struct i2c_client *client, u8 reg, u8 *val) { struct i2c_msg msg[2]; u8 buf[2]; + buf[0] = reg; buf[1] = 0; @@ -211,7 +213,8 @@ static int vcm_dw_i2c_write(struct i2c_client *client, u16 data) return ret == num_msg ? 0 : -EIO; } -/* Theory: per datasheet, the two VCMs both allow for a 2-byte read. +/* + * Theory: per datasheet, the two VCMs both allow for a 2-byte read. * The DW9714 doesn't actually specify what this does (it has a * two-byte write-only protocol, but specifies the read sequence as * legal), but it returns the same data (zeroes) always, after an @@ -222,12 +225,14 @@ static int vcm_dw_i2c_write(struct i2c_client *client, u16 data) * these) in AD5823 are not pairwise repetitions of the same 16 bit * word. So all we have to do is sequentially read two bytes at a * time and see if we detect a difference in any of the first four - * pairs. */ + * pairs. + */ static int vcm_detect(struct i2c_client *client) { int i, ret; struct i2c_msg msg; u16 data0 = 0, data; + for (i = 0; i < 4; i++) { msg.addr = VCM_ADDR; msg.flags = I2C_M_RD; @@ -235,8 +240,10 @@ static int vcm_detect(struct i2c_client *client) msg.buf = (u8 *)&data; ret = i2c_transfer(client->adapter, &msg, 1); - /* DW9714 always fails the first read and returns - * zeroes for subsequent ones */ + /* + * DW9714 always fails the first read and returns + * zeroes for subsequent ones + */ if (i == 0 && ret == -EREMOTEIO) { data0 = 0; continue; @@ -530,9 +537,11 @@ static long __ov5693_set_exposure(struct v4l2_subdev *sd, int coarse_itg, hts = ov5693_res[dev->fmt_idx].pixels_per_line; vts = ov5693_res[dev->fmt_idx].lines_per_frame; - /*If coarse_itg is larger than 1<<15, can not write to reg directly. - The way is to write coarse_itg/2 to the reg, meanwhile write 2*hts - to the reg. */ + /* + * If coarse_itg is larger than 1<<15, can not write to reg directly. + * The way is to write coarse_itg/2 to the reg, meanwhile write 2*hts + * to the reg. + */ if (coarse_itg > (1 << 15)) { hts = hts * 2; coarse_itg = (int)coarse_itg / 2; @@ -690,6 +699,7 @@ static long ov5693_s_exposure(struct v4l2_subdev *sd, /* we should not accept the invalid value below */ if (analog_gain == 0) { struct i2c_client *client = v4l2_get_subdevdata(sd); + v4l2_err(client, "%s: invalid value\n", __func__); return -EINVAL; } @@ -722,6 +732,7 @@ static int __ov5693_otp_read(struct v4l2_subdev *sd, u8 *buf) int ret; int i; u8 *b = buf; + dev->otp_size = 0; for (i = 1; i < OV5693_OTP_BANK_MAX; i++) { /*set bank NO and OTP read mode. */ @@ -753,7 +764,7 @@ static int __ov5693_otp_read(struct v4l2_subdev *sd, u8 *buf) //pr_debug("BANK[%2d] %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", i, *b, *(b+1), *(b+2), *(b+3), *(b+4), *(b+5), *(b+6), *(b+7), *(b+8), *(b+9), *(b+10), *(b+11), *(b+12), *(b+13), *(b+14), *(b+15)); //Intel OTP map, try to read 320byts first. - if (21 == i) { + if (i == 21) { if ((*b) == 0) { dev->otp_size = 320; break; @@ -761,15 +772,15 @@ static int __ov5693_otp_read(struct v4l2_subdev *sd, u8 *buf) b = buf; continue; } - } else if (24 == i) { //if the first 320bytes data doesn't not exist, try to read the next 32bytes data. + } else if (i == 24) { //if the first 320bytes data doesn't not exist, try to read the next 32bytes data. if ((*b) == 0) { dev->otp_size = 32; break; - } else { + } else { b = buf; continue; } - } else if (27 == i) { //if the prvious 32bytes data doesn't exist, try to read the next 32bytes data again. + } else if (i == 27) { //if the prvious 32bytes data doesn't exist, try to read the next 32bytes data again. if ((*b) == 0) { dev->otp_size = 32; break; @@ -875,8 +886,10 @@ static long ov5693_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) return 0; } -/* This returns the exposure time being used. This should only be used - for filling in EXIF data, not for actual image processing. */ +/* + * This returns the exposure time being used. This should only be used + * for filling in EXIF data, not for actual image processing. + */ static int ov5693_q_exposure(struct v4l2_subdev *sd, s32 *value) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -973,7 +986,7 @@ static int ov5693_t_focus_abs(struct v4l2_subdev *sd, s32 value) if (ret == 0) { dev->number_of_steps = value - dev->focus; dev->focus = value; - getnstimeofday(&(dev->timestamp_t_focus_abs)); + dev->timestamp_t_focus_abs = ktime_get(); } else dev_err(&client->dev, "%s: i2c failed. ret %d\n", __func__, ret); @@ -984,6 +997,7 @@ static int ov5693_t_focus_abs(struct v4l2_subdev *sd, s32 value) static int ov5693_t_focus_rel(struct v4l2_subdev *sd, s32 value) { struct ov5693_device *dev = to_ov5693_sensor(sd); + return ov5693_t_focus_abs(sd, dev->focus + value); } @@ -993,16 +1007,13 @@ static int ov5693_q_focus_status(struct v4l2_subdev *sd, s32 *value) { u32 status = 0; struct ov5693_device *dev = to_ov5693_sensor(sd); - struct timespec temptime; - const struct timespec timedelay = { - 0, - min((u32)abs(dev->number_of_steps) * DELAY_PER_STEP_NS, - (u32)DELAY_MAX_PER_STEP_NS), - }; - - getnstimeofday(&temptime); - temptime = timespec_sub(temptime, (dev->timestamp_t_focus_abs)); - if (timespec_compare(&temptime, &timedelay) <= 0) { + ktime_t temptime; + ktime_t timedelay = ns_to_ktime(min_t(u32, + abs(dev->number_of_steps) * DELAY_PER_STEP_NS, + DELAY_MAX_PER_STEP_NS)); + + temptime = ktime_sub(ktime_get(), (dev->timestamp_t_focus_abs)); + if (ktime_compare(temptime, timedelay) <= 0) { status |= ATOMISP_FOCUS_STATUS_MOVING; status |= ATOMISP_FOCUS_HP_IN_PROGRESS; } else { @@ -1033,6 +1044,7 @@ static int ov5693_q_focus_abs(struct v4l2_subdev *sd, s32 *value) static int ov5693_t_vcm_slew(struct v4l2_subdev *sd, s32 value) { struct ov5693_device *dev = to_ov5693_sensor(sd); + dev->number_of_steps = value; dev->vcm_update = true; return 0; @@ -1041,6 +1053,7 @@ static int ov5693_t_vcm_slew(struct v4l2_subdev *sd, s32 value) static int ov5693_t_vcm_timing(struct v4l2_subdev *sd, s32 value) { struct ov5693_device *dev = to_ov5693_sensor(sd); + dev->number_of_steps = value; dev->vcm_update = true; return 0; @@ -1293,11 +1306,13 @@ static int power_ctrl(struct v4l2_subdev *sd, bool flag) if (!dev || !dev->platform_data) return -ENODEV; - /* This driver assumes "internal DVDD, PWDNB tied to DOVDD". + /* + * This driver assumes "internal DVDD, PWDNB tied to DOVDD". * In this set up only gpio0 (XSHUTDN) should be available * but in some products (for example ECS) gpio1 (PWDNB) is * also available. If gpio1 is available we emulate it being - * tied to DOVDD here. */ + * tied to DOVDD here. + */ if (flag) { ret = dev->platform_data->v2p8_ctrl(sd, 1); dev->platform_data->gpio1_ctrl(sd, 1); @@ -1333,7 +1348,7 @@ static int __power_up(struct v4l2_subdev *sd) struct i2c_client *client = v4l2_get_subdevdata(sd); int ret; - if (NULL == dev->platform_data) { + if (!dev->platform_data) { dev_err(&client->dev, "no camera_sensor_platform_data"); return -ENODEV; @@ -1381,7 +1396,7 @@ static int power_down(struct v4l2_subdev *sd) int ret = 0; dev->focus = OV5693_INVALID_CONFIG; - if (NULL == dev->platform_data) { + if (!dev->platform_data) { dev_err(&client->dev, "no camera_sensor_platform_data"); return -ENODEV; @@ -1563,6 +1578,7 @@ static int ov5693_set_fmt(struct v4l2_subdev *sd, struct camera_mipi_info *ov5693_info = NULL; int ret = 0; int idx; + if (format->pad) return -EINVAL; if (!fmt) @@ -1599,6 +1615,7 @@ static int ov5693_set_fmt(struct v4l2_subdev *sd, ret = startup(sd); if (ret) { int i = 0; + dev_err(&client->dev, "ov5693 startup err, retry to power up\n"); for (i = 0; i < OV5693_POWER_UP_RETRY_NUM; i++) { dev_err(&client->dev, @@ -1655,6 +1672,7 @@ static int ov5693_get_fmt(struct v4l2_subdev *sd, { struct v4l2_mbus_framefmt *fmt = &format->format; struct ov5693_device *dev = to_ov5693_sensor(sd); + if (format->pad) return -EINVAL; @@ -1818,6 +1836,7 @@ static int ov5693_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *param) { struct ov5693_device *dev = to_ov5693_sensor(sd); + dev->run_mode = param->parm.capture.capturemode; mutex_lock(&dev->input_lock); @@ -1907,6 +1926,7 @@ static int ov5693_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); struct ov5693_device *dev = to_ov5693_sensor(sd); + dev_dbg(&client->dev, "ov5693_remove...\n"); dev->platform_data->csi_cfg(sd, 0); @@ -1928,13 +1948,14 @@ static int ov5693_probe(struct i2c_client *client) int i2c; int ret = 0; void *pdata = client->dev.platform_data; - struct acpi_device *adev; unsigned int i; - /* Firmware workaround: Some modules use a "secondary default" + /* + * Firmware workaround: Some modules use a "secondary default" * address of 0x10 which doesn't appear on schematics, and * some BIOS versions haven't gotten the memo. Work around - * via config. */ + * via config. + */ i2c = gmin_get_var_int(&client->dev, "I2CAddr", -1); if (i2c != -1) { dev_info(&client->dev, @@ -1952,14 +1973,9 @@ static int ov5693_probe(struct i2c_client *client) dev->fmt_idx = 0; v4l2_i2c_subdev_init(&(dev->sd), client, &ov5693_ops); - adev = ACPI_COMPANION(&client->dev); - if (adev) { - adev->power.flags.power_resources = 0; - pdata = gmin_camera_platform_data(&dev->sd, - ATOMISP_INPUT_FORMAT_RAW_10, - atomisp_bayer_order_bggr); - } - + pdata = gmin_camera_platform_data(&dev->sd, + ATOMISP_INPUT_FORMAT_RAW_10, + atomisp_bayer_order_bggr); if (!pdata) goto out_free; diff --git a/drivers/staging/media/atomisp/i2c/ov5693/ov5693.h b/drivers/staging/media/atomisp/i2c/ov5693/ov5693.h index 2ea6380..68cfcb4 100644 --- a/drivers/staging/media/atomisp/i2c/ov5693/ov5693.h +++ b/drivers/staging/media/atomisp/i2c/ov5693/ov5693.h @@ -221,7 +221,7 @@ struct ov5693_device { struct v4l2_ctrl_handler ctrl_handler; struct camera_sensor_platform_data *platform_data; - struct timespec timestamp_t_focus_abs; + ktime_t timestamp_t_focus_abs; int vt_pix_clk_freq_mhz; int fmt_idx; int run_mode; diff --git a/drivers/staging/media/atomisp/i2c/ov8858.c b/drivers/staging/media/atomisp/i2c/ov8858.c index ba147ac..3cf8c71 100644 --- a/drivers/staging/media/atomisp/i2c/ov8858.c +++ b/drivers/staging/media/atomisp/i2c/ov8858.c @@ -2077,29 +2077,28 @@ static int ov8858_probe(struct i2c_client *client) v4l2_i2c_subdev_init(&(dev->sd), client, &ov8858_ops); - if (ACPI_COMPANION(&client->dev)) { - pdata = gmin_camera_platform_data(&dev->sd, - ATOMISP_INPUT_FORMAT_RAW_10, - atomisp_bayer_order_bggr); - if (!pdata) { - dev_err(&client->dev, - "%s: failed to get acpi platform data\n", - __func__); - goto out_free; - } - ret = ov8858_s_config(&dev->sd, client->irq, pdata); - if (ret) { - dev_err(&client->dev, - "%s: failed to set config\n", __func__); - goto out_free; - } - ret = atomisp_register_i2c_module(&dev->sd, pdata, RAW_CAMERA); - if (ret) { - dev_err(&client->dev, - "%s: failed to register subdev\n", __func__); - goto out_free; - } + pdata = gmin_camera_platform_data(&dev->sd, + ATOMISP_INPUT_FORMAT_RAW_10, + atomisp_bayer_order_bggr); + if (!pdata) { + dev_err(&client->dev, + "%s: failed to get acpi platform data\n", + __func__); + goto out_free; + } + ret = ov8858_s_config(&dev->sd, client->irq, pdata); + if (ret) { + dev_err(&client->dev, + "%s: failed to set config\n", __func__); + goto out_free; } + ret = atomisp_register_i2c_module(&dev->sd, pdata, RAW_CAMERA); + if (ret) { + dev_err(&client->dev, + "%s: failed to register subdev\n", __func__); + goto out_free; + } + /* * sd->name is updated with sensor driver name by the v4l2. * change it to sensor name in this case. diff --git a/drivers/staging/media/atomisp/include/linux/atomisp.h b/drivers/staging/media/atomisp/include/linux/atomisp.h index 15fa567..ebe193b 100644 --- a/drivers/staging/media/atomisp/include/linux/atomisp.h +++ b/drivers/staging/media/atomisp/include/linux/atomisp.h @@ -68,7 +68,9 @@ #define V4L2_MBUS_FMT_CUSTOM_RGB32 0x800a /* Custom media bus format for M10MO RAW capture */ +#if 0 #define V4L2_MBUS_FMT_CUSTOM_M10MO_RAW 0x800b +#endif /* Configuration used by Bayer noise reduction and YCC noise reduction */ struct atomisp_nr_config { diff --git a/drivers/staging/media/atomisp/include/linux/atomisp_gmin_platform.h b/drivers/staging/media/atomisp/include/linux/atomisp_gmin_platform.h index 7e3ca12..c52c56a 100644 --- a/drivers/staging/media/atomisp/include/linux/atomisp_gmin_platform.h +++ b/drivers/staging/media/atomisp/include/linux/atomisp_gmin_platform.h @@ -23,7 +23,6 @@ int atomisp_register_i2c_module(struct v4l2_subdev *subdev, struct v4l2_subdev *atomisp_gmin_find_subdev(struct i2c_adapter *adapter, struct i2c_board_info *board_info); int atomisp_gmin_remove_subdev(struct v4l2_subdev *sd); -int gmin_get_config_var(struct device *dev, const char *var, char *out, size_t *out_len); int gmin_get_var_int(struct device *dev, const char *var, int def); int camera_sensor_csi(struct v4l2_subdev *sd, u32 port, u32 lanes, u32 format, u32 bayer_order, int flag); diff --git a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_drvfs.c b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_drvfs.c index 7129b88..ceedb82 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_drvfs.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_drvfs.c @@ -15,9 +15,9 @@ * */ +#include <linux/device.h> #include <linux/err.h> #include <linux/kernel.h> -#include <linux/pci.h> #include "atomisp_compat.h" #include "atomisp_internal.h" @@ -33,7 +33,7 @@ * bit 2: memory statistic */ struct _iunit_debug { - struct pci_driver *drv; + struct device_driver *drv; struct atomisp_device *isp; unsigned int dbglvl; unsigned int dbgfun; @@ -164,26 +164,25 @@ static const struct driver_attribute iunit_drvfs_attrs[] = { __ATTR(dbgopt, 0644, iunit_dbgopt_show, iunit_dbgopt_store), }; -static int iunit_drvfs_create_files(struct pci_driver *drv) +static int iunit_drvfs_create_files(struct device_driver *drv) { int i, ret = 0; for (i = 0; i < ARRAY_SIZE(iunit_drvfs_attrs); i++) - ret |= driver_create_file(&(drv->driver), - &iunit_drvfs_attrs[i]); + ret |= driver_create_file(drv, &iunit_drvfs_attrs[i]); return ret; } -static void iunit_drvfs_remove_files(struct pci_driver *drv) +static void iunit_drvfs_remove_files(struct device_driver *drv) { int i; for (i = 0; i < ARRAY_SIZE(iunit_drvfs_attrs); i++) - driver_remove_file(&(drv->driver), &iunit_drvfs_attrs[i]); + driver_remove_file(drv, &iunit_drvfs_attrs[i]); } -int atomisp_drvfs_init(struct pci_driver *drv, struct atomisp_device *isp) +int atomisp_drvfs_init(struct device_driver *drv, struct atomisp_device *isp) { int ret; @@ -193,7 +192,7 @@ int atomisp_drvfs_init(struct pci_driver *drv, struct atomisp_device *isp) ret = iunit_drvfs_create_files(iunit_debug.drv); if (ret) { dev_err(atomisp_dev, "drvfs_create_files error: %d\n", ret); - iunit_drvfs_remove_files(drv); + iunit_drvfs_remove_files(iunit_debug.drv); } return ret; diff --git a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_drvfs.h b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_drvfs.h index b91bfef..7c99240 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_drvfs.h +++ b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_drvfs.h @@ -18,8 +18,7 @@ #ifndef __ATOMISP_DRVFS_H__ #define __ATOMISP_DRVFS_H__ -extern int atomisp_drvfs_init(struct pci_driver *drv, struct atomisp_device - *isp); -extern void atomisp_drvfs_exit(void); +int atomisp_drvfs_init(struct device_driver *drv, struct atomisp_device *isp); +void atomisp_drvfs_exit(void); #endif /* __ATOMISP_DRVFS_H__ */ diff --git a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_internal.h b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_internal.h index 52a6f80..dc476a3 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_internal.h +++ b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_internal.h @@ -227,7 +227,6 @@ struct atomisp_device { struct media_device media_dev; struct atomisp_platform_data *pdata; void *mmu_l1_base; - struct pci_dev *pci_root; const struct firmware *firmware; struct pm_qos_request pm_qos; diff --git a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_ioctl.c b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_ioctl.c index 339b5d3..5c84dd6 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_ioctl.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_ioctl.c @@ -501,7 +501,9 @@ const struct atomisp_format_bridge atomisp_output_fmts[] = { .mbus_code = MEDIA_BUS_FMT_JPEG_1X8, .sh_fmt = CSS_FRAME_FORMAT_BINARY_8, .description = "JPEG" - }, { + }, +#if 0 + { /* This is a custom format being used by M10MO to send the RAW data */ .pixelformat = V4L2_PIX_FMT_CUSTOM_M10MO_RAW, .depth = 8, @@ -509,6 +511,7 @@ const struct atomisp_format_bridge atomisp_output_fmts[] = { .sh_fmt = CSS_FRAME_FORMAT_BINARY_8, .description = "Custom RAW for M10MO" }, +#endif }; const struct atomisp_format_bridge *atomisp_get_format_bridge( diff --git a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_subdev.c b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_subdev.c index 70b5398..f3e18d6 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_subdev.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_subdev.c @@ -48,7 +48,9 @@ const struct atomisp_in_fmt_conv atomisp_in_fmt_conv[] = { { V4L2_MBUS_FMT_CUSTOM_NV12, 12, 12, CSS_FRAME_FORMAT_NV12, 0, CSS_FRAME_FORMAT_NV12 }, { V4L2_MBUS_FMT_CUSTOM_NV21, 12, 12, CSS_FRAME_FORMAT_NV21, 0, CSS_FRAME_FORMAT_NV21 }, { V4L2_MBUS_FMT_CUSTOM_YUV420, 12, 12, ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY, 0, IA_CSS_STREAM_FORMAT_YUV420_8_LEGACY }, +#if 0 { V4L2_MBUS_FMT_CUSTOM_M10MO_RAW, 8, 8, CSS_FRAME_FORMAT_BINARY_8, 0, IA_CSS_STREAM_FORMAT_BINARY_8 }, +#endif /* no valid V4L2 MBUS code for metadata format, so leave it 0. */ { 0, 0, 0, ATOMISP_INPUT_FORMAT_EMBEDDED, 0, IA_CSS_STREAM_FORMAT_EMBEDDED }, {} diff --git a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_v4l2.c b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_v4l2.c index 3c260f8..548e00e 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_v4l2.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_v4l2.c @@ -1152,8 +1152,6 @@ alloc_fail: return err; } -static struct pci_driver atomisp_pci_driver; - #define ATOM_ISP_PCI_BAR 0 static int atomisp_pci_probe(struct pci_dev *dev, @@ -1212,11 +1210,6 @@ static int atomisp_pci_probe(struct pci_dev *dev, isp->pdev = dev; isp->dev = &dev->dev; isp->sw_contex.power_state = ATOM_ISP_POWER_UP; - isp->pci_root = pci_get_bus_and_slot(0, 0); - if (!isp->pci_root) { - dev_err(&dev->dev, "Unable to find PCI host\n"); - return -ENODEV; - } isp->saved_regs.ispmmadr = start; rt_mutex_init(&isp->mutex); @@ -1451,7 +1444,7 @@ static int atomisp_pci_probe(struct pci_dev *dev, isp->firmware = NULL; isp->css_env.isp_css_fw.data = NULL; - atomisp_drvfs_init(&atomisp_pci_driver, isp); + atomisp_drvfs_init(&dev->driver->driver, isp); return 0; @@ -1496,7 +1489,6 @@ load_fw_fail: /* Address later when we worry about the ...field chips */ if (IS_ENABLED(CONFIG_PM) && atomisp_mrfld_power_down(isp)) dev_err(&dev->dev, "Failed to switch off ISP\n"); - pci_dev_put(isp->pci_root); return err; } @@ -1517,8 +1509,6 @@ static void atomisp_pci_remove(struct pci_dev *dev) pm_qos_remove_request(&isp->pm_qos); atomisp_msi_irq_uninit(isp, dev); - pci_dev_put(isp->pci_root); - atomisp_unregister_entities(isp); destroy_workqueue(isp->wdt_work_queue); diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/eed1_8/ia_css_eed1_8.host.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/eed1_8/ia_css_eed1_8.host.c index 682f8b7..47bb504 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/eed1_8/ia_css_eed1_8.host.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/eed1_8/ia_css_eed1_8.host.c @@ -32,44 +32,44 @@ #define NUMBER_OF_TCINV_POINTS 9 #define NUMBER_OF_FCINV_POINTS 9 -const int16_t chgrinv_x[NUMBER_OF_CHGRINV_POINTS] = { +static const int16_t chgrinv_x[NUMBER_OF_CHGRINV_POINTS] = { 0, 16, 64, 144, 272, 448, 672, 976, 1376, 1888, 2528, 3312, 4256, 5376, 6688}; -const int16_t chgrinv_a[NUMBER_OF_CHGRINV_POINTS] = { +static const int16_t chgrinv_a[NUMBER_OF_CHGRINV_POINTS] = { -7171, -256, -29, -3456, -1071, -475, -189, -102, -48, -38, -10, -9, -7, -6, 0}; -const int16_t chgrinv_b[NUMBER_OF_CHGRINV_POINTS] = { +static const int16_t chgrinv_b[NUMBER_OF_CHGRINV_POINTS] = { 8191, 1021, 256, 114, 60, 37, 24, 17, 12, 9, 6, 5, 4, 3, 2}; -const int16_t chgrinv_c[NUMBER_OF_CHGRINV_POINTS] = { +static const int16_t chgrinv_c[NUMBER_OF_CHGRINV_POINTS] = { 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -const int16_t tcinv_x[NUMBER_OF_TCINV_POINTS] = { +static const int16_t tcinv_x[NUMBER_OF_TCINV_POINTS] = { 0, 4, 11, 23, 42, 68, 102, 148, 205}; -const int16_t tcinv_a[NUMBER_OF_TCINV_POINTS] = { +static const int16_t tcinv_a[NUMBER_OF_TCINV_POINTS] = { -6364, -631, -126, -34, -13, -6, -4452, -2156, 0}; -const int16_t tcinv_b[NUMBER_OF_TCINV_POINTS] = { +static const int16_t tcinv_b[NUMBER_OF_TCINV_POINTS] = { 8191, 1828, 726, 352, 197, 121, 80, 55, 40}; -const int16_t tcinv_c[NUMBER_OF_TCINV_POINTS] = { +static const int16_t tcinv_c[NUMBER_OF_TCINV_POINTS] = { 1, 1, 1, 1, 1, 1, 0, 0, 0}; -const int16_t fcinv_x[NUMBER_OF_FCINV_POINTS] = { +static const int16_t fcinv_x[NUMBER_OF_FCINV_POINTS] = { 0, 80, 216, 456, 824, 1344, 2040, 2952, 4096}; -const int16_t fcinv_a[NUMBER_OF_FCINV_POINTS] = { +static const int16_t fcinv_a[NUMBER_OF_FCINV_POINTS] = { -5244, -486, -86, -2849, -961, -400, -180, -86, 0}; -const int16_t fcinv_b[NUMBER_OF_FCINV_POINTS] = { +static const int16_t fcinv_b[NUMBER_OF_FCINV_POINTS] = { 8191, 1637, 607, 287, 159, 98, 64, 44, 32}; -const int16_t fcinv_c[NUMBER_OF_FCINV_POINTS] = { +static const int16_t fcinv_c[NUMBER_OF_FCINV_POINTS] = { 1, 1, 1, 0, 0, 0, 0, 0, 0}; diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/debug/src/ia_css_debug.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/debug/src/ia_css_debug.c index dd1127a..f22d73b 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/debug/src/ia_css_debug.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/debug/src/ia_css_debug.c @@ -2567,6 +2567,7 @@ ia_css_debug_mode_enable_dma_channel(int dma_id, return rc; } +static void dtrace_dot(const char *fmt, ...) { va_list ap; diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isp_param/interface/ia_css_isp_param_types.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isp_param/interface/ia_css_isp_param_types.h index 2283dd1..fa3f093 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isp_param/interface/ia_css_isp_param_types.h +++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isp_param/interface/ia_css_isp_param_types.h @@ -95,7 +95,7 @@ union ia_css_all_memory_offsets { }; #define IA_CSS_DEFAULT_ISP_MEM_PARAMS \ - { { { { 0, 0 } } } } + { { { { NULL, 0 } } } } #define IA_CSS_DEFAULT_ISP_CSS_PARAMS \ { { { { 0, 0 } } } } diff --git a/drivers/staging/media/atomisp/pci/atomisp2/hmm/hmm.c b/drivers/staging/media/atomisp/pci/atomisp2/hmm/hmm.c index a1c81c1..4338b8a 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/hmm/hmm.c +++ b/drivers/staging/media/atomisp/pci/atomisp2/hmm/hmm.c @@ -158,10 +158,10 @@ static ssize_t dynamic_pool_show(struct device *dev, return ret; }; -static DEVICE_ATTR(active_bo, 0444, active_bo_show, NULL); -static DEVICE_ATTR(free_bo, 0444, free_bo_show, NULL); -static DEVICE_ATTR(reserved_pool, 0444, reserved_pool_show, NULL); -static DEVICE_ATTR(dynamic_pool, 0444, dynamic_pool_show, NULL); +static DEVICE_ATTR_RO(active_bo); +static DEVICE_ATTR_RO(free_bo); +static DEVICE_ATTR_RO(reserved_pool); +static DEVICE_ATTR_RO(dynamic_pool); static struct attribute *sysfs_attrs_ctrl[] = { &dev_attr_active_bo.attr, diff --git a/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c b/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c index bf9f34b..d8b7183 100644 --- a/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c +++ b/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c @@ -114,7 +114,7 @@ int atomisp_register_i2c_module(struct v4l2_subdev *subdev, struct i2c_board_info *bi; struct gmin_subdev *gs; struct i2c_client *client = v4l2_get_subdevdata(subdev); - struct acpi_device *adev; + struct acpi_device *adev = ACPI_COMPANION(&client->dev); dev_info(&client->dev, "register atomisp i2c module type %d\n", type); @@ -124,9 +124,7 @@ int atomisp_register_i2c_module(struct v4l2_subdev *subdev, * tickled during suspend/resume. This has caused power and * performance issues on multiple devices. */ - adev = ACPI_COMPANION(&client->dev); - if (adev) - adev->power.flags.power_resources = 0; + adev->power.flags.power_resources = 0; for (i = 0; i < MAX_SUBDEVS; i++) if (!pdata.subdevs[i].type) @@ -211,7 +209,7 @@ struct gmin_cfg_var { const char *name, *val; }; -static const struct gmin_cfg_var ffrd8_vars[] = { +static struct gmin_cfg_var ffrd8_vars[] = { { "INTCF1B:00_ImxId", "0x134" }, { "INTCF1B:00_CsiPort", "1" }, { "INTCF1B:00_CsiLanes", "4" }, @@ -222,14 +220,14 @@ static const struct gmin_cfg_var ffrd8_vars[] = { /* Cribbed from MCG defaults in the mt9m114 driver, not actually verified * vs. T100 hardware */ -static const struct gmin_cfg_var t100_vars[] = { +static struct gmin_cfg_var t100_vars[] = { { "INT33F0:00_CsiPort", "0" }, { "INT33F0:00_CsiLanes", "1" }, { "INT33F0:00_CamClk", "1" }, {}, }; -static const struct gmin_cfg_var mrd7_vars[] = { +static struct gmin_cfg_var mrd7_vars[] = { {"INT33F8:00_CamType", "1"}, {"INT33F8:00_CsiPort", "1"}, {"INT33F8:00_CsiLanes", "2"}, @@ -245,7 +243,7 @@ static const struct gmin_cfg_var mrd7_vars[] = { {}, }; -static const struct gmin_cfg_var ecs7_vars[] = { +static struct gmin_cfg_var ecs7_vars[] = { {"INT33BE:00_CsiPort", "1"}, {"INT33BE:00_CsiLanes", "2"}, {"INT33BE:00_CsiFmt", "13"}, @@ -260,8 +258,7 @@ static const struct gmin_cfg_var ecs7_vars[] = { {}, }; - -static const struct gmin_cfg_var i8880_vars[] = { +static struct gmin_cfg_var i8880_vars[] = { {"XXOV2680:00_CsiPort", "1"}, {"XXOV2680:00_CsiLanes", "1"}, {"XXOV2680:00_CamClk", "0"}, @@ -271,18 +268,46 @@ static const struct gmin_cfg_var i8880_vars[] = { {}, }; -static const struct { - const char *dmi_board_name; - const struct gmin_cfg_var *vars; -} hard_vars[] = { - { "BYT-T FFD8", ffrd8_vars }, - { "T100TA", t100_vars }, - { "MRD7", mrd7_vars }, - { "ST70408", ecs7_vars }, - { "VTA0803", i8880_vars }, +static const struct dmi_system_id gmin_vars[] = { + { + .ident = "BYT-T FFD8", + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "BYT-T FFD8"), + }, + .driver_data = ffrd8_vars, + }, + { + .ident = "T100TA", + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "T100TA"), + }, + .driver_data = t100_vars, + }, + { + .ident = "MRD7", + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "TABLET"), + DMI_MATCH(DMI_BOARD_VERSION, "MRD 7"), + }, + .driver_data = mrd7_vars, + }, + { + .ident = "ST70408", + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "ST70408"), + }, + .driver_data = ecs7_vars, + }, + { + .ident = "VTA0803", + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "VTA0803"), + }, + .driver_data = i8880_vars, + }, + {} }; - #define GMIN_CFG_VAR_EFI_GUID EFI_GUID(0xecb54cd9, 0xe5ae, 0x4fdc, \ 0xa9, 0x71, 0xe8, 0x77, \ 0x75, 0x60, 0x68, 0xf7) @@ -322,8 +347,6 @@ static struct gmin_subdev *gmin_subdev_add(struct v4l2_subdev *subdev) VLV2_CLK_PLL_19P2MHZ); gmin_subdevs[i].csi_port = gmin_get_var_int(dev, "CsiPort", 0); gmin_subdevs[i].csi_lanes = gmin_get_var_int(dev, "CsiLanes", 1); - gmin_subdevs[i].gpio0 = gpiod_get_index(dev, NULL, 0, GPIOD_OUT_LOW); - gmin_subdevs[i].gpio1 = gpiod_get_index(dev, NULL, 1, GPIOD_OUT_LOW); /* get PMC clock with clock framework */ snprintf(gmin_pmc_clk_name, @@ -356,9 +379,11 @@ static struct gmin_subdev *gmin_subdev_add(struct v4l2_subdev *subdev) if (!ret) clk_disable_unprepare(gmin_subdevs[i].pmc_clk); + gmin_subdevs[i].gpio0 = gpiod_get_index(dev, NULL, 0, GPIOD_OUT_LOW); if (IS_ERR(gmin_subdevs[i].gpio0)) gmin_subdevs[i].gpio0 = NULL; + gmin_subdevs[i].gpio1 = gpiod_get_index(dev, NULL, 1, GPIOD_OUT_LOW); if (IS_ERR(gmin_subdevs[i].gpio1)) gmin_subdevs[i].gpio1 = NULL; @@ -394,7 +419,7 @@ static int gmin_gpio0_ctrl(struct v4l2_subdev *subdev, int on) { struct gmin_subdev *gs = find_gmin_subdev(subdev); - if (gs && gs->gpio0) { + if (gs) { gpiod_set_value(gs->gpio0, on); return 0; } @@ -405,7 +430,7 @@ static int gmin_gpio1_ctrl(struct v4l2_subdev *subdev, int on) { struct gmin_subdev *gs = find_gmin_subdev(subdev); - if (gs && gs->gpio1) { + if (gs) { gpiod_set_value(gs->gpio1, on); return 0; } @@ -606,17 +631,41 @@ int atomisp_gmin_register_vcm_control(struct camera_vcm_control *vcmCtrl) } EXPORT_SYMBOL_GPL(atomisp_gmin_register_vcm_control); +static int gmin_get_hardcoded_var(struct gmin_cfg_var *varlist, + const char *var8, char *out, size_t *out_len) +{ + struct gmin_cfg_var *gv; + + for (gv = varlist; gv->name; gv++) { + size_t vl; + + if (strcmp(var8, gv->name)) + continue; + + vl = strlen(gv->val); + if (vl > *out_len - 1) + return -ENOSPC; + + strcpy(out, gv->val); + *out_len = vl; + return 0; + } + + return -EINVAL; +} + /* Retrieves a device-specific configuration variable. The dev * argument should be a device with an ACPI companion, as all * configuration is based on firmware ID. */ -int gmin_get_config_var(struct device *dev, const char *var, char *out, - size_t *out_len) +static int gmin_get_config_var(struct device *dev, const char *var, + char *out, size_t *out_len) { char var8[CFG_VAR_NAME_MAX]; efi_char16_t var16[CFG_VAR_NAME_MAX]; struct efivar_entry *ev; - int i, j, ret; + const struct dmi_system_id *id; + int i, ret; if (dev && ACPI_COMPANION(dev)) dev = &ACPI_COMPANION(dev)->dev; @@ -633,28 +682,9 @@ int gmin_get_config_var(struct device *dev, const char *var, char *out, * Some device firmwares lack the ability to set EFI variables at * runtime. */ - for (i = 0; i < ARRAY_SIZE(hard_vars); i++) { - if (dmi_match(DMI_BOARD_NAME, hard_vars[i].dmi_board_name)) { - for (j = 0; hard_vars[i].vars[j].name; j++) { - size_t vl; - const struct gmin_cfg_var *gv; - - gv = &hard_vars[i].vars[j]; - vl = strlen(gv->val); - - if (strcmp(var8, gv->name)) - continue; - if (vl > *out_len - 1) - return -ENOSPC; - - memcpy(out, gv->val, min(*out_len, vl+1)); - out[*out_len-1] = 0; - *out_len = vl; - - return 0; - } - } - } + id = dmi_first_match(gmin_vars); + if (id) + return gmin_get_hardcoded_var(id->driver_data, var8, out, out_len); /* Our variable names are ASCII by construction, but EFI names * are wide chars. Convert and zero-pad. @@ -693,7 +723,6 @@ int gmin_get_config_var(struct device *dev, const char *var, char *out, return ret; } -EXPORT_SYMBOL_GPL(gmin_get_config_var); int gmin_get_var_int(struct device *dev, const char *var, int def) { diff --git a/drivers/staging/media/cxd2099/Makefile b/drivers/staging/media/cxd2099/Makefile index b2905e6..30432c9 100644 --- a/drivers/staging/media/cxd2099/Makefile +++ b/drivers/staging/media/cxd2099/Makefile @@ -1,5 +1,4 @@ obj-$(CONFIG_DVB_CXD2099) += cxd2099.o -ccflags-y += -Idrivers/media/dvb-core/ ccflags-y += -Idrivers/media/dvb-frontends/ ccflags-y += -Idrivers/media/tuners/ diff --git a/drivers/staging/media/cxd2099/cxd2099.c b/drivers/staging/media/cxd2099/cxd2099.c index 3e30f48..dc9cbd8 100644 --- a/drivers/staging/media/cxd2099/cxd2099.c +++ b/drivers/staging/media/cxd2099/cxd2099.c @@ -3,23 +3,14 @@ * * Copyright (C) 2010-2013 Digital Devices GmbH * - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 only, as published by the Free Software Foundation. * - * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA - * Or, point your browser to http://www.gnu.org/copyleft/gpl.html */ #include <linux/slab.h> @@ -35,7 +26,7 @@ static int buffermode; module_param(buffermode, int, 0444); -MODULE_PARM_DESC(buffermode, "Enable use of the CXD2099AR buffer mode (default: disabled)"); +MODULE_PARM_DESC(buffermode, "Enable CXD2099AR buffer mode (default: disabled)"); static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount); @@ -59,7 +50,7 @@ struct cxd { int amem_read; int cammode; - struct mutex lock; + struct mutex lock; /* device access lock */ u8 rbuf[1028]; u8 wbuf[1028]; @@ -101,7 +92,7 @@ static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr, .buf = val, .len = 1} }; if (i2c_transfer(adapter, msgs, 2) != 2) { - dev_err(&adapter->dev, "error in i2c_read_reg\n"); + dev_err(&adapter->dev, "error in %s()\n", __func__); return -1; } return 0; @@ -116,7 +107,7 @@ static int i2c_read(struct i2c_adapter *adapter, u8 adr, .buf = data, .len = n} }; if (i2c_transfer(adapter, msgs, 2) != 2) { - dev_err(&adapter->dev, "error in i2c_read\n"); + dev_err(&adapter->dev, "error in %s()\n", __func__); return -1; } return 0; @@ -134,7 +125,7 @@ static int read_block(struct cxd *ci, u8 adr, u8 *data, u16 n) while (n) { int len = n; - if (ci->cfg.max_i2c && (len > ci->cfg.max_i2c)) + if (ci->cfg.max_i2c && len > ci->cfg.max_i2c) len = ci->cfg.max_i2c; status = i2c_read(ci->i2c, ci->cfg.adr, 1, data, len); if (status) @@ -527,7 +518,7 @@ static int slot_shutdown(struct dvb_ca_en50221 *ca, int slot) { struct cxd *ci = ca->data; - dev_info(&ci->i2c->dev, "%s\n", __func__); + dev_dbg(&ci->i2c->dev, "%s\n", __func__); if (ci->cammode) read_data(ca, slot, ci->rbuf, 0); mutex_lock(&ci->lock); @@ -591,7 +582,7 @@ static int campoll(struct cxd *ci) } } if ((istat & 8) && - (ci->slot_stat == DVB_CA_EN50221_POLL_CAM_PRESENT)) { + ci->slot_stat == DVB_CA_EN50221_POLL_CAM_PRESENT) { ci->ready = 1; ci->slot_stat |= DVB_CA_EN50221_POLL_CAM_READY; } @@ -677,7 +668,8 @@ struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg, u8 val; if (i2c_read_reg(i2c, cfg->adr, 0, &val) < 0) { - dev_info(&i2c->dev, "No CXD2099 detected at %02x\n", cfg->adr); + dev_info(&i2c->dev, "No CXD2099AR detected at 0x%02x\n", + cfg->adr); return NULL; } @@ -695,7 +687,7 @@ struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg, ci->en = en_templ; ci->en.data = ci; init(ci); - dev_info(&i2c->dev, "Attached CXD2099AR at %02x\n", ci->cfg.adr); + dev_info(&i2c->dev, "Attached CXD2099AR at 0x%02x\n", ci->cfg.adr); if (!buffermode) { ci->en.read_data = NULL; @@ -708,6 +700,6 @@ struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg, } EXPORT_SYMBOL(cxd2099_attach); -MODULE_DESCRIPTION("cxd2099"); +MODULE_DESCRIPTION("CXD2099AR Common Interface controller driver"); MODULE_AUTHOR("Ralph Metzler"); MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/cxd2099/cxd2099.h b/drivers/staging/media/cxd2099/cxd2099.h index f4b29b1..253e3155 100644 --- a/drivers/staging/media/cxd2099/cxd2099.h +++ b/drivers/staging/media/cxd2099/cxd2099.h @@ -3,29 +3,20 @@ * * Copyright (C) 2010-2011 Digital Devices GmbH * - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 only, as published by the Free Software Foundation. * - * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA - * Or, point your browser to http://www.gnu.org/copyleft/gpl.html */ #ifndef _CXD2099_H_ #define _CXD2099_H_ -#include <dvb_ca_en50221.h> +#include <media/dvb_ca_en50221.h> struct cxd2099_cfg { u32 bitrate; @@ -42,8 +33,9 @@ struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg, void *priv, struct i2c_adapter *i2c); #else -static inline struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg, - void *priv, struct i2c_adapter *i2c) +static inline struct +dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg, void *priv, + struct i2c_adapter *i2c) { dev_warn(&i2c->dev, "%s: driver disabled by Kconfig\n", __func__); return NULL; diff --git a/drivers/staging/media/davinci_vpfe/TODO b/drivers/staging/media/davinci_vpfe/TODO index 7015ab3..3e5477e 100644 --- a/drivers/staging/media/davinci_vpfe/TODO +++ b/drivers/staging/media/davinci_vpfe/TODO @@ -2,11 +2,11 @@ TODO (general): ================================== - User space interface refinement - - Controls should be used when possible rather than private ioctl - - No enums should be used - - Use of MC and V4L2 subdev APIs when applicable - - Single interface header might suffice - - Current interface forces to configure everything at once + - Controls should be used when possible rather than private ioctl + - No enums should be used + - Use of MC and V4L2 subdev APIs when applicable + - Single interface header might suffice + - Current interface forces to configure everything at once - Get rid of the dm365_ipipe_hw.[ch] layer - Active external sub-devices defined by link configuration; no strcmp needed diff --git a/drivers/staging/media/imx/TODO b/drivers/staging/media/imx/TODO index 0bee313..9eb7326 100644 --- a/drivers/staging/media/imx/TODO +++ b/drivers/staging/media/imx/TODO @@ -1,19 +1,14 @@ -- Clean up and move the ov5642 subdev driver to drivers/media/i2c, or - merge support for OV5642 into drivers/media/i2c/ov5640.c, and create - the binding docs for it. - - The Frame Interval Monitor could be exported to v4l2-core for general use. -- At driver load time, the device-tree node that is the original source - (the "sensor"), is parsed to record its media bus configuration, and - this info is required in imx-media-csi.c to setup the CSI. - Laurent Pinchart argues that instead the CSI subdev should call its - neighbor's g_mbus_config op (which should be propagated if necessary) - to get this info. However Hans Verkuil is planning to remove the - g_mbus_config op. For now this driver uses the parsed DT mbus config - method until this issue is resolved. +- The CSI subdevice parses its nearest upstream neighbor's device-tree + bus config in order to setup the CSI. Laurent Pinchart argues that + instead the CSI subdev should call its neighbor's g_mbus_config op + (which should be propagated if necessary) to get this info. However + Hans Verkuil is planning to remove the g_mbus_config op. For now this + driver uses the parsed DT bus config method until this issue is + resolved. - This media driver supports inheriting V4L2 controls to the video capture devices, from the subdevices in the capture device's @@ -21,3 +16,47 @@ link_notify callback when the pipeline is modified. It should be decided whether this feature is useful enough to make it generally available by exporting to v4l2-core. + +- The OF graph is walked at probe time to form the list of fwnodes to + be passed to v4l2_async_notifier_register(), starting from the IPU + CSI ports. And after all async subdevices have been bound, + v4l2_fwnode_parse_link() is used to form the media links between + the entities discovered by walking the OF graph. + + While this approach allows support for arbitrary OF graphs, there + are some assumptions for this to work: + + 1. All port parent nodes reachable in the graph from the IPU CSI + ports bind to V4L2 async subdevice drivers. + + If a device has mixed-use ports such as video plus audio, the + endpoints from the audio ports are followed to devices that must + bind to V4L2 subdevice drivers, and not for example, to an ALSA + driver or a non-V4L2 media driver. If the device were bound to + such a driver, imx-media would never get an async completion + notification because the device fwnode was added to the async + list, but the driver does not interface with the V4L2 async + framework. + + 2. Every port reachable in the graph is treated as a media pad, + owned by the V4L2 subdevice that is bound to the port's parent. + + This presents problems for devices that don't make this port = pad + assumption. Examples are SMIAPP compatible cameras which define only + a single output port node, but which define multiple pads owned + by multiple subdevices (pixel-array, binner, scaler). Or video + decoders (entity function MEDIA_ENT_F_ATV_DECODER), which also define + only a single output port node, but define multiple pads for video, + VBI, and audio out. + + A workaround at present is to set the port reg properties to + correspond to the media pad index that the port represents. A + possible long-term solution is to implement a subdev API that + maps a port id to a media pad index. + + 3. Every endpoint of a port reachable in the graph is treated as + a media link, between V4L2 subdevices that are bound to the + port parents of the local and remote endpoints. + + Which means a port must not contain mixed-use endpoints, they + must all refer to media links between V4L2 subdevices. diff --git a/drivers/staging/media/imx/imx-ic-prp.c b/drivers/staging/media/imx/imx-ic-prp.c index 9e41987..c6d7e80 100644 --- a/drivers/staging/media/imx/imx-ic-prp.c +++ b/drivers/staging/media/imx/imx-ic-prp.c @@ -300,7 +300,7 @@ static int prp_link_validate(struct v4l2_subdev *sd, { struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd); struct prp_priv *priv = ic_priv->prp_priv; - struct imx_media_subdev *csi; + struct v4l2_subdev *csi; int ret; ret = v4l2_subdev_link_validate_default(sd, link, @@ -333,7 +333,7 @@ static int prp_link_validate(struct v4l2_subdev *sd, } if (csi) { - switch (csi->sd->grp_id) { + switch (csi->grp_id) { case IMX_MEDIA_GRP_ID_CSI0: priv->csi_id = 0; break; diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index ea145ba..576bdc7 100644 --- a/drivers/staging/media/imx/imx-media-capture.c +++ b/drivers/staging/media/imx/imx-media-capture.c @@ -449,9 +449,6 @@ static int capture_start_streaming(struct vb2_queue *vq, unsigned int count) unsigned long flags; int ret; - if (vb2_is_streaming(vq)) - return 0; - ret = imx_media_pipeline_set_stream(priv->md, &priv->src_sd->entity, true); if (ret) { @@ -480,9 +477,6 @@ static void capture_stop_streaming(struct vb2_queue *vq) unsigned long flags; int ret; - if (!vb2_is_streaming(vq)) - return; - spin_lock_irqsave(&priv->q_lock, flags); priv->stop = true; spin_unlock_irqrestore(&priv->q_lock, flags); @@ -754,6 +748,8 @@ imx_media_capture_device_init(struct v4l2_subdev *src_sd, int pad) vfd->queue = &priv->q; priv->vdev.vfd = vfd; + INIT_LIST_HEAD(&priv->vdev.list); + video_set_drvdata(vfd, priv); v4l2_ctrl_handler_init(&priv->ctrl_hdlr, 0); diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 26994b4..eb7be50 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -13,6 +13,7 @@ #include <linux/gcd.h> #include <linux/interrupt.h> #include <linux/module.h> +#include <linux/of_graph.h> #include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> #include <media/v4l2-ctrls.h> @@ -99,8 +100,8 @@ struct csi_priv { /* the mipi virtual channel number at link validate */ int vc_num; - /* the attached sensor at stream on */ - struct imx_media_subdev *sensor; + /* the upstream endpoint CSI is receiving from */ + struct v4l2_fwnode_endpoint upstream_ep; spinlock_t irqlock; /* protect eof_irq handler */ struct timer_list eof_timeout_timer; @@ -120,6 +121,70 @@ static inline struct csi_priv *sd_to_dev(struct v4l2_subdev *sdev) return container_of(sdev, struct csi_priv, sd); } +static inline bool is_parallel_16bit_bus(struct v4l2_fwnode_endpoint *ep) +{ + return ep->bus_type != V4L2_MBUS_CSI2 && + ep->bus.parallel.bus_width >= 16; +} + +/* + * Parses the fwnode endpoint from the source pad of the entity + * connected to this CSI. This will either be the entity directly + * upstream from the CSI-2 receiver, or directly upstream from the + * video mux. The endpoint is needed to determine the bus type and + * bus config coming into the CSI. + */ +static int csi_get_upstream_endpoint(struct csi_priv *priv, + struct v4l2_fwnode_endpoint *ep) +{ + struct device_node *endpoint, *port; + struct media_entity *src; + struct v4l2_subdev *sd; + struct media_pad *pad; + + if (!priv->src_sd) + return -EPIPE; + + src = &priv->src_sd->entity; + + if (src->function == MEDIA_ENT_F_VID_MUX) { + /* + * CSI is connected directly to video mux, skip up to + * CSI-2 receiver if it is in the path, otherwise stay + * with video mux. + */ + sd = imx_media_find_upstream_subdev(priv->md, src, + IMX_MEDIA_GRP_ID_CSI2); + if (!IS_ERR(sd)) + src = &sd->entity; + } + + /* get source pad of entity directly upstream from src */ + pad = imx_media_find_upstream_pad(priv->md, src, 0); + if (IS_ERR(pad)) + return PTR_ERR(pad); + + sd = media_entity_to_v4l2_subdev(pad->entity); + + /* + * NOTE: this assumes an OF-graph port id is the same as a + * media pad index. + */ + port = of_graph_get_port_by_id(sd->dev->of_node, pad->index); + if (!port) + return -ENODEV; + + endpoint = of_get_next_child(port, NULL); + of_node_put(port); + if (!endpoint) + return -ENODEV; + + v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), ep); + of_node_put(endpoint); + + return 0; +} + static void csi_idmac_put_ipu_resources(struct csi_priv *priv) { if (priv->idmac_ch) @@ -302,7 +367,6 @@ static void csi_idmac_unsetup_vb2_buf(struct csi_priv *priv, static int csi_idmac_setup_channel(struct csi_priv *priv) { struct imx_media_video_dev *vdev = priv->vdev; - struct v4l2_fwnode_endpoint *sensor_ep; struct v4l2_mbus_framefmt *infmt; struct ipu_image image; u32 passthrough_bits; @@ -312,7 +376,6 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) int ret; infmt = &priv->format_mbus[CSI_SINK_PAD]; - sensor_ep = &priv->sensor->sensor_ep; ipu_cpmem_zero(priv->idmac_ch); @@ -330,14 +393,14 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) * Check for conditions that require the IPU to handle the * data internally as generic data, aka passthrough mode: * - raw bayer formats - * - the sensor bus is 16-bit parallel + * - the CSI is receiving from a 16-bit parallel bus */ switch (image.pix.pixelformat) { case V4L2_PIX_FMT_SBGGR8: case V4L2_PIX_FMT_SGBRG8: case V4L2_PIX_FMT_SGRBG8: case V4L2_PIX_FMT_SRGGB8: - burst_size = 8; + burst_size = 16; passthrough = true; passthrough_bits = 8; break; @@ -354,8 +417,7 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) burst_size = (image.pix.width & 0x3f) ? ((image.pix.width & 0x1f) ? ((image.pix.width & 0xf) ? 8 : 16) : 32) : 64; - passthrough = (sensor_ep->bus_type != V4L2_MBUS_CSI2 && - sensor_ep->bus.parallel.bus_width >= 16); + passthrough = is_parallel_16bit_bus(&priv->upstream_ep); passthrough_bits = 16; /* Skip writing U and V components to odd rows */ ipu_cpmem_skip_odd_chroma_rows(priv->idmac_ch); @@ -364,14 +426,12 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) case V4L2_PIX_FMT_UYVY: burst_size = (image.pix.width & 0x1f) ? ((image.pix.width & 0xf) ? 8 : 16) : 32; - passthrough = (sensor_ep->bus_type != V4L2_MBUS_CSI2 && - sensor_ep->bus.parallel.bus_width >= 16); + passthrough = is_parallel_16bit_bus(&priv->upstream_ep); passthrough_bits = 16; break; default: burst_size = (image.pix.width & 0xf) ? 8 : 16; - passthrough = (sensor_ep->bus_type != V4L2_MBUS_CSI2 && - sensor_ep->bus.parallel.bus_width >= 16); + passthrough = is_parallel_16bit_bus(&priv->upstream_ep); passthrough_bits = 16; break; } @@ -568,22 +628,20 @@ static void csi_idmac_stop(struct csi_priv *priv) static int csi_setup(struct csi_priv *priv) { struct v4l2_mbus_framefmt *infmt, *outfmt; - struct v4l2_mbus_config sensor_mbus_cfg; - struct v4l2_fwnode_endpoint *sensor_ep; + struct v4l2_mbus_config mbus_cfg; struct v4l2_mbus_framefmt if_fmt; infmt = &priv->format_mbus[CSI_SINK_PAD]; outfmt = &priv->format_mbus[priv->active_output_pad]; - sensor_ep = &priv->sensor->sensor_ep; - /* compose mbus_config from sensor endpoint */ - sensor_mbus_cfg.type = sensor_ep->bus_type; - sensor_mbus_cfg.flags = (sensor_ep->bus_type == V4L2_MBUS_CSI2) ? - sensor_ep->bus.mipi_csi2.flags : - sensor_ep->bus.parallel.flags; + /* compose mbus_config from the upstream endpoint */ + mbus_cfg.type = priv->upstream_ep.bus_type; + mbus_cfg.flags = (priv->upstream_ep.bus_type == V4L2_MBUS_CSI2) ? + priv->upstream_ep.bus.mipi_csi2.flags : + priv->upstream_ep.bus.parallel.flags; /* - * we need to pass input sensor frame to CSI interface, but + * we need to pass input frame to CSI interface, but * with translated field type from output format */ if_fmt = *infmt; @@ -595,7 +653,7 @@ static int csi_setup(struct csi_priv *priv) priv->crop.width == 2 * priv->compose.width, priv->crop.height == 2 * priv->compose.height); - ipu_csi_init_interface(priv->csi, &sensor_mbus_cfg, &if_fmt); + ipu_csi_init_interface(priv->csi, &mbus_cfg, &if_fmt); ipu_csi_set_dest(priv->csi, priv->dest); @@ -611,35 +669,11 @@ static int csi_setup(struct csi_priv *priv) static int csi_start(struct csi_priv *priv) { struct v4l2_fract *output_fi, *input_fi; - u32 bad_frames = 0; int ret; - if (!priv->sensor) { - v4l2_err(&priv->sd, "no sensor attached\n"); - return -EINVAL; - } - output_fi = &priv->frame_interval[priv->active_output_pad]; input_fi = &priv->frame_interval[CSI_SINK_PAD]; - ret = v4l2_subdev_call(priv->sensor->sd, sensor, - g_skip_frames, &bad_frames); - if (!ret && bad_frames) { - u32 delay_usec; - - /* - * This sensor has bad frames when it is turned on, - * add a delay to avoid them before enabling the CSI - * hardware. Especially for sensors with a bt.656 interface, - * any shifts in the SAV/EAV sync codes will cause the CSI - * to lose vert/horiz sync. - */ - delay_usec = DIV_ROUND_UP_ULL( - (u64)USEC_PER_SEC * input_fi->numerator * bad_frames, - input_fi->denominator); - usleep_range(delay_usec, delay_usec + 1000); - } - if (priv->dest == IPU_CSI_DEST_IDMAC) { ret = csi_idmac_start(priv); if (ret) @@ -971,9 +1005,8 @@ static int csi_link_validate(struct v4l2_subdev *sd, struct v4l2_subdev_format *sink_fmt) { struct csi_priv *priv = v4l2_get_subdevdata(sd); - struct v4l2_fwnode_endpoint *sensor_ep; + struct v4l2_fwnode_endpoint upstream_ep; const struct imx_media_pixfmt *incc; - struct imx_media_subdev *sensor; bool is_csi2; int ret; @@ -982,22 +1015,20 @@ static int csi_link_validate(struct v4l2_subdev *sd, if (ret) return ret; - sensor = __imx_media_find_sensor(priv->md, &priv->sd.entity); - if (IS_ERR(sensor)) { - v4l2_err(&priv->sd, "no sensor attached\n"); - return PTR_ERR(priv->sensor); + ret = csi_get_upstream_endpoint(priv, &upstream_ep); + if (ret) { + v4l2_err(&priv->sd, "failed to find upstream endpoint\n"); + return ret; } mutex_lock(&priv->lock); - priv->sensor = sensor; - sensor_ep = &priv->sensor->sensor_ep; - is_csi2 = (sensor_ep->bus_type == V4L2_MBUS_CSI2); + priv->upstream_ep = upstream_ep; + is_csi2 = (upstream_ep.bus_type == V4L2_MBUS_CSI2); incc = priv->cc[CSI_SINK_PAD]; if (priv->dest != IPU_CSI_DEST_IDMAC && - (incc->bayer || (!is_csi2 && - sensor_ep->bus.parallel.bus_width >= 16))) { + (incc->bayer || is_parallel_16bit_bus(&upstream_ep))) { v4l2_err(&priv->sd, "bayer/16-bit parallel buses must go to IDMAC pad\n"); ret = -EINVAL; @@ -1067,12 +1098,8 @@ static void csi_try_crop(struct csi_priv *priv, struct v4l2_rect *crop, struct v4l2_subdev_pad_config *cfg, struct v4l2_mbus_framefmt *infmt, - struct imx_media_subdev *sensor) + struct v4l2_fwnode_endpoint *upstream_ep) { - struct v4l2_fwnode_endpoint *sensor_ep; - - sensor_ep = &sensor->sensor_ep; - crop->width = min_t(__u32, infmt->width, crop->width); if (crop->left + crop->width > infmt->width) crop->left = infmt->width - crop->width; @@ -1086,7 +1113,7 @@ static void csi_try_crop(struct csi_priv *priv, * sync, so fix it to NTSC/PAL active lines. NTSC contains * 2 extra lines of active video that need to be cropped. */ - if (sensor_ep->bus_type == V4L2_MBUS_BT656 && + if (upstream_ep->bus_type == V4L2_MBUS_BT656 && (V4L2_FIELD_HAS_BOTH(infmt->field) || infmt->field == V4L2_FIELD_ALTERNATE)) { crop->height = infmt->height; @@ -1236,7 +1263,7 @@ out: } static void csi_try_fmt(struct csi_priv *priv, - struct imx_media_subdev *sensor, + struct v4l2_fwnode_endpoint *upstream_ep, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *sdformat, struct v4l2_rect *crop, @@ -1304,7 +1331,7 @@ static void csi_try_fmt(struct csi_priv *priv, crop->top = 0; crop->width = sdformat->format.width; crop->height = sdformat->format.height; - csi_try_crop(priv, crop, cfg, &sdformat->format, sensor); + csi_try_crop(priv, crop, cfg, &sdformat->format, upstream_ep); compose->left = 0; compose->top = 0; compose->width = crop->width; @@ -1333,20 +1360,20 @@ static int csi_set_fmt(struct v4l2_subdev *sd, { struct csi_priv *priv = v4l2_get_subdevdata(sd); struct imx_media_video_dev *vdev = priv->vdev; + struct v4l2_fwnode_endpoint upstream_ep; const struct imx_media_pixfmt *cc; - struct imx_media_subdev *sensor; struct v4l2_pix_format vdev_fmt; struct v4l2_mbus_framefmt *fmt; struct v4l2_rect *crop, *compose; - int ret = 0; + int ret; if (sdformat->pad >= CSI_NUM_PADS) return -EINVAL; - sensor = imx_media_find_sensor(priv->md, &priv->sd.entity); - if (IS_ERR(sensor)) { - v4l2_err(&priv->sd, "no sensor attached\n"); - return PTR_ERR(sensor); + ret = csi_get_upstream_endpoint(priv, &upstream_ep); + if (ret) { + v4l2_err(&priv->sd, "failed to find upstream endpoint\n"); + return ret; } mutex_lock(&priv->lock); @@ -1359,7 +1386,7 @@ static int csi_set_fmt(struct v4l2_subdev *sd, crop = __csi_get_crop(priv, cfg, sdformat->which); compose = __csi_get_compose(priv, cfg, sdformat->which); - csi_try_fmt(priv, sensor, cfg, sdformat, crop, compose, &cc); + csi_try_fmt(priv, &upstream_ep, cfg, sdformat, crop, compose, &cc); fmt = __csi_get_fmt(priv, cfg, sdformat->pad, sdformat->which); *fmt = sdformat->format; @@ -1376,8 +1403,8 @@ static int csi_set_fmt(struct v4l2_subdev *sd, format.pad = pad; format.which = sdformat->which; format.format = sdformat->format; - csi_try_fmt(priv, sensor, cfg, &format, NULL, compose, - &outcc); + csi_try_fmt(priv, &upstream_ep, cfg, &format, + NULL, compose, &outcc); outfmt = __csi_get_fmt(priv, cfg, pad, sdformat->which); *outfmt = format.format; @@ -1472,18 +1499,18 @@ static int csi_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_selection *sel) { struct csi_priv *priv = v4l2_get_subdevdata(sd); + struct v4l2_fwnode_endpoint upstream_ep; struct v4l2_mbus_framefmt *infmt; struct v4l2_rect *crop, *compose; - struct imx_media_subdev *sensor; - int pad, ret = 0; + int pad, ret; if (sel->pad != CSI_SINK_PAD) return -EINVAL; - sensor = imx_media_find_sensor(priv->md, &priv->sd.entity); - if (IS_ERR(sensor)) { - v4l2_err(&priv->sd, "no sensor attached\n"); - return PTR_ERR(sensor); + ret = csi_get_upstream_endpoint(priv, &upstream_ep); + if (ret) { + v4l2_err(&priv->sd, "failed to find upstream endpoint\n"); + return ret; } mutex_lock(&priv->lock); @@ -1511,7 +1538,7 @@ static int csi_set_selection(struct v4l2_subdev *sd, goto out; } - csi_try_crop(priv, &sel->r, cfg, infmt, sensor); + csi_try_crop(priv, &sel->r, cfg, infmt, &upstream_ep); *crop = sel->r; diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c index 47c4c95..289d775 100644 --- a/drivers/staging/media/imx/imx-media-dev.c +++ b/drivers/staging/media/imx/imx-media-dev.c @@ -11,6 +11,7 @@ #include <linux/delay.h> #include <linux/fs.h> #include <linux/module.h> +#include <linux/of_graph.h> #include <linux/of_platform.h> #include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> @@ -32,29 +33,28 @@ static inline struct imx_media_dev *notifier2dev(struct v4l2_async_notifier *n) } /* - * Find a subdev by device node or device name. This is called during + * Find an asd by fwnode or device name. This is called during * driver load to form the async subdev list and bind them. */ -struct imx_media_subdev * -imx_media_find_async_subdev(struct imx_media_dev *imxmd, - struct device_node *np, - const char *devname) +static struct v4l2_async_subdev * +find_async_subdev(struct imx_media_dev *imxmd, + struct fwnode_handle *fwnode, + const char *devname) { - struct fwnode_handle *fwnode = np ? of_fwnode_handle(np) : NULL; - struct imx_media_subdev *imxsd; - int i; + struct imx_media_async_subdev *imxasd; + struct v4l2_async_subdev *asd; - for (i = 0; i < imxmd->subdev_notifier.num_subdevs; i++) { - imxsd = &imxmd->subdev[i]; - switch (imxsd->asd.match_type) { + list_for_each_entry(imxasd, &imxmd->asd_list, list) { + asd = &imxasd->asd; + switch (asd->match_type) { case V4L2_ASYNC_MATCH_FWNODE: - if (fwnode && imxsd->asd.match.fwnode.fwnode == fwnode) - return imxsd; + if (fwnode && asd->match.fwnode == fwnode) + return asd; break; case V4L2_ASYNC_MATCH_DEVNAME: - if (devname && - !strcmp(imxsd->asd.match.device_name.name, devname)) - return imxsd; + if (devname && !strcmp(asd->match.device_name, + devname)) + return asd; break; default: break; @@ -66,57 +66,53 @@ imx_media_find_async_subdev(struct imx_media_dev *imxmd, /* - * Adds a subdev to the async subdev list. If np is non-NULL, adds + * Adds a subdev to the async subdev list. If fwnode is non-NULL, adds * the async as a V4L2_ASYNC_MATCH_FWNODE match type, otherwise as * a V4L2_ASYNC_MATCH_DEVNAME match type using the dev_name of the * given platform_device. This is called during driver load when * forming the async subdev list. */ -struct imx_media_subdev * -imx_media_add_async_subdev(struct imx_media_dev *imxmd, - struct device_node *np, - struct platform_device *pdev) +int imx_media_add_async_subdev(struct imx_media_dev *imxmd, + struct fwnode_handle *fwnode, + struct platform_device *pdev) { - struct imx_media_subdev *imxsd; + struct device_node *np = to_of_node(fwnode); + struct imx_media_async_subdev *imxasd; struct v4l2_async_subdev *asd; const char *devname = NULL; - int sd_idx; + int ret = 0; mutex_lock(&imxmd->mutex); if (pdev) devname = dev_name(&pdev->dev); - /* return -EEXIST if this subdev already added */ - if (imx_media_find_async_subdev(imxmd, np, devname)) { + /* return -EEXIST if this asd already added */ + if (find_async_subdev(imxmd, fwnode, devname)) { dev_dbg(imxmd->md.dev, "%s: already added %s\n", __func__, np ? np->name : devname); - imxsd = ERR_PTR(-EEXIST); + ret = -EEXIST; goto out; } - sd_idx = imxmd->subdev_notifier.num_subdevs; - if (sd_idx >= IMX_MEDIA_MAX_SUBDEVS) { - dev_err(imxmd->md.dev, "%s: too many subdevs! can't add %s\n", - __func__, np ? np->name : devname); - imxsd = ERR_PTR(-ENOSPC); + imxasd = devm_kzalloc(imxmd->md.dev, sizeof(*imxasd), GFP_KERNEL); + if (!imxasd) { + ret = -ENOMEM; goto out; } + asd = &imxasd->asd; - imxsd = &imxmd->subdev[sd_idx]; - - asd = &imxsd->asd; - if (np) { + if (fwnode) { asd->match_type = V4L2_ASYNC_MATCH_FWNODE; - asd->match.fwnode.fwnode = of_fwnode_handle(np); + asd->match.fwnode = fwnode; } else { asd->match_type = V4L2_ASYNC_MATCH_DEVNAME; - strncpy(imxsd->devname, devname, sizeof(imxsd->devname)); - asd->match.device_name.name = imxsd->devname; - imxsd->pdev = pdev; + asd->match.device_name = devname; + imxasd->pdev = pdev; } - imxmd->async_ptrs[sd_idx] = asd; + list_add_tail(&imxasd->list, &imxmd->asd_list); + imxmd->subdev_notifier.num_subdevs++; dev_dbg(imxmd->md.dev, "%s: added %s, match type %s\n", @@ -124,50 +120,6 @@ imx_media_add_async_subdev(struct imx_media_dev *imxmd, out: mutex_unlock(&imxmd->mutex); - return imxsd; -} - -/* - * Adds an imx-media link to a subdev pad's link list. This is called - * during driver load when forming the links between subdevs. - * - * @pad: the local pad - * @remote_node: the device node of the remote subdev - * @remote_devname: the device name of the remote subdev - * @local_pad: local pad index - * @remote_pad: remote pad index - */ -int imx_media_add_pad_link(struct imx_media_dev *imxmd, - struct imx_media_pad *pad, - struct device_node *remote_node, - const char *remote_devname, - int local_pad, int remote_pad) -{ - struct imx_media_link *link; - int link_idx, ret = 0; - - mutex_lock(&imxmd->mutex); - - link_idx = pad->num_links; - if (link_idx >= IMX_MEDIA_MAX_LINKS) { - dev_err(imxmd->md.dev, "%s: too many links!\n", __func__); - ret = -ENOSPC; - goto out; - } - - link = &pad->link[link_idx]; - - link->remote_sd_node = remote_node; - if (remote_devname) - strncpy(link->remote_devname, remote_devname, - sizeof(link->remote_devname)); - - link->local_pad = local_pad; - link->remote_pad = remote_pad; - - pad->num_links++; -out: - mutex_unlock(&imxmd->mutex); return ret; } @@ -206,122 +158,66 @@ static int imx_media_subdev_bound(struct v4l2_async_notifier *notifier, struct v4l2_async_subdev *asd) { struct imx_media_dev *imxmd = notifier2dev(notifier); - struct device_node *np = to_of_node(sd->fwnode); - struct imx_media_subdev *imxsd; int ret = 0; mutex_lock(&imxmd->mutex); - imxsd = imx_media_find_async_subdev(imxmd, np, dev_name(sd->dev)); - if (!imxsd) { - ret = -EINVAL; - goto out; - } - if (sd->grp_id & IMX_MEDIA_GRP_ID_CSI) { ret = imx_media_get_ipu(imxmd, sd); if (ret) - goto out_unlock; - } else if (sd->entity.function == MEDIA_ENT_F_VID_MUX) { - /* this is a video mux */ - sd->grp_id = IMX_MEDIA_GRP_ID_VIDMUX; - } else if (imxsd->num_sink_pads == 0) { - /* - * this is an original source of video frames, it - * could be a camera sensor, an analog decoder, or - * a bridge device (HDMI -> MIPI CSI-2 for example). - * This group ID is used to locate the entity that - * is the original source of video in a pipeline. - */ - sd->grp_id = IMX_MEDIA_GRP_ID_SENSOR; + goto out; } - /* attach the subdev */ - imxsd->sd = sd; + v4l2_info(&imxmd->v4l2_dev, "subdev %s bound\n", sd->name); out: - if (ret) - v4l2_warn(&imxmd->v4l2_dev, - "Received unknown subdev %s\n", sd->name); - else - v4l2_info(&imxmd->v4l2_dev, - "Registered subdev %s\n", sd->name); - -out_unlock: mutex_unlock(&imxmd->mutex); return ret; } /* - * Create a single source->sink media link given a subdev and a single - * link from one of its source pads. Called after all subdevs have - * registered. + * create the media links for all subdevs that registered async. + * Called after all async subdevs have bound. */ -static int imx_media_create_link(struct imx_media_dev *imxmd, - struct imx_media_subdev *src, - struct imx_media_link *link) +static int imx_media_create_links(struct v4l2_async_notifier *notifier) { - struct imx_media_subdev *sink; - u16 source_pad, sink_pad; + struct imx_media_dev *imxmd = notifier2dev(notifier); + struct v4l2_subdev *sd; int ret; - sink = imx_media_find_async_subdev(imxmd, link->remote_sd_node, - link->remote_devname); - if (!sink) { - v4l2_warn(&imxmd->v4l2_dev, "%s: no sink for %s:%d\n", - __func__, src->sd->name, link->local_pad); - return 0; - } - - source_pad = link->local_pad; - sink_pad = link->remote_pad; - - v4l2_info(&imxmd->v4l2_dev, "%s: %s:%d -> %s:%d\n", __func__, - src->sd->name, source_pad, sink->sd->name, sink_pad); - - ret = media_create_pad_link(&src->sd->entity, source_pad, - &sink->sd->entity, sink_pad, 0); - if (ret) - v4l2_err(&imxmd->v4l2_dev, - "create_pad_link failed: %d\n", ret); - - return ret; -} - -/* - * create the media links from all imx-media pads and their links. - * Called after all subdevs have registered. - */ -static int imx_media_create_links(struct imx_media_dev *imxmd) -{ - struct imx_media_subdev *imxsd; - struct imx_media_link *link; - struct imx_media_pad *pad; - int num_pads, i, j, k; - int ret = 0; - - for (i = 0; i < imxmd->num_subdevs; i++) { - imxsd = &imxmd->subdev[i]; - num_pads = imxsd->num_sink_pads + imxsd->num_src_pads; - - for (j = 0; j < num_pads; j++) { - pad = &imxsd->pad[j]; - - /* only create the source->sink links */ - if (!(pad->pad.flags & MEDIA_PAD_FL_SOURCE)) - continue; - - for (k = 0; k < pad->num_links; k++) { - link = &pad->link[k]; - - ret = imx_media_create_link(imxmd, imxsd, link); - if (ret) - goto out; - } + /* + * Only links are created between subdevices that are known + * to the async notifier. If there are other non-async subdevices, + * they were created internally by some subdevice (smiapp is one + * example). In those cases it is expected the subdevice is + * responsible for creating those internal links. + */ + list_for_each_entry(sd, ¬ifier->done, async_list) { + switch (sd->grp_id) { + case IMX_MEDIA_GRP_ID_VDIC: + case IMX_MEDIA_GRP_ID_IC_PRP: + case IMX_MEDIA_GRP_ID_IC_PRPENC: + case IMX_MEDIA_GRP_ID_IC_PRPVF: + case IMX_MEDIA_GRP_ID_CSI0: + case IMX_MEDIA_GRP_ID_CSI1: + ret = imx_media_create_internal_links(imxmd, sd); + if (ret) + return ret; + /* + * the CSIs straddle between the external and the IPU + * internal entities, so create the external links + * to the CSI sink pads. + */ + if (sd->grp_id & IMX_MEDIA_GRP_ID_CSI) + imx_media_create_csi_of_links(imxmd, sd); + break; + default: + /* this is an external fwnode subdev */ + imx_media_create_of_links(imxmd, sd); + break; } } -out: - return ret; + return 0; } /* @@ -333,39 +229,45 @@ static int imx_media_add_vdev_to_pad(struct imx_media_dev *imxmd, struct media_pad *srcpad) { struct media_entity *entity = srcpad->entity; - struct imx_media_subdev *imxsd; - struct imx_media_pad *imxpad; + struct imx_media_pad_vdev *pad_vdev; + struct list_head *pad_vdev_list; struct media_link *link; struct v4l2_subdev *sd; - int i, vdev_idx, ret; + int i, ret; /* skip this entity if not a v4l2_subdev */ if (!is_media_entity_v4l2_subdev(entity)) return 0; sd = media_entity_to_v4l2_subdev(entity); - imxsd = imx_media_find_subdev_by_sd(imxmd, sd); - if (IS_ERR(imxsd)) - return PTR_ERR(imxsd); - imxpad = &imxsd->pad[srcpad->index]; - vdev_idx = imxpad->num_vdevs; + pad_vdev_list = to_pad_vdev_list(sd, srcpad->index); + if (!pad_vdev_list) { + v4l2_warn(&imxmd->v4l2_dev, "%s:%u has no vdev list!\n", + entity->name, srcpad->index); + /* + * shouldn't happen, but no reason to fail driver load, + * just skip this entity. + */ + return 0; + } /* just return if we've been here before */ - for (i = 0; i < vdev_idx; i++) - if (vdev == imxpad->vdev[i]) + list_for_each_entry(pad_vdev, pad_vdev_list, list) { + if (pad_vdev->vdev == vdev) return 0; - - if (vdev_idx >= IMX_MEDIA_MAX_VDEVS) { - dev_err(imxmd->md.dev, "can't add %s to pad %s:%u\n", - vdev->vfd->entity.name, entity->name, srcpad->index); - return -ENOSPC; } dev_dbg(imxmd->md.dev, "adding %s to pad %s:%u\n", vdev->vfd->entity.name, entity->name, srcpad->index); - imxpad->vdev[vdev_idx] = vdev; - imxpad->num_vdevs++; + + pad_vdev = devm_kzalloc(imxmd->md.dev, sizeof(*pad_vdev), GFP_KERNEL); + if (!pad_vdev) + return -ENOMEM; + + /* attach this vdev to this pad */ + pad_vdev->vdev = vdev; + list_add_tail(&pad_vdev->list, pad_vdev_list); /* move upstream from this entity's sink pads */ for (i = 0; i < entity->num_pads; i++) { @@ -387,15 +289,49 @@ static int imx_media_add_vdev_to_pad(struct imx_media_dev *imxmd, return 0; } +/* + * For every subdevice, allocate an array of list_head's, one list_head + * for each pad, to hold the list of video devices reachable from that + * pad. + */ +static int imx_media_alloc_pad_vdev_lists(struct imx_media_dev *imxmd) +{ + struct list_head *vdev_lists; + struct media_entity *entity; + struct v4l2_subdev *sd; + int i; + + list_for_each_entry(sd, &imxmd->v4l2_dev.subdevs, list) { + entity = &sd->entity; + vdev_lists = devm_kzalloc( + imxmd->md.dev, + entity->num_pads * sizeof(*vdev_lists), + GFP_KERNEL); + if (!vdev_lists) + return -ENOMEM; + + /* attach to the subdev's host private pointer */ + sd->host_priv = vdev_lists; + + for (i = 0; i < entity->num_pads; i++) + INIT_LIST_HEAD(to_pad_vdev_list(sd, i)); + } + + return 0; +} + /* form the vdev lists in all imx-media source pads */ static int imx_media_create_pad_vdev_lists(struct imx_media_dev *imxmd) { struct imx_media_video_dev *vdev; struct media_link *link; - int i, ret; + int ret; + + ret = imx_media_alloc_pad_vdev_lists(imxmd); + if (ret) + return ret; - for (i = 0; i < imxmd->num_vdevs; i++) { - vdev = imxmd->vdev[i]; + list_for_each_entry(vdev, &imxmd->vdev_list, list) { link = list_first_entry(&vdev->vfd->entity.links, struct media_link, list); ret = imx_media_add_vdev_to_pad(imxmd, vdev, link->source); @@ -410,20 +346,11 @@ static int imx_media_create_pad_vdev_lists(struct imx_media_dev *imxmd) static int imx_media_probe_complete(struct v4l2_async_notifier *notifier) { struct imx_media_dev *imxmd = notifier2dev(notifier); - int i, ret; + int ret; mutex_lock(&imxmd->mutex); - /* make sure all subdevs were bound */ - for (i = 0; i < imxmd->num_subdevs; i++) { - if (!imxmd->subdev[i].sd) { - v4l2_err(&imxmd->v4l2_dev, "unbound subdev!\n"); - ret = -ENODEV; - goto unlock; - } - } - - ret = imx_media_create_links(imxmd); + ret = imx_media_create_links(notifier); if (ret) goto unlock; @@ -492,12 +419,12 @@ static int imx_media_link_notify(struct media_link *link, u32 flags, unsigned int notification) { struct media_entity *source = link->source->entity; - struct imx_media_subdev *imxsd; - struct imx_media_pad *imxpad; + struct imx_media_pad_vdev *pad_vdev; + struct list_head *pad_vdev_list; struct imx_media_dev *imxmd; struct video_device *vfd; struct v4l2_subdev *sd; - int i, pad_idx, ret; + int pad_idx, ret; ret = v4l2_pipeline_link_notify(link, flags, notification); if (ret) @@ -512,10 +439,11 @@ static int imx_media_link_notify(struct media_link *link, u32 flags, imxmd = dev_get_drvdata(sd->v4l2_dev->dev); - imxsd = imx_media_find_subdev_by_sd(imxmd, sd); - if (IS_ERR(imxsd)) - return PTR_ERR(imxsd); - imxpad = &imxsd->pad[pad_idx]; + pad_vdev_list = to_pad_vdev_list(sd, pad_idx); + if (!pad_vdev_list) { + /* shouldn't happen, but no reason to fail link setup */ + return 0; + } /* * Before disabling a link, reset controls for all video @@ -526,8 +454,8 @@ static int imx_media_link_notify(struct media_link *link, u32 flags, */ if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH && !(flags & MEDIA_LNK_FL_ENABLED)) { - for (i = 0; i < imxpad->num_vdevs; i++) { - vfd = imxpad->vdev[i]->vfd; + list_for_each_entry(pad_vdev, pad_vdev_list, list) { + vfd = pad_vdev->vdev->vfd; dev_dbg(imxmd->md.dev, "reset controls for %s\n", vfd->entity.name); @@ -536,8 +464,8 @@ static int imx_media_link_notify(struct media_link *link, u32 flags, } } else if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH && (link->flags & MEDIA_LNK_FL_ENABLED)) { - for (i = 0; i < imxpad->num_vdevs; i++) { - vfd = imxpad->vdev[i]->vfd; + list_for_each_entry(pad_vdev, pad_vdev_list, list) { + vfd = pad_vdev->vdev->vfd; dev_dbg(imxmd->md.dev, "refresh controls for %s\n", vfd->entity.name); @@ -559,9 +487,10 @@ static int imx_media_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *node = dev->of_node; - struct imx_media_subdev *csi[4] = {0}; + struct imx_media_async_subdev *imxasd; + struct v4l2_async_subdev **subdevs; struct imx_media_dev *imxmd; - int ret; + int num_subdevs, i, ret; imxmd = devm_kzalloc(dev, sizeof(*imxmd), GFP_KERNEL); if (!imxmd) @@ -590,29 +519,44 @@ static int imx_media_probe(struct platform_device *pdev) dev_set_drvdata(imxmd->v4l2_dev.dev, imxmd); - ret = imx_media_of_parse(imxmd, &csi, node); + INIT_LIST_HEAD(&imxmd->asd_list); + INIT_LIST_HEAD(&imxmd->vdev_list); + + ret = imx_media_add_of_subdevs(imxmd, node); if (ret) { v4l2_err(&imxmd->v4l2_dev, - "imx_media_of_parse failed with %d\n", ret); + "add_of_subdevs failed with %d\n", ret); goto unreg_dev; } - ret = imx_media_add_internal_subdevs(imxmd, csi); + ret = imx_media_add_internal_subdevs(imxmd); if (ret) { v4l2_err(&imxmd->v4l2_dev, "add_internal_subdevs failed with %d\n", ret); goto unreg_dev; } + num_subdevs = imxmd->subdev_notifier.num_subdevs; + /* no subdevs? just bail */ - imxmd->num_subdevs = imxmd->subdev_notifier.num_subdevs; - if (imxmd->num_subdevs == 0) { + if (num_subdevs == 0) { ret = -ENODEV; goto unreg_dev; } + subdevs = devm_kzalloc(imxmd->md.dev, sizeof(*subdevs) * num_subdevs, + GFP_KERNEL); + if (!subdevs) { + ret = -ENOMEM; + goto unreg_dev; + } + + i = 0; + list_for_each_entry(imxasd, &imxmd->asd_list, list) + subdevs[i++] = &imxasd->asd; + /* prepare the async subdev notifier and register it */ - imxmd->subdev_notifier.subdevs = imxmd->async_ptrs; + imxmd->subdev_notifier.subdevs = subdevs; imxmd->subdev_notifier.ops = &imx_media_subdev_ops; ret = v4l2_async_notifier_register(&imxmd->v4l2_dev, &imxmd->subdev_notifier); diff --git a/drivers/staging/media/imx/imx-media-internal-sd.c b/drivers/staging/media/imx/imx-media-internal-sd.c index cdfbf40..70833fe 100644 --- a/drivers/staging/media/imx/imx-media-internal-sd.c +++ b/drivers/staging/media/imx/imx-media-internal-sd.c @@ -60,73 +60,68 @@ static const struct internal_subdev_id { }, }; +struct internal_subdev; + struct internal_link { - const struct internal_subdev_id *remote_id; + const struct internal_subdev *remote; + int local_pad; int remote_pad; }; +/* max pads per internal-sd */ +#define MAX_INTERNAL_PADS 8 +/* max links per internal-sd pad */ +#define MAX_INTERNAL_LINKS 8 + struct internal_pad { - bool devnode; /* does this pad link to a device node */ - struct internal_link link[IMX_MEDIA_MAX_LINKS]; + struct internal_link link[MAX_INTERNAL_LINKS]; }; static const struct internal_subdev { const struct internal_subdev_id *id; - struct internal_pad pad[IMX_MEDIA_MAX_PADS]; - int num_sink_pads; - int num_src_pads; -} internal_subdev[num_isd] = { + struct internal_pad pad[MAX_INTERNAL_PADS]; +} int_subdev[num_isd] = { [isd_csi0] = { .id = &isd_id[isd_csi0], - .num_sink_pads = CSI_NUM_SINK_PADS, - .num_src_pads = CSI_NUM_SRC_PADS, .pad[CSI_SRC_PAD_DIRECT] = { .link = { { - .remote_id = &isd_id[isd_ic_prp], + .local_pad = CSI_SRC_PAD_DIRECT, + .remote = &int_subdev[isd_ic_prp], .remote_pad = PRP_SINK_PAD, }, { - .remote_id = &isd_id[isd_vdic], + .local_pad = CSI_SRC_PAD_DIRECT, + .remote = &int_subdev[isd_vdic], .remote_pad = VDIC_SINK_PAD_DIRECT, }, }, }, - .pad[CSI_SRC_PAD_IDMAC] = { - .devnode = true, - }, }, [isd_csi1] = { .id = &isd_id[isd_csi1], - .num_sink_pads = CSI_NUM_SINK_PADS, - .num_src_pads = CSI_NUM_SRC_PADS, .pad[CSI_SRC_PAD_DIRECT] = { .link = { { - .remote_id = &isd_id[isd_ic_prp], + .local_pad = CSI_SRC_PAD_DIRECT, + .remote = &int_subdev[isd_ic_prp], .remote_pad = PRP_SINK_PAD, }, { - .remote_id = &isd_id[isd_vdic], + .local_pad = CSI_SRC_PAD_DIRECT, + .remote = &int_subdev[isd_vdic], .remote_pad = VDIC_SINK_PAD_DIRECT, }, }, }, - .pad[CSI_SRC_PAD_IDMAC] = { - .devnode = true, - }, }, [isd_vdic] = { .id = &isd_id[isd_vdic], - .num_sink_pads = VDIC_NUM_SINK_PADS, - .num_src_pads = VDIC_NUM_SRC_PADS, - .pad[VDIC_SINK_PAD_IDMAC] = { - .devnode = true, - }, .pad[VDIC_SRC_PAD_DIRECT] = { .link = { { - .remote_id = &isd_id[isd_ic_prp], + .local_pad = VDIC_SRC_PAD_DIRECT, + .remote = &int_subdev[isd_ic_prp], .remote_pad = PRP_SINK_PAD, }, }, @@ -135,12 +130,11 @@ static const struct internal_subdev { [isd_ic_prp] = { .id = &isd_id[isd_ic_prp], - .num_sink_pads = PRP_NUM_SINK_PADS, - .num_src_pads = PRP_NUM_SRC_PADS, .pad[PRP_SRC_PAD_PRPENC] = { .link = { { - .remote_id = &isd_id[isd_ic_prpenc], + .local_pad = PRP_SRC_PAD_PRPENC, + .remote = &int_subdev[isd_ic_prpenc], .remote_pad = 0, }, }, @@ -148,7 +142,8 @@ static const struct internal_subdev { .pad[PRP_SRC_PAD_PRPVF] = { .link = { { - .remote_id = &isd_id[isd_ic_prpvf], + .local_pad = PRP_SRC_PAD_PRPVF, + .remote = &int_subdev[isd_ic_prpvf], .remote_pad = 0, }, }, @@ -157,70 +152,111 @@ static const struct internal_subdev { [isd_ic_prpenc] = { .id = &isd_id[isd_ic_prpenc], - .num_sink_pads = PRPENCVF_NUM_SINK_PADS, - .num_src_pads = PRPENCVF_NUM_SRC_PADS, - .pad[PRPENCVF_SRC_PAD] = { - .devnode = true, - }, }, [isd_ic_prpvf] = { .id = &isd_id[isd_ic_prpvf], - .num_sink_pads = PRPENCVF_NUM_SINK_PADS, - .num_src_pads = PRPENCVF_NUM_SRC_PADS, - .pad[PRPENCVF_SRC_PAD] = { - .devnode = true, - }, }, }; -/* form a device name given a group id and ipu id */ -static inline void isd_id_to_devname(char *devname, int sz, - const struct internal_subdev_id *id, - int ipu_id) +/* form a device name given an internal subdev and ipu id */ +static inline void isd_to_devname(char *devname, int sz, + const struct internal_subdev *isd, + int ipu_id) { - int pdev_id = ipu_id * num_isd + id->index; + int pdev_id = ipu_id * num_isd + isd->id->index; - snprintf(devname, sz, "%s.%d", id->name, pdev_id); + snprintf(devname, sz, "%s.%d", isd->id->name, pdev_id); } -/* adds the links from given internal subdev */ -static int add_internal_links(struct imx_media_dev *imxmd, - const struct internal_subdev *isd, - struct imx_media_subdev *imxsd, - int ipu_id) +static const struct internal_subdev *find_intsd_by_grp_id(u32 grp_id) { - int i, num_pads, ret; + enum isd_enum i; - num_pads = isd->num_sink_pads + isd->num_src_pads; + for (i = 0; i < num_isd; i++) { + const struct internal_subdev *isd = &int_subdev[i]; - for (i = 0; i < num_pads; i++) { - const struct internal_pad *intpad = &isd->pad[i]; - struct imx_media_pad *pad = &imxsd->pad[i]; - int j; + if (isd->id->grp_id == grp_id) + return isd; + } - /* init the pad flags for this internal subdev */ - pad->pad.flags = (i < isd->num_sink_pads) ? - MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; - /* export devnode pad flag to the subdevs */ - pad->devnode = intpad->devnode; + return NULL; +} - for (j = 0; ; j++) { - const struct internal_link *link; - char remote_devname[32]; +static struct v4l2_subdev *find_sink(struct imx_media_dev *imxmd, + struct v4l2_subdev *src, + const struct internal_link *link) +{ + char sink_devname[32]; + int ipu_id; + + /* + * retrieve IPU id from subdev name, note: can't get this from + * struct imx_media_internal_sd_platformdata because if src is + * a CSI, it has different struct ipu_client_platformdata which + * does not contain IPU id. + */ + if (sscanf(src->name, "ipu%d", &ipu_id) != 1) + return NULL; + + isd_to_devname(sink_devname, sizeof(sink_devname), + link->remote, ipu_id - 1); + + return imx_media_find_subdev_by_devname(imxmd, sink_devname); +} + +static int create_ipu_internal_link(struct imx_media_dev *imxmd, + struct v4l2_subdev *src, + const struct internal_link *link) +{ + struct v4l2_subdev *sink; + int ret; + + sink = find_sink(imxmd, src, link); + if (!sink) + return -ENODEV; + + v4l2_info(&imxmd->v4l2_dev, "%s:%d -> %s:%d\n", + src->name, link->local_pad, + sink->name, link->remote_pad); + ret = media_create_pad_link(&src->entity, link->local_pad, + &sink->entity, link->remote_pad, 0); + if (ret) + v4l2_err(&imxmd->v4l2_dev, + "create_pad_link failed: %d\n", ret); + + return ret; +} + +int imx_media_create_internal_links(struct imx_media_dev *imxmd, + struct v4l2_subdev *sd) +{ + const struct internal_subdev *intsd; + const struct internal_pad *intpad; + const struct internal_link *link; + struct media_pad *pad; + int i, j, ret; + + intsd = find_intsd_by_grp_id(sd->grp_id); + if (!intsd) + return -ENODEV; + + /* create the source->sink links */ + for (i = 0; i < sd->entity.num_pads; i++) { + intpad = &intsd->pad[i]; + pad = &sd->entity.pads[i]; + + if (!(pad->flags & MEDIA_PAD_FL_SOURCE)) + continue; + + for (j = 0; ; j++) { link = &intpad->link[j]; - if (!link->remote_id) + if (!link->remote) break; - isd_id_to_devname(remote_devname, - sizeof(remote_devname), - link->remote_id, ipu_id); - - ret = imx_media_add_pad_link(imxmd, pad, - NULL, remote_devname, - i, link->remote_pad); + ret = create_ipu_internal_link(imxmd, sd, link); if (ret) return ret; } @@ -230,14 +266,12 @@ static int add_internal_links(struct imx_media_dev *imxmd, } /* register an internal subdev as a platform device */ -static struct imx_media_subdev * -add_internal_subdev(struct imx_media_dev *imxmd, - const struct internal_subdev *isd, - int ipu_id) +static int add_internal_subdev(struct imx_media_dev *imxmd, + const struct internal_subdev *isd, + int ipu_id) { struct imx_media_internal_sd_platformdata pdata; struct platform_device_info pdevinfo = {0}; - struct imx_media_subdev *imxsd; struct platform_device *pdev; pdata.grp_id = isd->id->grp_id; @@ -258,73 +292,51 @@ add_internal_subdev(struct imx_media_dev *imxmd, pdev = platform_device_register_full(&pdevinfo); if (IS_ERR(pdev)) - return ERR_CAST(pdev); + return PTR_ERR(pdev); - imxsd = imx_media_add_async_subdev(imxmd, NULL, pdev); - if (IS_ERR(imxsd)) - return imxsd; - - imxsd->num_sink_pads = isd->num_sink_pads; - imxsd->num_src_pads = isd->num_src_pads; - - return imxsd; + return imx_media_add_async_subdev(imxmd, NULL, pdev); } /* adds the internal subdevs in one ipu */ -static int add_ipu_internal_subdevs(struct imx_media_dev *imxmd, - struct imx_media_subdev *csi0, - struct imx_media_subdev *csi1, - int ipu_id) +static int add_ipu_internal_subdevs(struct imx_media_dev *imxmd, int ipu_id) { enum isd_enum i; - int ret; for (i = 0; i < num_isd; i++) { - const struct internal_subdev *isd = &internal_subdev[i]; - struct imx_media_subdev *imxsd; + const struct internal_subdev *isd = &int_subdev[i]; + int ret; /* * the CSIs are represented in the device-tree, so those - * devices are added already, and are added to the async - * subdev list by of_parse_subdev(), so we are given those - * subdevs as csi0 and csi1. + * devices are already added to the async subdev list by + * of_parse_subdev(). */ switch (isd->id->grp_id) { case IMX_MEDIA_GRP_ID_CSI0: - imxsd = csi0; - break; case IMX_MEDIA_GRP_ID_CSI1: - imxsd = csi1; + ret = 0; break; default: - imxsd = add_internal_subdev(imxmd, isd, ipu_id); + ret = add_internal_subdev(imxmd, isd, ipu_id); break; } - if (IS_ERR(imxsd)) - return PTR_ERR(imxsd); - - /* add the links from this subdev */ - if (imxsd) { - ret = add_internal_links(imxmd, isd, imxsd, ipu_id); - if (ret) - return ret; - } + if (ret) + return ret; } return 0; } -int imx_media_add_internal_subdevs(struct imx_media_dev *imxmd, - struct imx_media_subdev *csi[4]) +int imx_media_add_internal_subdevs(struct imx_media_dev *imxmd) { int ret; - ret = add_ipu_internal_subdevs(imxmd, csi[0], csi[1], 0); + ret = add_ipu_internal_subdevs(imxmd, 0); if (ret) goto remove; - ret = add_ipu_internal_subdevs(imxmd, csi[2], csi[3], 1); + ret = add_ipu_internal_subdevs(imxmd, 1); if (ret) goto remove; @@ -337,13 +349,12 @@ remove: void imx_media_remove_internal_subdevs(struct imx_media_dev *imxmd) { - struct imx_media_subdev *imxsd; - int i; + struct imx_media_async_subdev *imxasd; - for (i = 0; i < imxmd->subdev_notifier.num_subdevs; i++) { - imxsd = &imxmd->subdev[i]; - if (!imxsd->pdev) + list_for_each_entry(imxasd, &imxmd->asd_list, list) { + if (!imxasd->pdev) continue; - platform_device_unregister(imxsd->pdev); + + platform_device_unregister(imxasd->pdev); } } diff --git a/drivers/staging/media/imx/imx-media-of.c b/drivers/staging/media/imx/imx-media-of.c index 12df09f..acde372 100644 --- a/drivers/staging/media/imx/imx-media-of.c +++ b/drivers/staging/media/imx/imx-media-of.c @@ -20,34 +20,6 @@ #include <video/imx-ipu-v3.h> #include "imx-media.h" -static int of_add_pad_link(struct imx_media_dev *imxmd, - struct imx_media_pad *pad, - struct device_node *local_sd_node, - struct device_node *remote_sd_node, - int local_pad, int remote_pad) -{ - dev_dbg(imxmd->md.dev, "%s: adding %s:%d -> %s:%d\n", __func__, - local_sd_node->name, local_pad, - remote_sd_node->name, remote_pad); - - return imx_media_add_pad_link(imxmd, pad, remote_sd_node, NULL, - local_pad, remote_pad); -} - -static void of_parse_sensor(struct imx_media_dev *imxmd, - struct imx_media_subdev *sensor, - struct device_node *sensor_np) -{ - struct device_node *endpoint; - - endpoint = of_graph_get_next_endpoint(sensor_np, NULL); - if (endpoint) { - v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), - &sensor->sensor_ep); - of_node_put(endpoint); - } -} - static int of_get_port_count(const struct device_node *np) { struct device_node *ports, *child; @@ -67,15 +39,14 @@ static int of_get_port_count(const struct device_node *np) } /* - * find the remote device node and remote port id (remote pad #) - * given local endpoint node + * find the remote device node given local endpoint node */ -static void of_get_remote_pad(struct device_node *epnode, - struct device_node **remote_node, - int *remote_pad) +static bool of_get_remote(struct device_node *epnode, + struct device_node **remote_node) { struct device_node *rp, *rpp; struct device_node *remote; + bool is_csi_port; rp = of_graph_get_remote_port(epnode); rpp = of_graph_get_remote_port_parent(epnode); @@ -83,13 +54,12 @@ static void of_get_remote_pad(struct device_node *epnode, if (of_device_is_compatible(rpp, "fsl,imx6q-ipu")) { /* the remote is one of the CSI ports */ remote = rp; - *remote_pad = 0; of_node_put(rpp); + is_csi_port = true; } else { remote = rpp; - if (of_property_read_u32(rp, "reg", remote_pad)) - *remote_pad = 0; of_node_put(rp); + is_csi_port = false; } if (!of_device_is_available(remote)) { @@ -98,130 +68,66 @@ static void of_get_remote_pad(struct device_node *epnode, } else { *remote_node = remote; } + + return is_csi_port; } static int of_parse_subdev(struct imx_media_dev *imxmd, struct device_node *sd_np, - bool is_csi_port, struct imx_media_subdev **subdev) + bool is_csi_port) { - struct imx_media_subdev *imxsd; - int i, num_pads, ret; + int i, num_ports, ret; if (!of_device_is_available(sd_np)) { dev_dbg(imxmd->md.dev, "%s: %s not enabled\n", __func__, sd_np->name); - *subdev = NULL; /* unavailable is not an error */ return 0; } /* register this subdev with async notifier */ - imxsd = imx_media_add_async_subdev(imxmd, sd_np, NULL); - ret = PTR_ERR_OR_ZERO(imxsd); + ret = imx_media_add_async_subdev(imxmd, of_fwnode_handle(sd_np), + NULL); if (ret) { if (ret == -EEXIST) { /* already added, everything is fine */ - *subdev = NULL; return 0; } /* other error, can't continue */ return ret; } - *subdev = imxsd; - - if (is_csi_port) { - /* - * the ipu-csi has one sink port and two source ports. - * The source ports are not represented in the device tree, - * but are described by the internal pads and links later. - */ - num_pads = CSI_NUM_PADS; - imxsd->num_sink_pads = CSI_NUM_SINK_PADS; - } else if (of_device_is_compatible(sd_np, "fsl,imx6-mipi-csi2")) { - num_pads = of_get_port_count(sd_np); - /* the mipi csi2 receiver has only one sink port */ - imxsd->num_sink_pads = 1; - } else if (of_device_is_compatible(sd_np, "video-mux")) { - num_pads = of_get_port_count(sd_np); - /* for the video mux, all but the last port are sinks */ - imxsd->num_sink_pads = num_pads - 1; - } else { - num_pads = of_get_port_count(sd_np); - if (num_pads != 1) { - /* confused, but no reason to give up here */ - dev_warn(imxmd->md.dev, - "%s: unknown device %s with %d ports\n", - __func__, sd_np->name, num_pads); - return 0; - } - - /* - * we got to this node from this single source port, - * there are no sink pads. - */ - imxsd->num_sink_pads = 0; - } - - if (imxsd->num_sink_pads >= num_pads) - return -EINVAL; - - imxsd->num_src_pads = num_pads - imxsd->num_sink_pads; - - dev_dbg(imxmd->md.dev, "%s: %s has %d pads (%d sink, %d src)\n", - __func__, sd_np->name, num_pads, - imxsd->num_sink_pads, imxsd->num_src_pads); /* - * With no sink, this subdev node is the original source - * of video, parse it's media bus for use by the pipeline. + * the ipu-csi has one sink port. The source pads are not + * represented in the device tree by port nodes, but are + * described by the internal pads and links later. */ - if (imxsd->num_sink_pads == 0) - of_parse_sensor(imxmd, imxsd, sd_np); + num_ports = is_csi_port ? 1 : of_get_port_count(sd_np); - for (i = 0; i < num_pads; i++) { + for (i = 0; i < num_ports; i++) { struct device_node *epnode = NULL, *port, *remote_np; - struct imx_media_subdev *remote_imxsd; - struct imx_media_pad *pad; - int remote_pad; - - /* init this pad */ - pad = &imxsd->pad[i]; - pad->pad.flags = (i < imxsd->num_sink_pads) ? - MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; - - if (is_csi_port) - port = (i < imxsd->num_sink_pads) ? sd_np : NULL; - else - port = of_graph_get_port_by_id(sd_np, i); + + port = is_csi_port ? sd_np : of_graph_get_port_by_id(sd_np, i); if (!port) continue; for_each_child_of_node(port, epnode) { - of_get_remote_pad(epnode, &remote_np, &remote_pad); + bool remote_is_csi; + + remote_is_csi = of_get_remote(epnode, &remote_np); if (!remote_np) continue; - ret = of_add_pad_link(imxmd, pad, sd_np, remote_np, - i, remote_pad); + ret = of_parse_subdev(imxmd, remote_np, remote_is_csi); + of_node_put(remote_np); if (ret) break; - - if (i < imxsd->num_sink_pads) { - /* follow sink endpoints upstream */ - ret = of_parse_subdev(imxmd, remote_np, - false, &remote_imxsd); - if (ret) - break; - } - - of_node_put(remote_np); } if (port != sd_np) of_node_put(port); if (ret) { - of_node_put(remote_np); of_node_put(epnode); break; } @@ -230,13 +136,10 @@ of_parse_subdev(struct imx_media_dev *imxmd, struct device_node *sd_np, return ret; } -int imx_media_of_parse(struct imx_media_dev *imxmd, - struct imx_media_subdev *(*csi)[4], - struct device_node *np) +int imx_media_add_of_subdevs(struct imx_media_dev *imxmd, + struct device_node *np) { - struct imx_media_subdev *lcsi; struct device_node *csi_np; - u32 ipu_id, csi_id; int i, ret; for (i = 0; ; i++) { @@ -244,33 +147,120 @@ int imx_media_of_parse(struct imx_media_dev *imxmd, if (!csi_np) break; - ret = of_parse_subdev(imxmd, csi_np, true, &lcsi); + ret = of_parse_subdev(imxmd, csi_np, true); + of_node_put(csi_np); if (ret) - goto err_put; + return ret; + } - ret = of_property_read_u32(csi_np, "reg", &csi_id); - if (ret) { - dev_err(imxmd->md.dev, - "%s: csi port missing reg property!\n", - __func__); - goto err_put; - } + return 0; +} - ipu_id = of_alias_get_id(csi_np->parent, "ipu"); - of_node_put(csi_np); +/* + * Create a single media link to/from sd using a fwnode link. + * + * NOTE: this function assumes an OF port node is equivalent to + * a media pad (port id equal to media pad index), and that an + * OF endpoint node is equivalent to a media link. + */ +static int create_of_link(struct imx_media_dev *imxmd, + struct v4l2_subdev *sd, + struct v4l2_fwnode_link *link) +{ + struct v4l2_subdev *remote, *src, *sink; + int src_pad, sink_pad; - if (ipu_id > 1 || csi_id > 1) { - dev_err(imxmd->md.dev, - "%s: invalid ipu/csi id (%u/%u)\n", - __func__, ipu_id, csi_id); - return -EINVAL; - } + if (link->local_port >= sd->entity.num_pads) + return -EINVAL; + + remote = imx_media_find_subdev_by_fwnode(imxmd, link->remote_node); + if (!remote) + return 0; + + if (sd->entity.pads[link->local_port].flags & MEDIA_PAD_FL_SINK) { + src = remote; + src_pad = link->remote_port; + sink = sd; + sink_pad = link->local_port; + } else { + src = sd; + src_pad = link->local_port; + sink = remote; + sink_pad = link->remote_port; + } + + /* make sure link doesn't already exist before creating */ + if (media_entity_find_link(&src->entity.pads[src_pad], + &sink->entity.pads[sink_pad])) + return 0; + + v4l2_info(sd->v4l2_dev, "%s:%d -> %s:%d\n", + src->name, src_pad, sink->name, sink_pad); + + return media_create_pad_link(&src->entity, src_pad, + &sink->entity, sink_pad, 0); +} + +/* + * Create media links to/from sd using its device-tree endpoints. + */ +int imx_media_create_of_links(struct imx_media_dev *imxmd, + struct v4l2_subdev *sd) +{ + struct v4l2_fwnode_link link; + struct device_node *ep; + int ret; + + for_each_endpoint_of_node(sd->dev->of_node, ep) { + ret = v4l2_fwnode_parse_link(of_fwnode_handle(ep), &link); + if (ret) + continue; - (*csi)[ipu_id * 2 + csi_id] = lcsi; + ret = create_of_link(imxmd, sd, &link); + v4l2_fwnode_put_link(&link); + if (ret) + return ret; + } + + return 0; +} + +/* + * Create media links to the given CSI subdevice's sink pads, + * using its device-tree endpoints. + */ +int imx_media_create_csi_of_links(struct imx_media_dev *imxmd, + struct v4l2_subdev *csi) +{ + struct device_node *csi_np = csi->dev->of_node; + struct fwnode_handle *fwnode, *csi_ep; + struct v4l2_fwnode_link link; + struct device_node *ep; + int ret; + + link.local_node = of_fwnode_handle(csi_np); + link.local_port = CSI_SINK_PAD; + + for_each_child_of_node(csi_np, ep) { + csi_ep = of_fwnode_handle(ep); + + fwnode = fwnode_graph_get_remote_endpoint(csi_ep); + if (!fwnode) + continue; + + fwnode = fwnode_get_parent(fwnode); + fwnode_property_read_u32(fwnode, "reg", &link.remote_port); + fwnode = fwnode_get_next_parent(fwnode); + if (is_of_node(fwnode) && + of_node_cmp(to_of_node(fwnode)->name, "ports") == 0) + fwnode = fwnode_get_next_parent(fwnode); + link.remote_node = fwnode; + + ret = create_of_link(imxmd, csi, &link); + fwnode_handle_put(link.remote_node); + if (ret) + return ret; } return 0; -err_put: - of_node_put(csi_np); - return ret; } diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c index 5952387..13dafa7 100644 --- a/drivers/staging/media/imx/imx-media-utils.c +++ b/drivers/staging/media/imx/imx-media-utils.c @@ -668,38 +668,35 @@ void imx_media_grp_id_to_sd_name(char *sd_name, int sz, u32 grp_id, int ipu_id) } EXPORT_SYMBOL_GPL(imx_media_grp_id_to_sd_name); -struct imx_media_subdev * -imx_media_find_subdev_by_sd(struct imx_media_dev *imxmd, - struct v4l2_subdev *sd) +struct v4l2_subdev * +imx_media_find_subdev_by_fwnode(struct imx_media_dev *imxmd, + struct fwnode_handle *fwnode) { - struct imx_media_subdev *imxsd; - int i; + struct v4l2_subdev *sd; - for (i = 0; i < imxmd->num_subdevs; i++) { - imxsd = &imxmd->subdev[i]; - if (sd == imxsd->sd) - return imxsd; + list_for_each_entry(sd, &imxmd->v4l2_dev.subdevs, list) { + if (sd->fwnode == fwnode) + return sd; } - return ERR_PTR(-ENODEV); + return NULL; } -EXPORT_SYMBOL_GPL(imx_media_find_subdev_by_sd); +EXPORT_SYMBOL_GPL(imx_media_find_subdev_by_fwnode); -struct imx_media_subdev * -imx_media_find_subdev_by_id(struct imx_media_dev *imxmd, u32 grp_id) +struct v4l2_subdev * +imx_media_find_subdev_by_devname(struct imx_media_dev *imxmd, + const char *devname) { - struct imx_media_subdev *imxsd; - int i; + struct v4l2_subdev *sd; - for (i = 0; i < imxmd->num_subdevs; i++) { - imxsd = &imxmd->subdev[i]; - if (imxsd->sd && imxsd->sd->grp_id == grp_id) - return imxsd; + list_for_each_entry(sd, &imxmd->v4l2_dev.subdevs, list) { + if (!strcmp(devname, dev_name(sd->dev))) + return sd; } - return ERR_PTR(-ENODEV); + return NULL; } -EXPORT_SYMBOL_GPL(imx_media_find_subdev_by_id); +EXPORT_SYMBOL_GPL(imx_media_find_subdev_by_devname); /* * Adds a video device to the master video device list. This is called by @@ -708,32 +705,21 @@ EXPORT_SYMBOL_GPL(imx_media_find_subdev_by_id); int imx_media_add_video_device(struct imx_media_dev *imxmd, struct imx_media_video_dev *vdev) { - int vdev_idx, ret = 0; - mutex_lock(&imxmd->mutex); - vdev_idx = imxmd->num_vdevs; - if (vdev_idx >= IMX_MEDIA_MAX_VDEVS) { - dev_err(imxmd->md.dev, - "%s: too many video devices! can't add %s\n", - __func__, vdev->vfd->name); - ret = -ENOSPC; - goto out; - } + list_add_tail(&vdev->list, &imxmd->vdev_list); - imxmd->vdev[vdev_idx] = vdev; - imxmd->num_vdevs++; -out: mutex_unlock(&imxmd->mutex); - return ret; + return 0; } EXPORT_SYMBOL_GPL(imx_media_add_video_device); /* - * Search upstream or downstream for a subdevice in the current pipeline + * Search upstream/downstream for a subdevice in the current pipeline * with given grp_id, starting from start_entity. Returns the subdev's - * source/sink pad that it was reached from. Must be called with - * mdev->graph_mutex held. + * source/sink pad that it was reached from. If grp_id is zero, just + * returns the nearest source/sink pad to start_entity. Must be called + * with mdev->graph_mutex held. */ static struct media_pad * find_pipeline_pad(struct imx_media_dev *imxmd, @@ -756,11 +742,16 @@ find_pipeline_pad(struct imx_media_dev *imxmd, if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) continue; - sd = media_entity_to_v4l2_subdev(pad->entity); - if (sd->grp_id & grp_id) - return pad; + if (grp_id != 0) { + sd = media_entity_to_v4l2_subdev(pad->entity); + if (sd->grp_id & grp_id) + return pad; - return find_pipeline_pad(imxmd, pad->entity, grp_id, upstream); + return find_pipeline_pad(imxmd, pad->entity, + grp_id, upstream); + } else { + return pad; + } } return NULL; @@ -789,7 +780,6 @@ find_upstream_subdev(struct imx_media_dev *imxmd, return pad ? media_entity_to_v4l2_subdev(pad->entity) : NULL; } - /* * Find the upstream mipi-csi2 virtual channel reached from the given * start entity in the current pipeline. @@ -814,11 +804,30 @@ int imx_media_find_mipi_csi2_channel(struct imx_media_dev *imxmd, EXPORT_SYMBOL_GPL(imx_media_find_mipi_csi2_channel); /* + * Find a source pad reached upstream from the given start entity in + * the current pipeline. Must be called with mdev->graph_mutex held. + */ +struct media_pad * +imx_media_find_upstream_pad(struct imx_media_dev *imxmd, + struct media_entity *start_entity, + u32 grp_id) +{ + struct media_pad *pad; + + pad = find_pipeline_pad(imxmd, start_entity, grp_id, true); + if (!pad) + return ERR_PTR(-ENODEV); + + return pad; +} +EXPORT_SYMBOL_GPL(imx_media_find_upstream_pad); + +/* * Find a subdev reached upstream from the given start entity in * the current pipeline. * Must be called with mdev->graph_mutex held. */ -struct imx_media_subdev * +struct v4l2_subdev * imx_media_find_upstream_subdev(struct imx_media_dev *imxmd, struct media_entity *start_entity, u32 grp_id) @@ -829,33 +838,10 @@ imx_media_find_upstream_subdev(struct imx_media_dev *imxmd, if (!sd) return ERR_PTR(-ENODEV); - return imx_media_find_subdev_by_sd(imxmd, sd); + return sd; } EXPORT_SYMBOL_GPL(imx_media_find_upstream_subdev); -struct imx_media_subdev * -__imx_media_find_sensor(struct imx_media_dev *imxmd, - struct media_entity *start_entity) -{ - return imx_media_find_upstream_subdev(imxmd, start_entity, - IMX_MEDIA_GRP_ID_SENSOR); -} -EXPORT_SYMBOL_GPL(__imx_media_find_sensor); - -struct imx_media_subdev * -imx_media_find_sensor(struct imx_media_dev *imxmd, - struct media_entity *start_entity) -{ - struct imx_media_subdev *sensor; - - mutex_lock(&imxmd->md.graph_mutex); - sensor = __imx_media_find_sensor(imxmd, start_entity); - mutex_unlock(&imxmd->md.graph_mutex); - - return sensor; -} -EXPORT_SYMBOL_GPL(imx_media_find_sensor); - /* * Turn current pipeline streaming on/off starting from entity. */ diff --git a/drivers/staging/media/imx/imx-media.h b/drivers/staging/media/imx/imx-media.h index ac3ab11..2fd6dfd 100644 --- a/drivers/staging/media/imx/imx-media.h +++ b/drivers/staging/media/imx/imx-media.h @@ -11,6 +11,7 @@ #ifndef _IMX_MEDIA_H #define _IMX_MEDIA_H +#include <linux/platform_device.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-fwnode.h> @@ -19,26 +20,6 @@ #include <video/imx-ipu-v3.h> /* - * This is somewhat arbitrary, but we need at least: - * - 4 video devices per IPU - * - 3 IC subdevs per IPU - * - 1 VDIC subdev per IPU - * - 2 CSI subdevs per IPU - * - 1 mipi-csi2 receiver subdev - * - 2 video-mux subdevs - * - 2 camera sensor subdevs per IPU (1 parallel, 1 mipi-csi2) - * - */ -/* max video devices */ -#define IMX_MEDIA_MAX_VDEVS 8 -/* max subdevices */ -#define IMX_MEDIA_MAX_SUBDEVS 32 -/* max pads per subdev */ -#define IMX_MEDIA_MAX_PADS 16 -/* max links per pad */ -#define IMX_MEDIA_MAX_LINKS 8 - -/* * Pad definitions for the subdevs with multiple source or * sink pads */ @@ -51,9 +32,6 @@ enum { CSI_NUM_PADS, }; -#define CSI_NUM_SINK_PADS 1 -#define CSI_NUM_SRC_PADS 2 - /* ipu_vdic */ enum { VDIC_SINK_PAD_DIRECT = 0, @@ -62,9 +40,6 @@ enum { VDIC_NUM_PADS, }; -#define VDIC_NUM_SINK_PADS 2 -#define VDIC_NUM_SRC_PADS 1 - /* ipu_ic_prp */ enum { PRP_SINK_PAD = 0, @@ -73,9 +48,6 @@ enum { PRP_NUM_PADS, }; -#define PRP_NUM_SINK_PADS 1 -#define PRP_NUM_SRC_PADS 2 - /* ipu_ic_prpencvf */ enum { PRPENCVF_SINK_PAD = 0, @@ -83,9 +55,6 @@ enum { PRPENCVF_NUM_PADS, }; -#define PRPENCVF_NUM_SINK_PADS 1 -#define PRPENCVF_NUM_SRC_PADS 1 - /* How long to wait for EOF interrupts in the buffer-capture subdevs */ #define IMX_MEDIA_EOF_TIMEOUT 1000 @@ -110,6 +79,9 @@ struct imx_media_video_dev { /* the user format */ struct v4l2_format fmt; const struct imx_media_pixfmt *cc; + + /* links this vdev to master list */ + struct list_head list; }; static inline struct imx_media_buffer *to_imx_media_vb(struct vb2_buffer *vb) @@ -119,25 +91,24 @@ static inline struct imx_media_buffer *to_imx_media_vb(struct vb2_buffer *vb) return container_of(vbuf, struct imx_media_buffer, vbuf); } -struct imx_media_link { - struct device_node *remote_sd_node; - char remote_devname[32]; - int local_pad; - int remote_pad; -}; +/* + * to support control inheritance to video devices, this + * retrieves a pad's list_head of video devices that can + * be reached from the pad. Note that only the lists in + * source pads get populated, sink pads have empty lists. + */ +static inline struct list_head * +to_pad_vdev_list(struct v4l2_subdev *sd, int pad_index) +{ + struct list_head *vdev_list = sd->host_priv; -struct imx_media_pad { - struct media_pad pad; - struct imx_media_link link[IMX_MEDIA_MAX_LINKS]; - bool devnode; /* does this pad link to a device node */ - int num_links; - - /* - * list of video devices that can be reached from this pad, - * list is only valid for source pads. - */ - struct imx_media_video_dev *vdev[IMX_MEDIA_MAX_VDEVS]; - int num_vdevs; + return vdev_list ? &vdev_list[pad_index] : NULL; +} + +/* an entry in a pad's video device list */ +struct imx_media_pad_vdev { + struct imx_media_video_dev *vdev; + struct list_head list; }; struct imx_media_internal_sd_platformdata { @@ -146,23 +117,20 @@ struct imx_media_internal_sd_platformdata { int ipu_id; }; -struct imx_media_subdev { - struct v4l2_async_subdev asd; - struct v4l2_subdev *sd; /* set when bound */ - - struct imx_media_pad pad[IMX_MEDIA_MAX_PADS]; - int num_sink_pads; - int num_src_pads; - /* the platform device if this is an internal subdev */ +struct imx_media_async_subdev { + struct v4l2_async_subdev asd; + /* the platform device of IPU-internal subdevs */ struct platform_device *pdev; - /* the devname is needed for async devname match */ - char devname[32]; - - /* if this is a sensor */ - struct v4l2_fwnode_endpoint sensor_ep; + struct list_head list; }; +static inline struct imx_media_async_subdev * +to_imx_media_asd(struct v4l2_async_subdev *asd) +{ + return container_of(asd, struct imx_media_async_subdev, asd); +} + struct imx_media_dev { struct media_device md; struct v4l2_device v4l2_dev; @@ -172,19 +140,14 @@ struct imx_media_dev { struct mutex mutex; /* protect elements below */ - /* master subdevice list */ - struct imx_media_subdev subdev[IMX_MEDIA_MAX_SUBDEVS]; - int num_subdevs; - /* master video device list */ - struct imx_media_video_dev *vdev[IMX_MEDIA_MAX_VDEVS]; - int num_vdevs; + struct list_head vdev_list; /* IPUs this media driver control, valid after subdevs bound */ struct ipu_soc *ipu[2]; /* for async subdev registration */ - struct v4l2_async_subdev *async_ptrs[IMX_MEDIA_MAX_SUBDEVS]; + struct list_head asd_list; struct v4l2_async_notifier subdev_notifier; }; @@ -194,6 +157,7 @@ enum codespace_sel { CS_SEL_ANY, }; +/* imx-media-utils.c */ const struct imx_media_pixfmt * imx_media_find_format(u32 fourcc, enum codespace_sel cs_sel, bool allow_bayer); int imx_media_enum_format(u32 *fourcc, u32 index, enum codespace_sel cs_sel); @@ -205,7 +169,6 @@ int imx_media_enum_mbus_format(u32 *code, u32 index, enum codespace_sel cs_sel, const struct imx_media_pixfmt * imx_media_find_ipu_format(u32 code, enum codespace_sel cs_sel); int imx_media_enum_ipu_format(u32 *code, u32 index, enum codespace_sel cs_sel); - int imx_media_init_mbus_fmt(struct v4l2_mbus_framefmt *mbus, u32 width, u32 height, u32 code, u32 field, const struct imx_media_pixfmt **cc); @@ -219,48 +182,26 @@ int imx_media_mbus_fmt_to_ipu_image(struct ipu_image *image, struct v4l2_mbus_framefmt *mbus); int imx_media_ipu_image_to_mbus_fmt(struct v4l2_mbus_framefmt *mbus, struct ipu_image *image); - -struct imx_media_subdev * -imx_media_find_async_subdev(struct imx_media_dev *imxmd, - struct device_node *np, - const char *devname); -struct imx_media_subdev * -imx_media_add_async_subdev(struct imx_media_dev *imxmd, - struct device_node *np, - struct platform_device *pdev); -int imx_media_add_pad_link(struct imx_media_dev *imxmd, - struct imx_media_pad *pad, - struct device_node *remote_node, - const char *remote_devname, - int local_pad, int remote_pad); - void imx_media_grp_id_to_sd_name(char *sd_name, int sz, u32 grp_id, int ipu_id); - -int imx_media_add_internal_subdevs(struct imx_media_dev *imxmd, - struct imx_media_subdev *csi[4]); -void imx_media_remove_internal_subdevs(struct imx_media_dev *imxmd); - -struct imx_media_subdev * -imx_media_find_subdev_by_sd(struct imx_media_dev *imxmd, - struct v4l2_subdev *sd); -struct imx_media_subdev * -imx_media_find_subdev_by_id(struct imx_media_dev *imxmd, - u32 grp_id); +struct v4l2_subdev * +imx_media_find_subdev_by_fwnode(struct imx_media_dev *imxmd, + struct fwnode_handle *fwnode); +struct v4l2_subdev * +imx_media_find_subdev_by_devname(struct imx_media_dev *imxmd, + const char *devname); int imx_media_add_video_device(struct imx_media_dev *imxmd, struct imx_media_video_dev *vdev); int imx_media_find_mipi_csi2_channel(struct imx_media_dev *imxmd, struct media_entity *start_entity); -struct imx_media_subdev * +struct media_pad * +imx_media_find_upstream_pad(struct imx_media_dev *imxmd, + struct media_entity *start_entity, + u32 grp_id); +struct v4l2_subdev * imx_media_find_upstream_subdev(struct imx_media_dev *imxmd, struct media_entity *start_entity, u32 grp_id); -struct imx_media_subdev * -__imx_media_find_sensor(struct imx_media_dev *imxmd, - struct media_entity *start_entity); -struct imx_media_subdev * -imx_media_find_sensor(struct imx_media_dev *imxmd, - struct media_entity *start_entity); struct imx_media_dma_buf { void *virt; @@ -278,6 +219,11 @@ int imx_media_pipeline_set_stream(struct imx_media_dev *imxmd, struct media_entity *entity, bool on); +/* imx-media-dev.c */ +int imx_media_add_async_subdev(struct imx_media_dev *imxmd, + struct fwnode_handle *fwnode, + struct platform_device *pdev); + /* imx-media-fim.c */ struct imx_media_fim; void imx_media_fim_eof_monitor(struct imx_media_fim *fim, ktime_t timestamp); @@ -288,14 +234,19 @@ int imx_media_fim_add_controls(struct imx_media_fim *fim); struct imx_media_fim *imx_media_fim_init(struct v4l2_subdev *sd); void imx_media_fim_free(struct imx_media_fim *fim); +/* imx-media-internal-sd.c */ +int imx_media_add_internal_subdevs(struct imx_media_dev *imxmd); +int imx_media_create_internal_links(struct imx_media_dev *imxmd, + struct v4l2_subdev *sd); +void imx_media_remove_internal_subdevs(struct imx_media_dev *imxmd); + /* imx-media-of.c */ -struct imx_media_subdev * -imx_media_of_find_subdev(struct imx_media_dev *imxmd, - struct device_node *np, - const char *name); -int imx_media_of_parse(struct imx_media_dev *dev, - struct imx_media_subdev *(*csi)[4], - struct device_node *np); +int imx_media_add_of_subdevs(struct imx_media_dev *dev, + struct device_node *np); +int imx_media_create_of_links(struct imx_media_dev *imxmd, + struct v4l2_subdev *sd); +int imx_media_create_csi_of_links(struct imx_media_dev *imxmd, + struct v4l2_subdev *csi); /* imx-media-capture.c */ struct imx_media_video_dev * @@ -310,16 +261,14 @@ void imx_media_capture_device_set_format(struct imx_media_video_dev *vdev, void imx_media_capture_device_error(struct imx_media_video_dev *vdev); /* subdev group ids */ -#define IMX_MEDIA_GRP_ID_SENSOR (1 << 8) -#define IMX_MEDIA_GRP_ID_VIDMUX (1 << 9) -#define IMX_MEDIA_GRP_ID_CSI2 (1 << 10) -#define IMX_MEDIA_GRP_ID_CSI_BIT 11 +#define IMX_MEDIA_GRP_ID_CSI2 BIT(8) +#define IMX_MEDIA_GRP_ID_CSI_BIT 9 #define IMX_MEDIA_GRP_ID_CSI (0x3 << IMX_MEDIA_GRP_ID_CSI_BIT) -#define IMX_MEDIA_GRP_ID_CSI0 (1 << IMX_MEDIA_GRP_ID_CSI_BIT) +#define IMX_MEDIA_GRP_ID_CSI0 BIT(IMX_MEDIA_GRP_ID_CSI_BIT) #define IMX_MEDIA_GRP_ID_CSI1 (2 << IMX_MEDIA_GRP_ID_CSI_BIT) -#define IMX_MEDIA_GRP_ID_VDIC (1 << 13) -#define IMX_MEDIA_GRP_ID_IC_PRP (1 << 14) -#define IMX_MEDIA_GRP_ID_IC_PRPENC (1 << 15) -#define IMX_MEDIA_GRP_ID_IC_PRPVF (1 << 16) +#define IMX_MEDIA_GRP_ID_VDIC BIT(11) +#define IMX_MEDIA_GRP_ID_IC_PRP BIT(12) +#define IMX_MEDIA_GRP_ID_IC_PRPENC BIT(13) +#define IMX_MEDIA_GRP_ID_IC_PRPVF BIT(14) #endif diff --git a/drivers/staging/media/imx/imx6-mipi-csi2.c b/drivers/staging/media/imx/imx6-mipi-csi2.c index 5061f3f..477d191 100644 --- a/drivers/staging/media/imx/imx6-mipi-csi2.c +++ b/drivers/staging/media/imx/imx6-mipi-csi2.c @@ -252,8 +252,8 @@ static int csi2_dphy_wait_stopstate(struct csi2_dev *csi2) u32 mask, reg; int ret; - mask = PHY_STOPSTATECLK | - ((csi2->bus.num_data_lanes - 1) << PHY_STOPSTATEDATA_BIT); + mask = PHY_STOPSTATECLK | (((1 << csi2->bus.num_data_lanes) - 1) << + PHY_STOPSTATEDATA_BIT); ret = readl_poll_timeout(csi2->base + CSI2_PHY_STATE, reg, (reg & mask) == mask, 0, 500000); diff --git a/drivers/staging/media/lirc/Kconfig b/drivers/staging/media/lirc/Kconfig deleted file mode 100644 index 3e350a9..0000000 --- a/drivers/staging/media/lirc/Kconfig +++ /dev/null @@ -1,21 +0,0 @@ -# -# LIRC driver(s) configuration -# -menuconfig LIRC_STAGING - bool "Linux Infrared Remote Control IR receiver/transmitter drivers" - depends on LIRC - help - Say Y here, and all supported Linux Infrared Remote Control IR and - RF receiver and transmitter drivers will be displayed. When paired - with a remote control and the lirc daemon, the receiver drivers - allow control of your Linux system via remote control. - -if LIRC_STAGING - -config LIRC_ZILOG - tristate "Zilog/Hauppauge IR Transmitter" - depends on LIRC && I2C - help - Driver for the Zilog/Hauppauge IR Transmitter, found on - PVR-150/500, HVR-1200/1250/1700/1800, HD-PVR and other cards -endif diff --git a/drivers/staging/media/lirc/Makefile b/drivers/staging/media/lirc/Makefile deleted file mode 100644 index 6655624..0000000 --- a/drivers/staging/media/lirc/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# Makefile for the lirc drivers. -# - -# Each configuration option enables a list of files. - -obj-$(CONFIG_LIRC_ZILOG) += lirc_zilog.o diff --git a/drivers/staging/media/lirc/TODO b/drivers/staging/media/lirc/TODO deleted file mode 100644 index a97800a..0000000 --- a/drivers/staging/media/lirc/TODO +++ /dev/null @@ -1,36 +0,0 @@ -1. Both ir-kbd-i2c and lirc_zilog provide support for RX events for -the chips supported by lirc_zilog. Before moving lirc_zilog out of staging: - -a. ir-kbd-i2c needs a module parameter added to allow the user to tell - ir-kbd-i2c to ignore Z8 IR units. - -b. lirc_zilog should provide Rx key presses to the rc core like ir-kbd-i2c - does. - - -2. lirc_zilog module ref-counting need examination. It has not been -verified that cdev and lirc_dev will take the proper module references on -lirc_zilog to prevent removal of lirc_zilog when the /dev/lircN device node -is open. - -(The good news is ref-counting of lirc_zilog internal structures appears to be -complete. Testing has shown the cx18 module can be unloaded out from under -irw + lircd + lirc_dev, with the /dev/lirc0 device node open, with no adverse -effects. The cx18 module could then be reloaded and irw properly began -receiving button presses again and ir_send worked without error.) - - -3. Bridge drivers, if able, should provide a chip reset() callback -to lirc_zilog via struct IR_i2c_init_data. cx18 and ivtv already have routines -to perform Z8 chip resets via GPIO manipulations. This would allow lirc_zilog -to bring the chip back to normal when it hangs, in the same places the -original lirc_pvr150 driver code does. This is not strictly needed, so it -is not required to move lirc_zilog out of staging. - -Note: Both lirc_zilog and ir-kbd-i2c support the Zilog Z8 for IR, as programmed -and installed on Hauppauge products. When working on either module, developers -must consider at least the following bridge drivers which mention an IR Rx unit -at address 0x71 (indicative of a Z8): - - ivtv cx18 hdpvr pvrusb2 bt8xx cx88 saa7134 - diff --git a/drivers/staging/media/lirc/lirc_zilog.c b/drivers/staging/media/lirc/lirc_zilog.c deleted file mode 100644 index a003603..0000000 --- a/drivers/staging/media/lirc/lirc_zilog.c +++ /dev/null @@ -1,1653 +0,0 @@ -/* - * i2c IR lirc driver for devices with zilog IR processors - * - * Copyright (c) 2000 Gerd Knorr <kraxel@goldbach.in-berlin.de> - * modified for PixelView (BT878P+W/FM) by - * Michal Kochanowicz <mkochano@pld.org.pl> - * Christoph Bartelmus <lirc@bartelmus.de> - * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by - * Ulrich Mueller <ulrich.mueller42@web.de> - * modified for Asus TV-Box and Creative/VisionTek BreakOut-Box by - * Stefan Jahn <stefan@lkcc.org> - * modified for inclusion into kernel sources by - * Jerome Brock <jbrock@users.sourceforge.net> - * modified for Leadtek Winfast PVR2000 by - * Thomas Reitmayr (treitmayr@yahoo.com) - * modified for Hauppauge PVR-150 IR TX device by - * Mark Weaver <mark@npsl.co.uk> - * changed name from lirc_pvr150 to lirc_zilog, works on more than pvr-150 - * Jarod Wilson <jarod@redhat.com> - * - * parts are cut&pasted from the lirc_i2c.c driver - * - * Numerous changes updating lirc_zilog.c in kernel 2.6.38 and later are - * Copyright (C) 2011 Andy Walls <awalls@md.metrocast.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include <linux/module.h> -#include <linux/kmod.h> -#include <linux/kernel.h> -#include <linux/sched/signal.h> -#include <linux/fs.h> -#include <linux/poll.h> -#include <linux/string.h> -#include <linux/timer.h> -#include <linux/delay.h> -#include <linux/completion.h> -#include <linux/errno.h> -#include <linux/slab.h> -#include <linux/i2c.h> -#include <linux/firmware.h> -#include <linux/vmalloc.h> - -#include <linux/mutex.h> -#include <linux/kthread.h> - -#include <media/lirc_dev.h> -#include <media/lirc.h> - -/* Max transfer size done by I2C transfer functions */ -#define MAX_XFER_SIZE 64 - -struct IR; - -struct IR_rx { - struct kref ref; - struct IR *ir; - - /* RX device */ - struct mutex client_lock; - struct i2c_client *c; - - /* RX polling thread data */ - struct task_struct *task; - - /* RX read data */ - unsigned char b[3]; - bool hdpvr_data_fmt; -}; - -struct IR_tx { - struct kref ref; - struct IR *ir; - - /* TX device */ - struct mutex client_lock; - struct i2c_client *c; - - /* TX additional actions needed */ - int need_boot; - bool post_tx_ready_poll; -}; - -struct IR { - struct kref ref; - struct list_head list; - - /* FIXME spinlock access to l->features */ - struct lirc_dev *l; - struct lirc_buffer rbuf; - - struct mutex ir_lock; - atomic_t open_count; - - struct device *dev; - struct i2c_adapter *adapter; - - spinlock_t rx_ref_lock; /* struct IR_rx kref get()/put() */ - struct IR_rx *rx; - - spinlock_t tx_ref_lock; /* struct IR_tx kref get()/put() */ - struct IR_tx *tx; -}; - -/* IR transceiver instance object list */ -/* - * This lock is used for the following: - * a. ir_devices_list access, insertions, deletions - * b. struct IR kref get()s and put()s - * c. serialization of ir_probe() for the two i2c_clients for a Z8 - */ -static DEFINE_MUTEX(ir_devices_lock); -static LIST_HEAD(ir_devices_list); - -/* Block size for IR transmitter */ -#define TX_BLOCK_SIZE 99 - -/* Hauppauge IR transmitter data */ -struct tx_data_struct { - /* Boot block */ - unsigned char *boot_data; - - /* Start of binary data block */ - unsigned char *datap; - - /* End of binary data block */ - unsigned char *endp; - - /* Number of installed codesets */ - unsigned int num_code_sets; - - /* Pointers to codesets */ - unsigned char **code_sets; - - /* Global fixed data template */ - int fixed[TX_BLOCK_SIZE]; -}; - -static struct tx_data_struct *tx_data; -static struct mutex tx_data_lock; - - -/* module parameters */ -static bool debug; /* debug output */ -static bool tx_only; /* only handle the IR Tx function */ - - -/* struct IR reference counting */ -static struct IR *get_ir_device(struct IR *ir, bool ir_devices_lock_held) -{ - if (ir_devices_lock_held) { - kref_get(&ir->ref); - } else { - mutex_lock(&ir_devices_lock); - kref_get(&ir->ref); - mutex_unlock(&ir_devices_lock); - } - return ir; -} - -static void release_ir_device(struct kref *ref) -{ - struct IR *ir = container_of(ref, struct IR, ref); - - /* - * Things should be in this state by now: - * ir->rx set to NULL and deallocated - happens before ir->rx->ir put() - * ir->rx->task kthread stopped - happens before ir->rx->ir put() - * ir->tx set to NULL and deallocated - happens before ir->tx->ir put() - * ir->open_count == 0 - happens on final close() - * ir_lock, tx_ref_lock, rx_ref_lock, all released - */ - if (ir->l) - lirc_unregister_device(ir->l); - - if (kfifo_initialized(&ir->rbuf.fifo)) - lirc_buffer_free(&ir->rbuf); - list_del(&ir->list); - kfree(ir); -} - -static int put_ir_device(struct IR *ir, bool ir_devices_lock_held) -{ - int released; - - if (ir_devices_lock_held) - return kref_put(&ir->ref, release_ir_device); - - mutex_lock(&ir_devices_lock); - released = kref_put(&ir->ref, release_ir_device); - mutex_unlock(&ir_devices_lock); - - return released; -} - -/* struct IR_rx reference counting */ -static struct IR_rx *get_ir_rx(struct IR *ir) -{ - struct IR_rx *rx; - - spin_lock(&ir->rx_ref_lock); - rx = ir->rx; - if (rx) - kref_get(&rx->ref); - spin_unlock(&ir->rx_ref_lock); - return rx; -} - -static void destroy_rx_kthread(struct IR_rx *rx, bool ir_devices_lock_held) -{ - /* end up polling thread */ - if (!IS_ERR_OR_NULL(rx->task)) { - kthread_stop(rx->task); - rx->task = NULL; - /* Put the ir ptr that ir_probe() gave to the rx poll thread */ - put_ir_device(rx->ir, ir_devices_lock_held); - } -} - -static void release_ir_rx(struct kref *ref) -{ - struct IR_rx *rx = container_of(ref, struct IR_rx, ref); - struct IR *ir = rx->ir; - - /* - * This release function can't do all the work, as we want - * to keep the rx_ref_lock a spinlock, and killing the poll thread - * and releasing the ir reference can cause a sleep. That work is - * performed by put_ir_rx() - */ - ir->l->features &= ~LIRC_CAN_REC_LIRCCODE; - /* Don't put_ir_device(rx->ir) here; lock can't be freed yet */ - ir->rx = NULL; - /* Don't do the kfree(rx) here; we still need to kill the poll thread */ -} - -static int put_ir_rx(struct IR_rx *rx, bool ir_devices_lock_held) -{ - int released; - struct IR *ir = rx->ir; - - spin_lock(&ir->rx_ref_lock); - released = kref_put(&rx->ref, release_ir_rx); - spin_unlock(&ir->rx_ref_lock); - /* Destroy the rx kthread while not holding the spinlock */ - if (released) { - destroy_rx_kthread(rx, ir_devices_lock_held); - kfree(rx); - /* Make sure we're not still in a poll_table somewhere */ - wake_up_interruptible(&ir->rbuf.wait_poll); - } - /* Do a reference put() for the rx->ir reference, if we released rx */ - if (released) - put_ir_device(ir, ir_devices_lock_held); - return released; -} - -/* struct IR_tx reference counting */ -static struct IR_tx *get_ir_tx(struct IR *ir) -{ - struct IR_tx *tx; - - spin_lock(&ir->tx_ref_lock); - tx = ir->tx; - if (tx) - kref_get(&tx->ref); - spin_unlock(&ir->tx_ref_lock); - return tx; -} - -static void release_ir_tx(struct kref *ref) -{ - struct IR_tx *tx = container_of(ref, struct IR_tx, ref); - struct IR *ir = tx->ir; - - ir->l->features &= ~LIRC_CAN_SEND_LIRCCODE; - /* Don't put_ir_device(tx->ir) here, so our lock doesn't get freed */ - ir->tx = NULL; - kfree(tx); -} - -static int put_ir_tx(struct IR_tx *tx, bool ir_devices_lock_held) -{ - int released; - struct IR *ir = tx->ir; - - spin_lock(&ir->tx_ref_lock); - released = kref_put(&tx->ref, release_ir_tx); - spin_unlock(&ir->tx_ref_lock); - /* Do a reference put() for the tx->ir reference, if we released tx */ - if (released) - put_ir_device(ir, ir_devices_lock_held); - return released; -} - -static int add_to_buf(struct IR *ir) -{ - __u16 code; - unsigned char codes[2]; - unsigned char keybuf[6]; - int got_data = 0; - int ret; - int failures = 0; - unsigned char sendbuf[1] = { 0 }; - struct lirc_buffer *rbuf = ir->l->buf; - struct IR_rx *rx; - struct IR_tx *tx; - - if (lirc_buffer_full(rbuf)) { - dev_dbg(ir->dev, "buffer overflow\n"); - return -EOVERFLOW; - } - - rx = get_ir_rx(ir); - if (!rx) - return -ENXIO; - - /* Ensure our rx->c i2c_client remains valid for the duration */ - mutex_lock(&rx->client_lock); - if (!rx->c) { - mutex_unlock(&rx->client_lock); - put_ir_rx(rx, false); - return -ENXIO; - } - - tx = get_ir_tx(ir); - - /* - * service the device as long as it is returning - * data and we have space - */ - do { - if (kthread_should_stop()) { - ret = -ENODATA; - break; - } - - /* - * Lock i2c bus for the duration. RX/TX chips interfere so - * this is worth it - */ - mutex_lock(&ir->ir_lock); - - if (kthread_should_stop()) { - mutex_unlock(&ir->ir_lock); - ret = -ENODATA; - break; - } - - /* - * Send random "poll command" (?) Windows driver does this - * and it is a good point to detect chip failure. - */ - ret = i2c_master_send(rx->c, sendbuf, 1); - if (ret != 1) { - dev_err(ir->dev, "i2c_master_send failed with %d\n", - ret); - if (failures >= 3) { - mutex_unlock(&ir->ir_lock); - dev_err(ir->dev, - "unable to read from the IR chip after 3 resets, giving up\n"); - break; - } - - /* Looks like the chip crashed, reset it */ - dev_err(ir->dev, - "polling the IR receiver chip failed, trying reset\n"); - - set_current_state(TASK_UNINTERRUPTIBLE); - if (kthread_should_stop()) { - mutex_unlock(&ir->ir_lock); - ret = -ENODATA; - break; - } - schedule_timeout((100 * HZ + 999) / 1000); - if (tx) - tx->need_boot = 1; - - ++failures; - mutex_unlock(&ir->ir_lock); - ret = 0; - continue; - } - - if (kthread_should_stop()) { - mutex_unlock(&ir->ir_lock); - ret = -ENODATA; - break; - } - ret = i2c_master_recv(rx->c, keybuf, sizeof(keybuf)); - mutex_unlock(&ir->ir_lock); - if (ret != sizeof(keybuf)) { - dev_err(ir->dev, - "i2c_master_recv failed with %d -- keeping last read buffer\n", - ret); - } else { - rx->b[0] = keybuf[3]; - rx->b[1] = keybuf[4]; - rx->b[2] = keybuf[5]; - dev_dbg(ir->dev, - "key (0x%02x/0x%02x)\n", - rx->b[0], rx->b[1]); - } - - /* key pressed ? */ - if (rx->hdpvr_data_fmt) { - if (got_data && (keybuf[0] == 0x80)) { - ret = 0; - break; - } else if (got_data && (keybuf[0] == 0x00)) { - ret = -ENODATA; - break; - } - } else if ((rx->b[0] & 0x80) == 0) { - ret = got_data ? 0 : -ENODATA; - break; - } - - /* look what we have */ - code = (((__u16)rx->b[0] & 0x7f) << 6) | (rx->b[1] >> 2); - - codes[0] = (code >> 8) & 0xff; - codes[1] = code & 0xff; - - /* return it */ - lirc_buffer_write(rbuf, codes); - ++got_data; - ret = 0; - } while (!lirc_buffer_full(rbuf)); - - mutex_unlock(&rx->client_lock); - if (tx) - put_ir_tx(tx, false); - put_ir_rx(rx, false); - return ret; -} - -/* - * Main function of the polling thread -- from lirc_dev. - * We don't fit the LIRC model at all anymore. This is horrible, but - * basically we have a single RX/TX device with a nasty failure mode - * that needs to be accounted for across the pair. lirc lets us provide - * fops, but prevents us from using the internal polling, etc. if we do - * so. Hence the replication. Might be neater to extend the LIRC model - * to account for this but I'd think it's a very special case of seriously - * messed up hardware. - */ -static int lirc_thread(void *arg) -{ - struct IR *ir = arg; - struct lirc_buffer *rbuf = ir->l->buf; - - dev_dbg(ir->dev, "poll thread started\n"); - - while (!kthread_should_stop()) { - set_current_state(TASK_INTERRUPTIBLE); - - /* if device not opened, we can sleep half a second */ - if (atomic_read(&ir->open_count) == 0) { - schedule_timeout(HZ / 2); - continue; - } - - /* - * This is ~113*2 + 24 + jitter (2*repeat gap + code length). - * We use this interval as the chip resets every time you poll - * it (bad!). This is therefore just sufficient to catch all - * of the button presses. It makes the remote much more - * responsive. You can see the difference by running irw and - * holding down a button. With 100ms, the old polling - * interval, you'll notice breaks in the repeat sequence - * corresponding to lost keypresses. - */ - schedule_timeout((260 * HZ) / 1000); - if (kthread_should_stop()) - break; - if (!add_to_buf(ir)) - wake_up_interruptible(&rbuf->wait_poll); - } - - dev_dbg(ir->dev, "poll thread ended\n"); - return 0; -} - -/* safe read of a uint32 (always network byte order) */ -static int read_uint32(unsigned char **data, - unsigned char *endp, unsigned int *val) -{ - if (*data + 4 > endp) - return 0; - *val = ((*data)[0] << 24) | ((*data)[1] << 16) | - ((*data)[2] << 8) | (*data)[3]; - *data += 4; - return 1; -} - -/* safe read of a uint8 */ -static int read_uint8(unsigned char **data, - unsigned char *endp, unsigned char *val) -{ - if (*data + 1 > endp) - return 0; - *val = *((*data)++); - return 1; -} - -/* safe skipping of N bytes */ -static int skip(unsigned char **data, - unsigned char *endp, unsigned int distance) -{ - if (*data + distance > endp) - return 0; - *data += distance; - return 1; -} - -/* decompress key data into the given buffer */ -static int get_key_data(unsigned char *buf, - unsigned int codeset, unsigned int key) -{ - unsigned char *data, *endp, *diffs, *key_block; - unsigned char keys, ndiffs, id; - unsigned int base, lim, pos, i; - - /* Binary search for the codeset */ - for (base = 0, lim = tx_data->num_code_sets; lim; lim >>= 1) { - pos = base + (lim >> 1); - data = tx_data->code_sets[pos]; - - if (!read_uint32(&data, tx_data->endp, &i)) - goto corrupt; - - if (i == codeset) { - break; - } else if (codeset > i) { - base = pos + 1; - --lim; - } - } - /* Not found? */ - if (!lim) - return -EPROTO; - - /* Set end of data block */ - endp = pos < tx_data->num_code_sets - 1 ? - tx_data->code_sets[pos + 1] : tx_data->endp; - - /* Read the block header */ - if (!read_uint8(&data, endp, &keys) || - !read_uint8(&data, endp, &ndiffs) || - ndiffs > TX_BLOCK_SIZE || keys == 0) - goto corrupt; - - /* Save diffs & skip */ - diffs = data; - if (!skip(&data, endp, ndiffs)) - goto corrupt; - - /* Read the id of the first key */ - if (!read_uint8(&data, endp, &id)) - goto corrupt; - - /* Unpack the first key's data */ - for (i = 0; i < TX_BLOCK_SIZE; ++i) { - if (tx_data->fixed[i] == -1) { - if (!read_uint8(&data, endp, &buf[i])) - goto corrupt; - } else { - buf[i] = (unsigned char)tx_data->fixed[i]; - } - } - - /* Early out key found/not found */ - if (key == id) - return 0; - if (keys == 1) - return -EPROTO; - - /* Sanity check */ - key_block = data; - if (!skip(&data, endp, (keys - 1) * (ndiffs + 1))) - goto corrupt; - - /* Binary search for the key */ - for (base = 0, lim = keys - 1; lim; lim >>= 1) { - /* Seek to block */ - unsigned char *key_data; - - pos = base + (lim >> 1); - key_data = key_block + (ndiffs + 1) * pos; - - if (*key_data == key) { - /* skip key id */ - ++key_data; - - /* found, so unpack the diffs */ - for (i = 0; i < ndiffs; ++i) { - unsigned char val; - - if (!read_uint8(&key_data, endp, &val) || - diffs[i] >= TX_BLOCK_SIZE) - goto corrupt; - buf[diffs[i]] = val; - } - - return 0; - } else if (key > *key_data) { - base = pos + 1; - --lim; - } - } - /* Key not found */ - return -EPROTO; - -corrupt: - pr_err("firmware is corrupt\n"); - return -EFAULT; -} - -/* send a block of data to the IR TX device */ -static int send_data_block(struct IR_tx *tx, unsigned char *data_block) -{ - int i, j, ret; - unsigned char buf[5]; - - for (i = 0; i < TX_BLOCK_SIZE;) { - int tosend = TX_BLOCK_SIZE - i; - - if (tosend > 4) - tosend = 4; - buf[0] = (unsigned char)(i + 1); - for (j = 0; j < tosend; ++j) - buf[1 + j] = data_block[i + j]; - dev_dbg(tx->ir->dev, "%*ph", 5, buf); - ret = i2c_master_send(tx->c, buf, tosend + 1); - if (ret != tosend + 1) { - dev_err(tx->ir->dev, - "i2c_master_send failed with %d\n", ret); - return ret < 0 ? ret : -EFAULT; - } - i += tosend; - } - return 0; -} - -/* send boot data to the IR TX device */ -static int send_boot_data(struct IR_tx *tx) -{ - int ret, i; - unsigned char buf[4]; - - /* send the boot block */ - ret = send_data_block(tx, tx_data->boot_data); - if (ret != 0) - return ret; - - /* Hit the go button to activate the new boot data */ - buf[0] = 0x00; - buf[1] = 0x20; - ret = i2c_master_send(tx->c, buf, 2); - if (ret != 2) { - dev_err(tx->ir->dev, "i2c_master_send failed with %d\n", ret); - return ret < 0 ? ret : -EFAULT; - } - - /* - * Wait for zilog to settle after hitting go post boot block upload. - * Without this delay, the HD-PVR and HVR-1950 both return an -EIO - * upon attempting to get firmware revision, and tx probe thus fails. - */ - for (i = 0; i < 10; i++) { - ret = i2c_master_send(tx->c, buf, 1); - if (ret == 1) - break; - udelay(100); - } - - if (ret != 1) { - dev_err(tx->ir->dev, "i2c_master_send failed with %d\n", ret); - return ret < 0 ? ret : -EFAULT; - } - - /* Here comes the firmware version... (hopefully) */ - ret = i2c_master_recv(tx->c, buf, 4); - if (ret != 4) { - dev_err(tx->ir->dev, "i2c_master_recv failed with %d\n", ret); - return 0; - } - if ((buf[0] != 0x80) && (buf[0] != 0xa0)) { - dev_err(tx->ir->dev, "unexpected IR TX init response: %02x\n", - buf[0]); - return 0; - } - dev_notice(tx->ir->dev, - "Zilog/Hauppauge IR blaster firmware version %d.%d.%d loaded\n", - buf[1], buf[2], buf[3]); - - return 0; -} - -/* unload "firmware", lock held */ -static void fw_unload_locked(void) -{ - if (tx_data) { - vfree(tx_data->code_sets); - - vfree(tx_data->datap); - - vfree(tx_data); - tx_data = NULL; - pr_debug("successfully unloaded IR blaster firmware\n"); - } -} - -/* unload "firmware" for the IR TX device */ -static void fw_unload(void) -{ - mutex_lock(&tx_data_lock); - fw_unload_locked(); - mutex_unlock(&tx_data_lock); -} - -/* load "firmware" for the IR TX device */ -static int fw_load(struct IR_tx *tx) -{ - int ret; - unsigned int i; - unsigned char *data, version, num_global_fixed; - const struct firmware *fw_entry; - - /* Already loaded? */ - mutex_lock(&tx_data_lock); - if (tx_data) { - ret = 0; - goto out; - } - - /* Request codeset data file */ - ret = request_firmware(&fw_entry, "haup-ir-blaster.bin", tx->ir->dev); - if (ret != 0) { - dev_err(tx->ir->dev, - "firmware haup-ir-blaster.bin not available (%d)\n", - ret); - ret = ret < 0 ? ret : -EFAULT; - goto out; - } - dev_dbg(tx->ir->dev, "firmware of size %zu loaded\n", fw_entry->size); - - /* Parse the file */ - tx_data = vmalloc(sizeof(*tx_data)); - if (!tx_data) { - release_firmware(fw_entry); - ret = -ENOMEM; - goto out; - } - tx_data->code_sets = NULL; - - /* Copy the data so hotplug doesn't get confused and timeout */ - tx_data->datap = vmalloc(fw_entry->size); - if (!tx_data->datap) { - release_firmware(fw_entry); - vfree(tx_data); - ret = -ENOMEM; - goto out; - } - memcpy(tx_data->datap, fw_entry->data, fw_entry->size); - tx_data->endp = tx_data->datap + fw_entry->size; - release_firmware(fw_entry); fw_entry = NULL; - - /* Check version */ - data = tx_data->datap; - if (!read_uint8(&data, tx_data->endp, &version)) - goto corrupt; - if (version != 1) { - dev_err(tx->ir->dev, - "unsupported code set file version (%u, expected 1) -- please upgrade to a newer driver\n", - version); - fw_unload_locked(); - ret = -EFAULT; - goto out; - } - - /* Save boot block for later */ - tx_data->boot_data = data; - if (!skip(&data, tx_data->endp, TX_BLOCK_SIZE)) - goto corrupt; - - if (!read_uint32(&data, tx_data->endp, - &tx_data->num_code_sets)) - goto corrupt; - - dev_dbg(tx->ir->dev, "%u IR blaster codesets loaded\n", - tx_data->num_code_sets); - - tx_data->code_sets = vmalloc( - tx_data->num_code_sets * sizeof(char *)); - if (!tx_data->code_sets) { - fw_unload_locked(); - ret = -ENOMEM; - goto out; - } - - for (i = 0; i < TX_BLOCK_SIZE; ++i) - tx_data->fixed[i] = -1; - - /* Read global fixed data template */ - if (!read_uint8(&data, tx_data->endp, &num_global_fixed) || - num_global_fixed > TX_BLOCK_SIZE) - goto corrupt; - for (i = 0; i < num_global_fixed; ++i) { - unsigned char pos, val; - - if (!read_uint8(&data, tx_data->endp, &pos) || - !read_uint8(&data, tx_data->endp, &val) || - pos >= TX_BLOCK_SIZE) - goto corrupt; - tx_data->fixed[pos] = (int)val; - } - - /* Filch out the position of each code set */ - for (i = 0; i < tx_data->num_code_sets; ++i) { - unsigned int id; - unsigned char keys; - unsigned char ndiffs; - - /* Save the codeset position */ - tx_data->code_sets[i] = data; - - /* Read header */ - if (!read_uint32(&data, tx_data->endp, &id) || - !read_uint8(&data, tx_data->endp, &keys) || - !read_uint8(&data, tx_data->endp, &ndiffs) || - ndiffs > TX_BLOCK_SIZE || keys == 0) - goto corrupt; - - /* skip diff positions */ - if (!skip(&data, tx_data->endp, ndiffs)) - goto corrupt; - - /* - * After the diffs we have the first key id + data - - * global fixed - */ - if (!skip(&data, tx_data->endp, - 1 + TX_BLOCK_SIZE - num_global_fixed)) - goto corrupt; - - /* Then we have keys-1 blocks of key id+diffs */ - if (!skip(&data, tx_data->endp, - (ndiffs + 1) * (keys - 1))) - goto corrupt; - } - ret = 0; - goto out; - -corrupt: - dev_err(tx->ir->dev, "firmware is corrupt\n"); - fw_unload_locked(); - ret = -EFAULT; - -out: - mutex_unlock(&tx_data_lock); - return ret; -} - -/* copied from lirc_dev */ -static ssize_t read(struct file *filep, char __user *outbuf, size_t n, - loff_t *ppos) -{ - struct IR *ir = lirc_get_pdata(filep); - struct IR_rx *rx; - struct lirc_buffer *rbuf = ir->l->buf; - int ret = 0, written = 0, retries = 0; - unsigned int m; - DECLARE_WAITQUEUE(wait, current); - - dev_dbg(ir->dev, "read called\n"); - if (n % rbuf->chunk_size) { - dev_dbg(ir->dev, "read result = -EINVAL\n"); - return -EINVAL; - } - - rx = get_ir_rx(ir); - if (!rx) - return -ENXIO; - - /* - * we add ourselves to the task queue before buffer check - * to avoid losing scan code (in case when queue is awaken somewhere - * between while condition checking and scheduling) - */ - add_wait_queue(&rbuf->wait_poll, &wait); - set_current_state(TASK_INTERRUPTIBLE); - - /* - * while we didn't provide 'length' bytes, device is opened in blocking - * mode and 'copy_to_user' is happy, wait for data. - */ - while (written < n && ret == 0) { - if (lirc_buffer_empty(rbuf)) { - /* - * According to the read(2) man page, 'written' can be - * returned as less than 'n', instead of blocking - * again, returning -EWOULDBLOCK, or returning - * -ERESTARTSYS - */ - if (written) - break; - if (filep->f_flags & O_NONBLOCK) { - ret = -EWOULDBLOCK; - break; - } - if (signal_pending(current)) { - ret = -ERESTARTSYS; - break; - } - schedule(); - set_current_state(TASK_INTERRUPTIBLE); - } else { - unsigned char buf[MAX_XFER_SIZE]; - - if (rbuf->chunk_size > sizeof(buf)) { - dev_err(ir->dev, - "chunk_size is too big (%d)!\n", - rbuf->chunk_size); - ret = -EINVAL; - break; - } - m = lirc_buffer_read(rbuf, buf); - if (m == rbuf->chunk_size) { - ret = copy_to_user(outbuf + written, buf, - rbuf->chunk_size); - written += rbuf->chunk_size; - } else { - retries++; - } - if (retries >= 5) { - dev_err(ir->dev, "Buffer read failed!\n"); - ret = -EIO; - } - } - } - - remove_wait_queue(&rbuf->wait_poll, &wait); - put_ir_rx(rx, false); - set_current_state(TASK_RUNNING); - - dev_dbg(ir->dev, "read result = %d (%s)\n", ret, - ret ? "Error" : "OK"); - - return ret ? ret : written; -} - -/* send a keypress to the IR TX device */ -static int send_code(struct IR_tx *tx, unsigned int code, unsigned int key) -{ - unsigned char data_block[TX_BLOCK_SIZE]; - unsigned char buf[2]; - int i, ret; - - /* Get data for the codeset/key */ - ret = get_key_data(data_block, code, key); - - if (ret == -EPROTO) { - dev_err(tx->ir->dev, - "failed to get data for code %u, key %u -- check lircd.conf entries\n", - code, key); - return ret; - } else if (ret != 0) { - return ret; - } - - /* Send the data block */ - ret = send_data_block(tx, data_block); - if (ret != 0) - return ret; - - /* Send data block length? */ - buf[0] = 0x00; - buf[1] = 0x40; - ret = i2c_master_send(tx->c, buf, 2); - if (ret != 2) { - dev_err(tx->ir->dev, "i2c_master_send failed with %d\n", ret); - return ret < 0 ? ret : -EFAULT; - } - - /* Give the z8 a moment to process data block */ - for (i = 0; i < 10; i++) { - ret = i2c_master_send(tx->c, buf, 1); - if (ret == 1) - break; - udelay(100); - } - - if (ret != 1) { - dev_err(tx->ir->dev, "i2c_master_send failed with %d\n", ret); - return ret < 0 ? ret : -EFAULT; - } - - /* Send finished download? */ - ret = i2c_master_recv(tx->c, buf, 1); - if (ret != 1) { - dev_err(tx->ir->dev, "i2c_master_recv failed with %d\n", ret); - return ret < 0 ? ret : -EFAULT; - } - if (buf[0] != 0xA0) { - dev_err(tx->ir->dev, "unexpected IR TX response #1: %02x\n", - buf[0]); - return -EFAULT; - } - - /* Send prepare command? */ - buf[0] = 0x00; - buf[1] = 0x80; - ret = i2c_master_send(tx->c, buf, 2); - if (ret != 2) { - dev_err(tx->ir->dev, "i2c_master_send failed with %d\n", ret); - return ret < 0 ? ret : -EFAULT; - } - - /* - * The sleep bits aren't necessary on the HD PVR, and in fact, the - * last i2c_master_recv always fails with a -5, so for now, we're - * going to skip this whole mess and say we're done on the HD PVR - */ - if (!tx->post_tx_ready_poll) { - dev_dbg(tx->ir->dev, "sent code %u, key %u\n", code, key); - return 0; - } - - /* - * This bit NAKs until the device is ready, so we retry it - * sleeping a bit each time. This seems to be what the windows - * driver does, approximately. - * Try for up to 1s. - */ - for (i = 0; i < 20; ++i) { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((50 * HZ + 999) / 1000); - ret = i2c_master_send(tx->c, buf, 1); - if (ret == 1) - break; - dev_dbg(tx->ir->dev, - "NAK expected: i2c_master_send failed with %d (try %d)\n", - ret, i + 1); - } - if (ret != 1) { - dev_err(tx->ir->dev, - "IR TX chip never got ready: last i2c_master_send failed with %d\n", - ret); - return ret < 0 ? ret : -EFAULT; - } - - /* Seems to be an 'ok' response */ - i = i2c_master_recv(tx->c, buf, 1); - if (i != 1) { - dev_err(tx->ir->dev, "i2c_master_recv failed with %d\n", ret); - return -EFAULT; - } - if (buf[0] != 0x80) { - dev_err(tx->ir->dev, "unexpected IR TX response #2: %02x\n", - buf[0]); - return -EFAULT; - } - - /* Oh good, it worked */ - dev_dbg(tx->ir->dev, "sent code %u, key %u\n", code, key); - return 0; -} - -/* - * Write a code to the device. We take in a 32-bit number (an int) and then - * decode this to a codeset/key index. The key data is then decompressed and - * sent to the device. We have a spin lock as per i2c documentation to prevent - * multiple concurrent sends which would probably cause the device to explode. - */ -static ssize_t write(struct file *filep, const char __user *buf, size_t n, - loff_t *ppos) -{ - struct IR *ir = lirc_get_pdata(filep); - struct IR_tx *tx; - size_t i; - int failures = 0; - - /* Validate user parameters */ - if (n % sizeof(int)) - return -EINVAL; - - /* Get a struct IR_tx reference */ - tx = get_ir_tx(ir); - if (!tx) - return -ENXIO; - - /* Ensure our tx->c i2c_client remains valid for the duration */ - mutex_lock(&tx->client_lock); - if (!tx->c) { - mutex_unlock(&tx->client_lock); - put_ir_tx(tx, false); - return -ENXIO; - } - - /* Lock i2c bus for the duration */ - mutex_lock(&ir->ir_lock); - - /* Send each keypress */ - for (i = 0; i < n;) { - int ret = 0; - int command; - - if (copy_from_user(&command, buf + i, sizeof(command))) { - mutex_unlock(&ir->ir_lock); - mutex_unlock(&tx->client_lock); - put_ir_tx(tx, false); - return -EFAULT; - } - - /* Send boot data first if required */ - if (tx->need_boot == 1) { - /* Make sure we have the 'firmware' loaded, first */ - ret = fw_load(tx); - if (ret != 0) { - mutex_unlock(&ir->ir_lock); - mutex_unlock(&tx->client_lock); - put_ir_tx(tx, false); - if (ret != -ENOMEM) - ret = -EIO; - return ret; - } - /* Prep the chip for transmitting codes */ - ret = send_boot_data(tx); - if (ret == 0) - tx->need_boot = 0; - } - - /* Send the code */ - if (ret == 0) { - ret = send_code(tx, (unsigned int)command >> 16, - (unsigned int)command & 0xFFFF); - if (ret == -EPROTO) { - mutex_unlock(&ir->ir_lock); - mutex_unlock(&tx->client_lock); - put_ir_tx(tx, false); - return ret; - } - } - - /* - * Hmm, a failure. If we've had a few then give up, otherwise - * try a reset - */ - if (ret != 0) { - /* Looks like the chip crashed, reset it */ - dev_err(tx->ir->dev, - "sending to the IR transmitter chip failed, trying reset\n"); - - if (failures >= 3) { - dev_err(tx->ir->dev, - "unable to send to the IR chip after 3 resets, giving up\n"); - mutex_unlock(&ir->ir_lock); - mutex_unlock(&tx->client_lock); - put_ir_tx(tx, false); - return ret; - } - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((100 * HZ + 999) / 1000); - tx->need_boot = 1; - ++failures; - } else { - i += sizeof(int); - } - } - - /* Release i2c bus */ - mutex_unlock(&ir->ir_lock); - - mutex_unlock(&tx->client_lock); - - /* Give back our struct IR_tx reference */ - put_ir_tx(tx, false); - - /* All looks good */ - return n; -} - -/* copied from lirc_dev */ -static __poll_t poll(struct file *filep, poll_table *wait) -{ - struct IR *ir = lirc_get_pdata(filep); - struct IR_rx *rx; - struct lirc_buffer *rbuf = ir->l->buf; - __poll_t ret; - - dev_dbg(ir->dev, "%s called\n", __func__); - - rx = get_ir_rx(ir); - if (!rx) { - /* - * Revisit this, if our poll function ever reports writeable - * status for Tx - */ - dev_dbg(ir->dev, "%s result = POLLERR\n", __func__); - return POLLERR; - } - - /* - * Add our lirc_buffer's wait_queue to the poll_table. A wake up on - * that buffer's wait queue indicates we may have a new poll status. - */ - poll_wait(filep, &rbuf->wait_poll, wait); - - /* Indicate what ops could happen immediately without blocking */ - ret = lirc_buffer_empty(rbuf) ? 0 : (POLLIN | POLLRDNORM); - - dev_dbg(ir->dev, "%s result = %s\n", __func__, - ret ? "POLLIN|POLLRDNORM" : "none"); - return ret; -} - -static long ioctl(struct file *filep, unsigned int cmd, unsigned long arg) -{ - struct IR *ir = lirc_get_pdata(filep); - unsigned long __user *uptr = (unsigned long __user *)arg; - int result; - unsigned long mode, features; - - features = ir->l->features; - - switch (cmd) { - case LIRC_GET_LENGTH: - result = put_user(13UL, uptr); - break; - case LIRC_GET_FEATURES: - result = put_user(features, uptr); - break; - case LIRC_GET_REC_MODE: - if (!(features & LIRC_CAN_REC_MASK)) - return -ENOTTY; - - result = put_user(LIRC_REC2MODE - (features & LIRC_CAN_REC_MASK), - uptr); - break; - case LIRC_SET_REC_MODE: - if (!(features & LIRC_CAN_REC_MASK)) - return -ENOTTY; - - result = get_user(mode, uptr); - if (!result && !(LIRC_MODE2REC(mode) & features)) - result = -ENOTTY; - break; - case LIRC_GET_SEND_MODE: - if (!(features & LIRC_CAN_SEND_MASK)) - return -ENOTTY; - - result = put_user(LIRC_MODE_LIRCCODE, uptr); - break; - case LIRC_SET_SEND_MODE: - if (!(features & LIRC_CAN_SEND_MASK)) - return -ENOTTY; - - result = get_user(mode, uptr); - if (!result && mode != LIRC_MODE_LIRCCODE) - return -EINVAL; - break; - default: - return -EINVAL; - } - return result; -} - -/* - * Open the IR device. - */ -static int open(struct inode *node, struct file *filep) -{ - struct IR *ir; - - lirc_init_pdata(node, filep); - ir = lirc_get_pdata(filep); - - atomic_inc(&ir->open_count); - - nonseekable_open(node, filep); - return 0; -} - -/* Close the IR device */ -static int close(struct inode *node, struct file *filep) -{ - struct IR *ir = lirc_get_pdata(filep); - - atomic_dec(&ir->open_count); - - put_ir_device(ir, false); - return 0; -} - -static int ir_remove(struct i2c_client *client); -static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id); - -#define ID_FLAG_TX 0x01 -#define ID_FLAG_HDPVR 0x02 - -static const struct i2c_device_id ir_transceiver_id[] = { - { "ir_tx_z8f0811_haup", ID_FLAG_TX }, - { "ir_rx_z8f0811_haup", 0 }, - { "ir_tx_z8f0811_hdpvr", ID_FLAG_HDPVR | ID_FLAG_TX }, - { "ir_rx_z8f0811_hdpvr", ID_FLAG_HDPVR }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ir_transceiver_id); - -static struct i2c_driver driver = { - .driver = { - .name = "Zilog/Hauppauge i2c IR", - }, - .probe = ir_probe, - .remove = ir_remove, - .id_table = ir_transceiver_id, -}; - -static const struct file_operations lirc_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = read, - .write = write, - .poll = poll, - .unlocked_ioctl = ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = ioctl, -#endif - .open = open, - .release = close -}; - -static int ir_remove(struct i2c_client *client) -{ - if (strncmp("ir_tx_z8", client->name, 8) == 0) { - struct IR_tx *tx = i2c_get_clientdata(client); - - if (tx) { - mutex_lock(&tx->client_lock); - tx->c = NULL; - mutex_unlock(&tx->client_lock); - put_ir_tx(tx, false); - } - } else if (strncmp("ir_rx_z8", client->name, 8) == 0) { - struct IR_rx *rx = i2c_get_clientdata(client); - - if (rx) { - mutex_lock(&rx->client_lock); - rx->c = NULL; - mutex_unlock(&rx->client_lock); - put_ir_rx(rx, false); - } - } - return 0; -} - -/* ir_devices_lock must be held */ -static struct IR *get_ir_device_by_adapter(struct i2c_adapter *adapter) -{ - struct IR *ir; - - if (list_empty(&ir_devices_list)) - return NULL; - - list_for_each_entry(ir, &ir_devices_list, list) - if (ir->adapter == adapter) { - get_ir_device(ir, true); - return ir; - } - - return NULL; -} - -static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) -{ - struct IR *ir; - struct IR_tx *tx; - struct IR_rx *rx; - struct i2c_adapter *adap = client->adapter; - int ret; - bool tx_probe = false; - - dev_dbg(&client->dev, "%s: %s on i2c-%d (%s), client addr=0x%02x\n", - __func__, id->name, adap->nr, adap->name, client->addr); - - /* - * The IR receiver is at i2c address 0x71. - * The IR transmitter is at i2c address 0x70. - */ - - if (id->driver_data & ID_FLAG_TX) - tx_probe = true; - else if (tx_only) /* module option */ - return -ENXIO; - - pr_info("probing IR %s on %s (i2c-%d)\n", - tx_probe ? "Tx" : "Rx", adap->name, adap->nr); - - mutex_lock(&ir_devices_lock); - - /* Use a single struct IR instance for both the Rx and Tx functions */ - ir = get_ir_device_by_adapter(adap); - if (!ir) { - ir = kzalloc(sizeof(*ir), GFP_KERNEL); - if (!ir) { - ret = -ENOMEM; - goto out_no_ir; - } - kref_init(&ir->ref); - - /* store for use in ir_probe() again, and open() later on */ - INIT_LIST_HEAD(&ir->list); - list_add_tail(&ir->list, &ir_devices_list); - - ir->adapter = adap; - ir->dev = &adap->dev; - mutex_init(&ir->ir_lock); - atomic_set(&ir->open_count, 0); - spin_lock_init(&ir->tx_ref_lock); - spin_lock_init(&ir->rx_ref_lock); - - /* set lirc_dev stuff */ - ir->l = lirc_allocate_device(); - if (!ir->l) { - ret = -ENOMEM; - goto out_put_ir; - } - - snprintf(ir->l->name, sizeof(ir->l->name), "lirc_zilog"); - ir->l->code_length = 13; - ir->l->fops = &lirc_fops; - ir->l->owner = THIS_MODULE; - ir->l->dev.parent = &adap->dev; - - /* - * FIXME this is a pointer reference to us, but no refcount. - * - * This OK for now, since lirc_dev currently won't touch this - * buffer as we provide our own lirc_fops. - * - * Currently our own lirc_fops rely on this ir->l->buf pointer - */ - ir->l->buf = &ir->rbuf; - /* This will be returned by lirc_get_pdata() */ - ir->l->data = ir; - ret = lirc_buffer_init(ir->l->buf, 2, BUFLEN / 2); - if (ret) { - lirc_free_device(ir->l); - ir->l = NULL; - goto out_put_ir; - } - } - - if (tx_probe) { - /* Get the IR_rx instance for later, if already allocated */ - rx = get_ir_rx(ir); - - /* Set up a struct IR_tx instance */ - tx = kzalloc(sizeof(*tx), GFP_KERNEL); - if (!tx) { - ret = -ENOMEM; - goto out_put_xx; - } - kref_init(&tx->ref); - ir->tx = tx; - - ir->l->features |= LIRC_CAN_SEND_LIRCCODE; - mutex_init(&tx->client_lock); - tx->c = client; - tx->need_boot = 1; - tx->post_tx_ready_poll = - (id->driver_data & ID_FLAG_HDPVR) ? false : true; - - /* An ir ref goes to the struct IR_tx instance */ - tx->ir = get_ir_device(ir, true); - - /* A tx ref goes to the i2c_client */ - i2c_set_clientdata(client, get_ir_tx(ir)); - - /* - * Load the 'firmware'. We do this before registering with - * lirc_dev, so the first firmware load attempt does not happen - * after a open() or write() call on the device. - * - * Failure here is not deemed catastrophic, so the receiver will - * still be usable. Firmware load will be retried in write(), - * if it is needed. - */ - fw_load(tx); - - /* Proceed only if the Rx client is also ready or not needed */ - if (!rx && !tx_only) { - dev_info(tx->ir->dev, - "probe of IR Tx on %s (i2c-%d) done. Waiting on IR Rx.\n", - adap->name, adap->nr); - goto out_ok; - } - } else { - /* Get the IR_tx instance for later, if already allocated */ - tx = get_ir_tx(ir); - - /* Set up a struct IR_rx instance */ - rx = kzalloc(sizeof(*rx), GFP_KERNEL); - if (!rx) { - ret = -ENOMEM; - goto out_put_xx; - } - kref_init(&rx->ref); - ir->rx = rx; - - ir->l->features |= LIRC_CAN_REC_LIRCCODE; - mutex_init(&rx->client_lock); - rx->c = client; - rx->hdpvr_data_fmt = - (id->driver_data & ID_FLAG_HDPVR) ? true : false; - - /* An ir ref goes to the struct IR_rx instance */ - rx->ir = get_ir_device(ir, true); - - /* An rx ref goes to the i2c_client */ - i2c_set_clientdata(client, get_ir_rx(ir)); - - /* - * Start the polling thread. - * It will only perform an empty loop around schedule_timeout() - * until we register with lirc_dev and the first user open() - */ - /* An ir ref goes to the new rx polling kthread */ - rx->task = kthread_run(lirc_thread, get_ir_device(ir, true), - "zilog-rx-i2c-%d", adap->nr); - if (IS_ERR(rx->task)) { - ret = PTR_ERR(rx->task); - dev_err(tx->ir->dev, - "%s: could not start IR Rx polling thread\n", - __func__); - /* Failed kthread, so put back the ir ref */ - put_ir_device(ir, true); - /* Failure exit, so put back rx ref from i2c_client */ - i2c_set_clientdata(client, NULL); - put_ir_rx(rx, true); - ir->l->features &= ~LIRC_CAN_REC_LIRCCODE; - goto out_put_tx; - } - - /* Proceed only if the Tx client is also ready */ - if (!tx) { - pr_info("probe of IR Rx on %s (i2c-%d) done. Waiting on IR Tx.\n", - adap->name, adap->nr); - goto out_ok; - } - } - - /* register with lirc */ - ret = lirc_register_device(ir->l); - if (ret < 0) { - dev_err(tx->ir->dev, - "%s: lirc_register_device() failed: %i\n", - __func__, ret); - lirc_free_device(ir->l); - ir->l = NULL; - goto out_put_xx; - } - - dev_info(ir->dev, - "IR unit on %s (i2c-%d) registered as lirc%d and ready\n", - adap->name, adap->nr, ir->l->minor); - -out_ok: - if (rx) - put_ir_rx(rx, true); - if (tx) - put_ir_tx(tx, true); - put_ir_device(ir, true); - dev_info(ir->dev, - "probe of IR %s on %s (i2c-%d) done\n", - tx_probe ? "Tx" : "Rx", adap->name, adap->nr); - mutex_unlock(&ir_devices_lock); - return 0; - -out_put_xx: - if (rx) - put_ir_rx(rx, true); -out_put_tx: - if (tx) - put_ir_tx(tx, true); -out_put_ir: - put_ir_device(ir, true); -out_no_ir: - dev_err(&client->dev, - "%s: probing IR %s on %s (i2c-%d) failed with %d\n", - __func__, tx_probe ? "Tx" : "Rx", adap->name, adap->nr, ret); - mutex_unlock(&ir_devices_lock); - return ret; -} - -static int __init zilog_init(void) -{ - int ret; - - pr_notice("Zilog/Hauppauge IR driver initializing\n"); - - mutex_init(&tx_data_lock); - - request_module("firmware_class"); - - ret = i2c_add_driver(&driver); - if (ret) - pr_err("initialization failed\n"); - else - pr_notice("initialization complete\n"); - - return ret; -} - -static void __exit zilog_exit(void) -{ - i2c_del_driver(&driver); - /* if loaded */ - fw_unload(); - pr_notice("Zilog/Hauppauge IR driver unloaded\n"); -} - -module_init(zilog_init); -module_exit(zilog_exit); - -MODULE_DESCRIPTION("Zilog/Hauppauge infrared transmitter driver (i2c stack)"); -MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, Ulrich Mueller, Stefan Jahn, Jerome Brock, Mark Weaver, Andy Walls"); -MODULE_LICENSE("GPL"); -/* for compat with old name, which isn't all that accurate anymore */ -MODULE_ALIAS("lirc_pvr150"); - -module_param(debug, bool, 0644); -MODULE_PARM_DESC(debug, "Enable debugging messages"); - -module_param(tx_only, bool, 0644); -MODULE_PARM_DESC(tx_only, "Only handle the IR transmit function"); diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c index c26c99fd..b1036ba 100644 --- a/drivers/staging/media/omap4iss/iss.c +++ b/drivers/staging/media/omap4iss/iss.c @@ -893,7 +893,7 @@ void omap4iss_put(struct iss_device *iss) return; mutex_lock(&iss->iss_mutex); - BUG_ON(iss->ref_count == 0); + WARN_ON(iss->ref_count == 0); if (--iss->ref_count == 0) { iss_disable_interrupts(iss); /* Reset the ISS if an entity has failed to stop. This is the diff --git a/drivers/staging/media/tegra-vde/Kconfig b/drivers/staging/media/tegra-vde/Kconfig new file mode 100644 index 0000000..5c49146 --- /dev/null +++ b/drivers/staging/media/tegra-vde/Kconfig @@ -0,0 +1,8 @@ +config TEGRA_VDE + tristate "NVIDIA Tegra Video Decoder Engine driver" + depends on ARCH_TEGRA || COMPILE_TEST + select DMA_SHARED_BUFFER + select SRAM + help + Say Y here to enable support for the NVIDIA Tegra video decoder + driver. diff --git a/drivers/staging/media/tegra-vde/Makefile b/drivers/staging/media/tegra-vde/Makefile new file mode 100644 index 0000000..444c1d6 --- /dev/null +++ b/drivers/staging/media/tegra-vde/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_TEGRA_VDE) += tegra-vde.o diff --git a/drivers/staging/media/tegra-vde/TODO b/drivers/staging/media/tegra-vde/TODO new file mode 100644 index 0000000..31aaa3e --- /dev/null +++ b/drivers/staging/media/tegra-vde/TODO @@ -0,0 +1,4 @@ +TODO: + - Implement V4L2 API once it gains support for stateless decoders. + +Contact: Dmitry Osipenko <digetx@gmail.com> diff --git a/drivers/staging/media/tegra-vde/tegra-vde.c b/drivers/staging/media/tegra-vde/tegra-vde.c new file mode 100644 index 0000000..c47659e --- /dev/null +++ b/drivers/staging/media/tegra-vde/tegra-vde.c @@ -0,0 +1,1213 @@ +/* + * NVIDIA Tegra Video decoder driver + * + * Copyright (C) 2016-2017 Dmitry Osipenko <digetx@gmail.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/clk.h> +#include <linux/dma-buf.h> +#include <linux/genalloc.h> +#include <linux/interrupt.h> +#include <linux/iopoll.h> +#include <linux/miscdevice.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/reset.h> +#include <linux/slab.h> +#include <linux/uaccess.h> + +#include <soc/tegra/pmc.h> + +#include "uapi.h" + +#define ICMDQUE_WR 0x00 +#define CMDQUE_CONTROL 0x08 +#define INTR_STATUS 0x18 +#define BSE_INT_ENB 0x40 +#define BSE_CONFIG 0x44 + +#define BSE_ICMDQUE_EMPTY BIT(3) +#define BSE_DMA_BUSY BIT(23) + +#define VDE_WR(__data, __addr) \ +do { \ + dev_dbg(vde->miscdev.parent, \ + "%s: %d: 0x%08X => " #__addr ")\n", \ + __func__, __LINE__, (u32)(__data)); \ + writel_relaxed(__data, __addr); \ +} while (0) + +struct video_frame { + struct dma_buf_attachment *y_dmabuf_attachment; + struct dma_buf_attachment *cb_dmabuf_attachment; + struct dma_buf_attachment *cr_dmabuf_attachment; + struct dma_buf_attachment *aux_dmabuf_attachment; + struct sg_table *y_sgt; + struct sg_table *cb_sgt; + struct sg_table *cr_sgt; + struct sg_table *aux_sgt; + dma_addr_t y_addr; + dma_addr_t cb_addr; + dma_addr_t cr_addr; + dma_addr_t aux_addr; + u32 frame_num; + u32 flags; +}; + +struct tegra_vde { + void __iomem *sxe; + void __iomem *bsev; + void __iomem *mbe; + void __iomem *ppe; + void __iomem *mce; + void __iomem *tfe; + void __iomem *ppb; + void __iomem *vdma; + void __iomem *frameid; + struct mutex lock; + struct miscdevice miscdev; + struct reset_control *rst; + struct gen_pool *iram_pool; + struct completion decode_completion; + struct clk *clk; + dma_addr_t iram_lists_addr; + u32 *iram; +}; + +static void tegra_vde_set_bits(struct tegra_vde *vde, + u32 mask, void __iomem *regs) +{ + u32 value = readl_relaxed(regs); + + VDE_WR(value | mask, regs); +} + +static int tegra_vde_wait_mbe(struct tegra_vde *vde) +{ + u32 tmp; + + return readl_relaxed_poll_timeout(vde->mbe + 0x8C, tmp, + (tmp >= 0x10), 1, 100); +} + +static int tegra_vde_setup_mbe_frame_idx(struct tegra_vde *vde, + unsigned int refs_nb, + bool setup_refs) +{ + u32 frame_idx_enb_mask = 0; + u32 value; + unsigned int frame_idx; + unsigned int idx; + int err; + + VDE_WR(0xD0000000 | (0 << 23), vde->mbe + 0x80); + VDE_WR(0xD0200000 | (0 << 23), vde->mbe + 0x80); + + err = tegra_vde_wait_mbe(vde); + if (err) + return err; + + if (!setup_refs) + return 0; + + for (idx = 0, frame_idx = 1; idx < refs_nb; idx++, frame_idx++) { + VDE_WR(0xD0000000 | (frame_idx << 23), vde->mbe + 0x80); + VDE_WR(0xD0200000 | (frame_idx << 23), vde->mbe + 0x80); + + frame_idx_enb_mask |= frame_idx << (6 * (idx % 4)); + + if (idx % 4 == 3 || idx == refs_nb - 1) { + value = 0xC0000000; + value |= (idx >> 2) << 24; + value |= frame_idx_enb_mask; + + VDE_WR(value, vde->mbe + 0x80); + + err = tegra_vde_wait_mbe(vde); + if (err) + return err; + + frame_idx_enb_mask = 0; + } + } + + return 0; +} + +static void tegra_vde_mbe_set_0xa_reg(struct tegra_vde *vde, int reg, u32 val) +{ + VDE_WR(0xA0000000 | (reg << 24) | (val & 0xFFFF), vde->mbe + 0x80); + VDE_WR(0xA0000000 | ((reg + 1) << 24) | (val >> 16), vde->mbe + 0x80); +} + +static int tegra_vde_wait_bsev(struct tegra_vde *vde, bool wait_dma) +{ + struct device *dev = vde->miscdev.parent; + u32 value; + int err; + + err = readl_relaxed_poll_timeout(vde->bsev + INTR_STATUS, value, + !(value & BIT(2)), 1, 100); + if (err) { + dev_err(dev, "BSEV unknown bit timeout\n"); + return err; + } + + err = readl_relaxed_poll_timeout(vde->bsev + INTR_STATUS, value, + (value & BSE_ICMDQUE_EMPTY), 1, 100); + if (err) { + dev_err(dev, "BSEV ICMDQUE flush timeout\n"); + return err; + } + + if (!wait_dma) + return 0; + + err = readl_relaxed_poll_timeout(vde->bsev + INTR_STATUS, value, + !(value & BSE_DMA_BUSY), 1, 100); + if (err) { + dev_err(dev, "BSEV DMA timeout\n"); + return err; + } + + return 0; +} + +static int tegra_vde_push_to_bsev_icmdqueue(struct tegra_vde *vde, + u32 value, bool wait_dma) +{ + VDE_WR(value, vde->bsev + ICMDQUE_WR); + + return tegra_vde_wait_bsev(vde, wait_dma); +} + +static void tegra_vde_setup_frameid(struct tegra_vde *vde, + struct video_frame *frame, + unsigned int frameid, + u32 mbs_width, u32 mbs_height) +{ + u32 y_addr = frame ? frame->y_addr : 0x6CDEAD00; + u32 cb_addr = frame ? frame->cb_addr : 0x6CDEAD00; + u32 cr_addr = frame ? frame->cr_addr : 0x6CDEAD00; + u32 value1 = frame ? ((mbs_width << 16) | mbs_height) : 0; + u32 value2 = frame ? ((((mbs_width + 1) >> 1) << 6) | 1) : 0; + + VDE_WR(y_addr >> 8, vde->frameid + 0x000 + frameid * 4); + VDE_WR(cb_addr >> 8, vde->frameid + 0x100 + frameid * 4); + VDE_WR(cr_addr >> 8, vde->frameid + 0x180 + frameid * 4); + VDE_WR(value1, vde->frameid + 0x080 + frameid * 4); + VDE_WR(value2, vde->frameid + 0x280 + frameid * 4); +} + +static void tegra_setup_frameidx(struct tegra_vde *vde, + struct video_frame *frames, + unsigned int frames_nb, + u32 mbs_width, u32 mbs_height) +{ + unsigned int idx; + + for (idx = 0; idx < frames_nb; idx++) + tegra_vde_setup_frameid(vde, &frames[idx], idx, + mbs_width, mbs_height); + + for (; idx < 17; idx++) + tegra_vde_setup_frameid(vde, NULL, idx, 0, 0); +} + +static void tegra_vde_setup_iram_entry(struct tegra_vde *vde, + unsigned int table, + unsigned int row, + u32 value1, u32 value2) +{ + u32 *iram_tables = vde->iram; + + dev_dbg(vde->miscdev.parent, "IRAM table %u: row %u: 0x%08X 0x%08X\n", + table, row, value1, value2); + + iram_tables[0x20 * table + row * 2] = value1; + iram_tables[0x20 * table + row * 2 + 1] = value2; +} + +static void tegra_vde_setup_iram_tables(struct tegra_vde *vde, + struct video_frame *dpb_frames, + unsigned int ref_frames_nb, + unsigned int with_earlier_poc_nb) +{ + struct video_frame *frame; + u32 value, aux_addr; + int with_later_poc_nb; + unsigned int i, k; + + dev_dbg(vde->miscdev.parent, "DPB: Frame 0: frame_num = %d\n", + dpb_frames[0].frame_num); + + dev_dbg(vde->miscdev.parent, "REF L0:\n"); + + for (i = 0; i < 16; i++) { + if (i < ref_frames_nb) { + frame = &dpb_frames[i + 1]; + + aux_addr = frame->aux_addr; + + value = (i + 1) << 26; + value |= !(frame->flags & FLAG_B_FRAME) << 25; + value |= 1 << 24; + value |= frame->frame_num; + + dev_dbg(vde->miscdev.parent, + "\tFrame %d: frame_num = %d B_frame = %d\n", + i + 1, frame->frame_num, + (frame->flags & FLAG_B_FRAME)); + } else { + aux_addr = 0x6ADEAD00; + value = 0; + } + + tegra_vde_setup_iram_entry(vde, 0, i, value, aux_addr); + tegra_vde_setup_iram_entry(vde, 1, i, value, aux_addr); + tegra_vde_setup_iram_entry(vde, 2, i, value, aux_addr); + tegra_vde_setup_iram_entry(vde, 3, i, value, aux_addr); + } + + if (!(dpb_frames[0].flags & FLAG_B_FRAME)) + return; + + if (with_earlier_poc_nb >= ref_frames_nb) + return; + + with_later_poc_nb = ref_frames_nb - with_earlier_poc_nb; + + dev_dbg(vde->miscdev.parent, + "REF L1: with_later_poc_nb %d with_earlier_poc_nb %d\n", + with_later_poc_nb, with_earlier_poc_nb); + + for (i = 0, k = with_earlier_poc_nb; i < with_later_poc_nb; i++, k++) { + frame = &dpb_frames[k + 1]; + + aux_addr = frame->aux_addr; + + value = (k + 1) << 26; + value |= !(frame->flags & FLAG_B_FRAME) << 25; + value |= 1 << 24; + value |= frame->frame_num; + + dev_dbg(vde->miscdev.parent, + "\tFrame %d: frame_num = %d\n", + k + 1, frame->frame_num); + + tegra_vde_setup_iram_entry(vde, 2, i, value, aux_addr); + } + + for (k = 0; i < ref_frames_nb; i++, k++) { + frame = &dpb_frames[k + 1]; + + aux_addr = frame->aux_addr; + + value = (k + 1) << 26; + value |= !(frame->flags & FLAG_B_FRAME) << 25; + value |= 1 << 24; + value |= frame->frame_num; + + dev_dbg(vde->miscdev.parent, + "\tFrame %d: frame_num = %d\n", + k + 1, frame->frame_num); + + tegra_vde_setup_iram_entry(vde, 2, i, value, aux_addr); + } +} + +static int tegra_vde_setup_hw_context(struct tegra_vde *vde, + struct tegra_vde_h264_decoder_ctx *ctx, + struct video_frame *dpb_frames, + dma_addr_t bitstream_data_addr, + size_t bitstream_data_size, + unsigned int macroblocks_nb) +{ + struct device *dev = vde->miscdev.parent; + u32 value; + int err; + + tegra_vde_set_bits(vde, 0x000A, vde->sxe + 0xF0); + tegra_vde_set_bits(vde, 0x000B, vde->bsev + CMDQUE_CONTROL); + tegra_vde_set_bits(vde, 0x8002, vde->mbe + 0x50); + tegra_vde_set_bits(vde, 0x000A, vde->mbe + 0xA0); + tegra_vde_set_bits(vde, 0x000A, vde->ppe + 0x14); + tegra_vde_set_bits(vde, 0x000A, vde->ppe + 0x28); + tegra_vde_set_bits(vde, 0x0A00, vde->mce + 0x08); + tegra_vde_set_bits(vde, 0x000A, vde->tfe + 0x00); + tegra_vde_set_bits(vde, 0x0005, vde->vdma + 0x04); + + VDE_WR(0x00000000, vde->vdma + 0x1C); + VDE_WR(0x00000000, vde->vdma + 0x00); + VDE_WR(0x00000007, vde->vdma + 0x04); + VDE_WR(0x00000007, vde->frameid + 0x200); + VDE_WR(0x00000005, vde->tfe + 0x04); + VDE_WR(0x00000000, vde->mbe + 0x84); + VDE_WR(0x00000010, vde->sxe + 0x08); + VDE_WR(0x00000150, vde->sxe + 0x54); + VDE_WR(0x0000054C, vde->sxe + 0x58); + VDE_WR(0x00000E34, vde->sxe + 0x5C); + VDE_WR(0x063C063C, vde->mce + 0x10); + VDE_WR(0x0003FC00, vde->bsev + INTR_STATUS); + VDE_WR(0x0000150D, vde->bsev + BSE_CONFIG); + VDE_WR(0x00000100, vde->bsev + BSE_INT_ENB); + VDE_WR(0x00000000, vde->bsev + 0x98); + VDE_WR(0x00000060, vde->bsev + 0x9C); + + memset(vde->iram + 128, 0, macroblocks_nb / 2); + + tegra_setup_frameidx(vde, dpb_frames, ctx->dpb_frames_nb, + ctx->pic_width_in_mbs, ctx->pic_height_in_mbs); + + tegra_vde_setup_iram_tables(vde, dpb_frames, + ctx->dpb_frames_nb - 1, + ctx->dpb_ref_frames_with_earlier_poc_nb); + wmb(); + + VDE_WR(0x00000000, vde->bsev + 0x8C); + VDE_WR(bitstream_data_addr + bitstream_data_size, + vde->bsev + 0x54); + + value = ctx->pic_width_in_mbs << 11 | ctx->pic_height_in_mbs << 3; + + VDE_WR(value, vde->bsev + 0x88); + + err = tegra_vde_wait_bsev(vde, false); + if (err) + return err; + + err = tegra_vde_push_to_bsev_icmdqueue(vde, 0x800003FC, false); + if (err) + return err; + + value = 0x01500000; + value |= ((vde->iram_lists_addr + 512) >> 2) & 0xFFFF; + + err = tegra_vde_push_to_bsev_icmdqueue(vde, value, true); + if (err) + return err; + + err = tegra_vde_push_to_bsev_icmdqueue(vde, 0x840F054C, false); + if (err) + return err; + + err = tegra_vde_push_to_bsev_icmdqueue(vde, 0x80000080, false); + if (err) + return err; + + value = 0x0E340000 | ((vde->iram_lists_addr >> 2) & 0xFFFF); + + err = tegra_vde_push_to_bsev_icmdqueue(vde, value, true); + if (err) + return err; + + value = 0x00800005; + value |= ctx->pic_width_in_mbs << 11; + value |= ctx->pic_height_in_mbs << 3; + + VDE_WR(value, vde->sxe + 0x10); + + value = !ctx->baseline_profile << 17; + value |= ctx->level_idc << 13; + value |= ctx->log2_max_pic_order_cnt_lsb << 7; + value |= ctx->pic_order_cnt_type << 5; + value |= ctx->log2_max_frame_num; + + VDE_WR(value, vde->sxe + 0x40); + + value = ctx->pic_init_qp << 25; + value |= !!(ctx->deblocking_filter_control_present_flag) << 2; + value |= !!ctx->pic_order_present_flag; + + VDE_WR(value, vde->sxe + 0x44); + + value = ctx->chroma_qp_index_offset; + value |= ctx->num_ref_idx_l0_active_minus1 << 5; + value |= ctx->num_ref_idx_l1_active_minus1 << 10; + value |= !!ctx->constrained_intra_pred_flag << 15; + + VDE_WR(value, vde->sxe + 0x48); + + value = 0x0C000000; + value |= !!(dpb_frames[0].flags & FLAG_B_FRAME) << 24; + + VDE_WR(value, vde->sxe + 0x4C); + + value = 0x03800000; + value |= min_t(size_t, bitstream_data_size, SZ_1M); + + VDE_WR(value, vde->sxe + 0x68); + + VDE_WR(bitstream_data_addr, vde->sxe + 0x6C); + + value = 0x10000005; + value |= ctx->pic_width_in_mbs << 11; + value |= ctx->pic_height_in_mbs << 3; + + VDE_WR(value, vde->mbe + 0x80); + + value = 0x26800000; + value |= ctx->level_idc << 4; + value |= !ctx->baseline_profile << 1; + value |= !!ctx->direct_8x8_inference_flag; + + VDE_WR(value, vde->mbe + 0x80); + + VDE_WR(0xF4000001, vde->mbe + 0x80); + VDE_WR(0x20000000, vde->mbe + 0x80); + VDE_WR(0xF4000101, vde->mbe + 0x80); + + value = 0x20000000; + value |= ctx->chroma_qp_index_offset << 8; + + VDE_WR(value, vde->mbe + 0x80); + + err = tegra_vde_setup_mbe_frame_idx(vde, + ctx->dpb_frames_nb - 1, + ctx->pic_order_cnt_type == 0); + if (err) { + dev_err(dev, "MBE frames setup failed %d\n", err); + return err; + } + + tegra_vde_mbe_set_0xa_reg(vde, 0, 0x000009FC); + tegra_vde_mbe_set_0xa_reg(vde, 2, 0x61DEAD00); + tegra_vde_mbe_set_0xa_reg(vde, 4, 0x62DEAD00); + tegra_vde_mbe_set_0xa_reg(vde, 6, 0x63DEAD00); + tegra_vde_mbe_set_0xa_reg(vde, 8, dpb_frames[0].aux_addr); + + value = 0xFC000000; + value |= !!(dpb_frames[0].flags & FLAG_B_FRAME) << 2; + + if (!ctx->baseline_profile) + value |= !!(dpb_frames[0].flags & FLAG_REFERENCE) << 1; + + VDE_WR(value, vde->mbe + 0x80); + + err = tegra_vde_wait_mbe(vde); + if (err) { + dev_err(dev, "MBE programming failed %d\n", err); + return err; + } + + return 0; +} + +static void tegra_vde_decode_frame(struct tegra_vde *vde, + unsigned int macroblocks_nb) +{ + reinit_completion(&vde->decode_completion); + + VDE_WR(0x00000001, vde->bsev + 0x8C); + VDE_WR(0x20000000 | (macroblocks_nb - 1), vde->sxe + 0x00); +} + +static void tegra_vde_detach_and_put_dmabuf(struct dma_buf_attachment *a, + struct sg_table *sgt, + enum dma_data_direction dma_dir) +{ + struct dma_buf *dmabuf = a->dmabuf; + + dma_buf_unmap_attachment(a, sgt, dma_dir); + dma_buf_detach(dmabuf, a); + dma_buf_put(dmabuf); +} + +static int tegra_vde_attach_dmabuf(struct device *dev, + int fd, + unsigned long offset, + unsigned int min_size, + struct dma_buf_attachment **a, + dma_addr_t *addr, + struct sg_table **s, + size_t *size, + enum dma_data_direction dma_dir) +{ + struct dma_buf_attachment *attachment; + struct dma_buf *dmabuf; + struct sg_table *sgt; + int err; + + dmabuf = dma_buf_get(fd); + if (IS_ERR(dmabuf)) { + dev_err(dev, "Invalid dmabuf FD\n"); + return PTR_ERR(dmabuf); + } + + if ((u64)offset + min_size > dmabuf->size) { + dev_err(dev, "Too small dmabuf size %zu @0x%lX, " + "should be at least %d\n", + dmabuf->size, offset, min_size); + return -EINVAL; + } + + attachment = dma_buf_attach(dmabuf, dev); + if (IS_ERR(attachment)) { + dev_err(dev, "Failed to attach dmabuf\n"); + err = PTR_ERR(attachment); + goto err_put; + } + + sgt = dma_buf_map_attachment(attachment, dma_dir); + if (IS_ERR(sgt)) { + dev_err(dev, "Failed to get dmabufs sg_table\n"); + err = PTR_ERR(sgt); + goto err_detach; + } + + if (sgt->nents != 1) { + dev_err(dev, "Sparse DMA region is unsupported\n"); + err = -EINVAL; + goto err_unmap; + } + + *addr = sg_dma_address(sgt->sgl) + offset; + *a = attachment; + *s = sgt; + + if (size) + *size = dmabuf->size - offset; + + return 0; + +err_unmap: + dma_buf_unmap_attachment(attachment, sgt, dma_dir); +err_detach: + dma_buf_detach(dmabuf, attachment); +err_put: + dma_buf_put(dmabuf); + + return err; +} + +static int tegra_vde_attach_dmabufs_to_frame(struct device *dev, + struct video_frame *frame, + struct tegra_vde_h264_frame *src, + enum dma_data_direction dma_dir, + bool baseline_profile, + size_t csize) +{ + int err; + + err = tegra_vde_attach_dmabuf(dev, src->y_fd, + src->y_offset, csize * 4, + &frame->y_dmabuf_attachment, + &frame->y_addr, + &frame->y_sgt, + NULL, dma_dir); + if (err) + return err; + + err = tegra_vde_attach_dmabuf(dev, src->cb_fd, + src->cb_offset, csize, + &frame->cb_dmabuf_attachment, + &frame->cb_addr, + &frame->cb_sgt, + NULL, dma_dir); + if (err) + goto err_release_y; + + err = tegra_vde_attach_dmabuf(dev, src->cr_fd, + src->cr_offset, csize, + &frame->cr_dmabuf_attachment, + &frame->cr_addr, + &frame->cr_sgt, + NULL, dma_dir); + if (err) + goto err_release_cb; + + if (baseline_profile) { + frame->aux_addr = 0x64DEAD00; + return 0; + } + + err = tegra_vde_attach_dmabuf(dev, src->aux_fd, + src->aux_offset, csize, + &frame->aux_dmabuf_attachment, + &frame->aux_addr, + &frame->aux_sgt, + NULL, dma_dir); + if (err) + goto err_release_cr; + + return 0; + +err_release_cr: + tegra_vde_detach_and_put_dmabuf(frame->cr_dmabuf_attachment, + frame->cr_sgt, dma_dir); +err_release_cb: + tegra_vde_detach_and_put_dmabuf(frame->cb_dmabuf_attachment, + frame->cb_sgt, dma_dir); +err_release_y: + tegra_vde_detach_and_put_dmabuf(frame->y_dmabuf_attachment, + frame->y_sgt, dma_dir); + + return err; +} + +static void tegra_vde_release_frame_dmabufs(struct video_frame *frame, + enum dma_data_direction dma_dir, + bool baseline_profile) +{ + if (!baseline_profile) + tegra_vde_detach_and_put_dmabuf(frame->aux_dmabuf_attachment, + frame->aux_sgt, dma_dir); + + tegra_vde_detach_and_put_dmabuf(frame->cr_dmabuf_attachment, + frame->cr_sgt, dma_dir); + + tegra_vde_detach_and_put_dmabuf(frame->cb_dmabuf_attachment, + frame->cb_sgt, dma_dir); + + tegra_vde_detach_and_put_dmabuf(frame->y_dmabuf_attachment, + frame->y_sgt, dma_dir); +} + +static int tegra_vde_validate_frame(struct device *dev, + struct tegra_vde_h264_frame *frame) +{ + if (frame->frame_num > 0x7FFFFF) { + dev_err(dev, "Bad frame_num %u\n", frame->frame_num); + return -EINVAL; + } + + if (frame->y_offset & 0xFF) { + dev_err(dev, "Bad y_offset 0x%X\n", frame->y_offset); + return -EINVAL; + } + + if (frame->cb_offset & 0xFF) { + dev_err(dev, "Bad cb_offset 0x%X\n", frame->cb_offset); + return -EINVAL; + } + + if (frame->cr_offset & 0xFF) { + dev_err(dev, "Bad cr_offset 0x%X\n", frame->cr_offset); + return -EINVAL; + } + + return 0; +} + +static int tegra_vde_validate_h264_ctx(struct device *dev, + struct tegra_vde_h264_decoder_ctx *ctx) +{ + if (ctx->dpb_frames_nb == 0 || ctx->dpb_frames_nb > 17) { + dev_err(dev, "Bad DPB size %u\n", ctx->dpb_frames_nb); + return -EINVAL; + } + + if (ctx->level_idc > 15) { + dev_err(dev, "Bad level value %u\n", ctx->level_idc); + return -EINVAL; + } + + if (ctx->pic_init_qp > 52) { + dev_err(dev, "Bad pic_init_qp value %u\n", ctx->pic_init_qp); + return -EINVAL; + } + + if (ctx->log2_max_pic_order_cnt_lsb > 16) { + dev_err(dev, "Bad log2_max_pic_order_cnt_lsb value %u\n", + ctx->log2_max_pic_order_cnt_lsb); + return -EINVAL; + } + + if (ctx->log2_max_frame_num > 16) { + dev_err(dev, "Bad log2_max_frame_num value %u\n", + ctx->log2_max_frame_num); + return -EINVAL; + } + + if (ctx->chroma_qp_index_offset > 31) { + dev_err(dev, "Bad chroma_qp_index_offset value %u\n", + ctx->chroma_qp_index_offset); + return -EINVAL; + } + + if (ctx->pic_order_cnt_type > 2) { + dev_err(dev, "Bad pic_order_cnt_type value %u\n", + ctx->pic_order_cnt_type); + return -EINVAL; + } + + if (ctx->num_ref_idx_l0_active_minus1 > 15) { + dev_err(dev, "Bad num_ref_idx_l0_active_minus1 value %u\n", + ctx->num_ref_idx_l0_active_minus1); + return -EINVAL; + } + + if (ctx->num_ref_idx_l1_active_minus1 > 15) { + dev_err(dev, "Bad num_ref_idx_l1_active_minus1 value %u\n", + ctx->num_ref_idx_l1_active_minus1); + return -EINVAL; + } + + if (!ctx->pic_width_in_mbs || ctx->pic_width_in_mbs > 127) { + dev_err(dev, "Bad pic_width_in_mbs value %u\n", + ctx->pic_width_in_mbs); + return -EINVAL; + } + + if (!ctx->pic_height_in_mbs || ctx->pic_height_in_mbs > 127) { + dev_err(dev, "Bad pic_height_in_mbs value %u\n", + ctx->pic_height_in_mbs); + return -EINVAL; + } + + return 0; +} + +static int tegra_vde_ioctl_decode_h264(struct tegra_vde *vde, + unsigned long vaddr) +{ + struct device *dev = vde->miscdev.parent; + struct tegra_vde_h264_decoder_ctx ctx; + struct tegra_vde_h264_frame frames[17]; + struct tegra_vde_h264_frame __user *frames_user; + struct video_frame *dpb_frames; + struct dma_buf_attachment *bitstream_data_dmabuf_attachment; + struct sg_table *bitstream_sgt; + enum dma_data_direction dma_dir; + dma_addr_t bitstream_data_addr; + dma_addr_t bsev_ptr; + size_t bitstream_data_size; + unsigned int macroblocks_nb; + unsigned int read_bytes; + unsigned int i; + long timeout; + int ret, err; + + if (copy_from_user(&ctx, (void __user *)vaddr, sizeof(ctx))) + return -EFAULT; + + ret = tegra_vde_validate_h264_ctx(dev, &ctx); + if (ret) + return ret; + + ret = tegra_vde_attach_dmabuf(dev, ctx.bitstream_data_fd, + ctx.bitstream_data_offset, 0, + &bitstream_data_dmabuf_attachment, + &bitstream_data_addr, + &bitstream_sgt, + &bitstream_data_size, + DMA_TO_DEVICE); + if (ret) + return ret; + + dpb_frames = kcalloc(ctx.dpb_frames_nb, sizeof(*dpb_frames), + GFP_KERNEL); + if (!dpb_frames) { + ret = -ENOMEM; + goto release_bitstream_dmabuf; + } + + macroblocks_nb = ctx.pic_width_in_mbs * ctx.pic_height_in_mbs; + frames_user = u64_to_user_ptr(ctx.dpb_frames_ptr); + + if (copy_from_user(frames, frames_user, + ctx.dpb_frames_nb * sizeof(*frames))) { + ret = -EFAULT; + goto free_dpb_frames; + } + + for (i = 0; i < ctx.dpb_frames_nb; i++) { + ret = tegra_vde_validate_frame(dev, &frames[i]); + if (ret) + goto release_dpb_frames; + + dpb_frames[i].flags = frames[i].flags; + dpb_frames[i].frame_num = frames[i].frame_num; + + dma_dir = (i == 0) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + + ret = tegra_vde_attach_dmabufs_to_frame(dev, &dpb_frames[i], + &frames[i], dma_dir, + ctx.baseline_profile, + macroblocks_nb * 64); + if (ret) + goto release_dpb_frames; + } + + ret = mutex_lock_interruptible(&vde->lock); + if (ret) + goto release_dpb_frames; + + ret = pm_runtime_get_sync(dev); + if (ret < 0) + goto unlock; + + /* + * We rely on the VDE registers reset value, otherwise VDE + * causes bus lockup. + */ + ret = reset_control_reset(vde->rst); + if (ret) { + dev_err(dev, "Failed to reset HW: %d\n", ret); + goto put_runtime_pm; + } + + ret = tegra_vde_setup_hw_context(vde, &ctx, dpb_frames, + bitstream_data_addr, + bitstream_data_size, + macroblocks_nb); + if (ret) + goto put_runtime_pm; + + tegra_vde_decode_frame(vde, macroblocks_nb); + + timeout = wait_for_completion_interruptible_timeout( + &vde->decode_completion, msecs_to_jiffies(1000)); + if (timeout == 0) { + bsev_ptr = readl_relaxed(vde->bsev + 0x10); + macroblocks_nb = readl_relaxed(vde->sxe + 0xC8) & 0x1FFF; + read_bytes = bsev_ptr ? bsev_ptr - bitstream_data_addr : 0; + + dev_err(dev, "Decoding failed: " + "read 0x%X bytes, %u macroblocks parsed\n", + read_bytes, macroblocks_nb); + + ret = -EIO; + } else if (timeout < 0) { + ret = timeout; + } + + err = reset_control_assert(vde->rst); + if (err) + dev_err(dev, "Failed to assert HW reset: %d\n", err); + +put_runtime_pm: + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + +unlock: + mutex_unlock(&vde->lock); + +release_dpb_frames: + while (i--) { + dma_dir = (i == 0) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + + tegra_vde_release_frame_dmabufs(&dpb_frames[i], dma_dir, + ctx.baseline_profile); + } + +free_dpb_frames: + kfree(dpb_frames); + +release_bitstream_dmabuf: + tegra_vde_detach_and_put_dmabuf(bitstream_data_dmabuf_attachment, + bitstream_sgt, DMA_TO_DEVICE); + + return ret; +} + +static long tegra_vde_unlocked_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct miscdevice *miscdev = filp->private_data; + struct tegra_vde *vde = container_of(miscdev, struct tegra_vde, + miscdev); + + switch (cmd) { + case TEGRA_VDE_IOCTL_DECODE_H264: + return tegra_vde_ioctl_decode_h264(vde, arg); + } + + dev_err(miscdev->parent, "Invalid IOCTL command %u\n", cmd); + + return -ENOTTY; +} + +static const struct file_operations tegra_vde_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = tegra_vde_unlocked_ioctl, +}; + +static irqreturn_t tegra_vde_isr(int irq, void *data) +{ + struct tegra_vde *vde = data; + + tegra_vde_set_bits(vde, 0, vde->frameid + 0x208); + complete(&vde->decode_completion); + + return IRQ_HANDLED; +} + +static int tegra_vde_runtime_suspend(struct device *dev) +{ + struct tegra_vde *vde = dev_get_drvdata(dev); + int err; + + err = tegra_powergate_power_off(TEGRA_POWERGATE_VDEC); + if (err) { + dev_err(dev, "Failed to power down HW: %d\n", err); + return err; + } + + clk_disable_unprepare(vde->clk); + + return 0; +} + +static int tegra_vde_runtime_resume(struct device *dev) +{ + struct tegra_vde *vde = dev_get_drvdata(dev); + int err; + + err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_VDEC, + vde->clk, vde->rst); + if (err) { + dev_err(dev, "Failed to power up HW : %d\n", err); + return err; + } + + return 0; +} + +static int tegra_vde_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *regs; + struct tegra_vde *vde; + int irq, err; + + vde = devm_kzalloc(dev, sizeof(*vde), GFP_KERNEL); + if (!vde) + return -ENOMEM; + + platform_set_drvdata(pdev, vde); + + regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sxe"); + if (!regs) + return -ENODEV; + + vde->sxe = devm_ioremap_resource(dev, regs); + if (IS_ERR(vde->sxe)) + return PTR_ERR(vde->sxe); + + regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "bsev"); + if (!regs) + return -ENODEV; + + vde->bsev = devm_ioremap_resource(dev, regs); + if (IS_ERR(vde->bsev)) + return PTR_ERR(vde->bsev); + + regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mbe"); + if (!regs) + return -ENODEV; + + vde->mbe = devm_ioremap_resource(dev, regs); + if (IS_ERR(vde->mbe)) + return PTR_ERR(vde->mbe); + + regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ppe"); + if (!regs) + return -ENODEV; + + vde->ppe = devm_ioremap_resource(dev, regs); + if (IS_ERR(vde->ppe)) + return PTR_ERR(vde->ppe); + + regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mce"); + if (!regs) + return -ENODEV; + + vde->mce = devm_ioremap_resource(dev, regs); + if (IS_ERR(vde->mce)) + return PTR_ERR(vde->mce); + + regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tfe"); + if (!regs) + return -ENODEV; + + vde->tfe = devm_ioremap_resource(dev, regs); + if (IS_ERR(vde->tfe)) + return PTR_ERR(vde->tfe); + + regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ppb"); + if (!regs) + return -ENODEV; + + vde->ppb = devm_ioremap_resource(dev, regs); + if (IS_ERR(vde->ppb)) + return PTR_ERR(vde->ppb); + + regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vdma"); + if (!regs) + return -ENODEV; + + vde->vdma = devm_ioremap_resource(dev, regs); + if (IS_ERR(vde->vdma)) + return PTR_ERR(vde->vdma); + + regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "frameid"); + if (!regs) + return -ENODEV; + + vde->frameid = devm_ioremap_resource(dev, regs); + if (IS_ERR(vde->frameid)) + return PTR_ERR(vde->frameid); + + vde->clk = devm_clk_get(dev, NULL); + if (IS_ERR(vde->clk)) { + err = PTR_ERR(vde->clk); + dev_err(dev, "Could not get VDE clk %d\n", err); + return err; + } + + vde->rst = devm_reset_control_get(dev, NULL); + if (IS_ERR(vde->rst)) { + err = PTR_ERR(vde->rst); + dev_err(dev, "Could not get VDE reset %d\n", err); + return err; + } + + irq = platform_get_irq_byname(pdev, "sync-token"); + if (irq < 0) + return irq; + + err = devm_request_irq(dev, irq, tegra_vde_isr, 0, + dev_name(dev), vde); + if (err) { + dev_err(dev, "Could not request IRQ %d\n", err); + return err; + } + + vde->iram_pool = of_gen_pool_get(dev->of_node, "iram", 0); + if (!vde->iram_pool) { + dev_err(dev, "Could not get IRAM pool\n"); + return -EPROBE_DEFER; + } + + vde->iram = gen_pool_dma_alloc(vde->iram_pool, + gen_pool_size(vde->iram_pool), + &vde->iram_lists_addr); + if (!vde->iram) { + dev_err(dev, "Could not reserve IRAM\n"); + return -ENOMEM; + } + + mutex_init(&vde->lock); + init_completion(&vde->decode_completion); + + vde->miscdev.minor = MISC_DYNAMIC_MINOR; + vde->miscdev.name = "tegra_vde"; + vde->miscdev.fops = &tegra_vde_fops; + vde->miscdev.parent = dev; + + err = misc_register(&vde->miscdev); + if (err) { + dev_err(dev, "Failed to register misc device: %d\n", err); + goto err_gen_free; + } + + pm_runtime_enable(dev); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_autosuspend_delay(dev, 300); + + if (!pm_runtime_enabled(dev)) { + err = tegra_vde_runtime_resume(dev); + if (err) + goto err_misc_unreg; + } + + return 0; + +err_misc_unreg: + misc_deregister(&vde->miscdev); + +err_gen_free: + gen_pool_free(vde->iram_pool, (unsigned long)vde->iram, + gen_pool_size(vde->iram_pool)); + + return err; +} + +static int tegra_vde_remove(struct platform_device *pdev) +{ + struct tegra_vde *vde = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + int err; + + if (!pm_runtime_enabled(dev)) { + err = tegra_vde_runtime_suspend(dev); + if (err) + return err; + } + + pm_runtime_dont_use_autosuspend(dev); + pm_runtime_disable(dev); + + misc_deregister(&vde->miscdev); + + gen_pool_free(vde->iram_pool, (unsigned long)vde->iram, + gen_pool_size(vde->iram_pool)); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int tegra_vde_pm_suspend(struct device *dev) +{ + struct tegra_vde *vde = dev_get_drvdata(dev); + int err; + + mutex_lock(&vde->lock); + + err = pm_runtime_force_suspend(dev); + if (err < 0) + return err; + + return 0; +} + +static int tegra_vde_pm_resume(struct device *dev) +{ + struct tegra_vde *vde = dev_get_drvdata(dev); + int err; + + err = pm_runtime_force_resume(dev); + if (err < 0) + return err; + + mutex_unlock(&vde->lock); + + return 0; +} +#endif + +static const struct dev_pm_ops tegra_vde_pm_ops = { + SET_RUNTIME_PM_OPS(tegra_vde_runtime_suspend, + tegra_vde_runtime_resume, + NULL) + SET_SYSTEM_SLEEP_PM_OPS(tegra_vde_pm_suspend, + tegra_vde_pm_resume) +}; + +static const struct of_device_id tegra_vde_of_match[] = { + { .compatible = "nvidia,tegra20-vde", }, + { }, +}; +MODULE_DEVICE_TABLE(of, tegra_vde_of_match); + +static struct platform_driver tegra_vde_driver = { + .probe = tegra_vde_probe, + .remove = tegra_vde_remove, + .driver = { + .name = "tegra-vde", + .of_match_table = tegra_vde_of_match, + .pm = &tegra_vde_pm_ops, + }, +}; +module_platform_driver(tegra_vde_driver); + +MODULE_DESCRIPTION("NVIDIA Tegra Video Decoder driver"); +MODULE_AUTHOR("Dmitry Osipenko <digetx@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/tegra-vde/uapi.h b/drivers/staging/media/tegra-vde/uapi.h new file mode 100644 index 0000000..a50c7bc --- /dev/null +++ b/drivers/staging/media/tegra-vde/uapi.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2016-2017 Dmitry Osipenko <digetx@gmail.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _UAPI_TEGRA_VDE_H_ +#define _UAPI_TEGRA_VDE_H_ + +#include <linux/types.h> +#include <asm/ioctl.h> + +#define FLAG_B_FRAME (1 << 0) +#define FLAG_REFERENCE (1 << 1) + +struct tegra_vde_h264_frame { + __s32 y_fd; + __s32 cb_fd; + __s32 cr_fd; + __s32 aux_fd; + __u32 y_offset; + __u32 cb_offset; + __u32 cr_offset; + __u32 aux_offset; + __u32 frame_num; + __u32 flags; + + __u32 reserved; +} __attribute__((packed)); + +struct tegra_vde_h264_decoder_ctx { + __s32 bitstream_data_fd; + __u32 bitstream_data_offset; + + __u64 dpb_frames_ptr; + __u8 dpb_frames_nb; + __u8 dpb_ref_frames_with_earlier_poc_nb; + + // SPS + __u8 baseline_profile; + __u8 level_idc; + __u8 log2_max_pic_order_cnt_lsb; + __u8 log2_max_frame_num; + __u8 pic_order_cnt_type; + __u8 direct_8x8_inference_flag; + __u8 pic_width_in_mbs; + __u8 pic_height_in_mbs; + + // PPS + __u8 pic_init_qp; + __u8 deblocking_filter_control_present_flag; + __u8 constrained_intra_pred_flag; + __u8 chroma_qp_index_offset; + __u8 pic_order_present_flag; + + // Slice header + __u8 num_ref_idx_l0_active_minus1; + __u8 num_ref_idx_l1_active_minus1; + + __u32 reserved; +} __attribute__((packed)); + +#define VDE_IOCTL_BASE ('v' + 0x20) + +#define VDE_IO(nr) _IO(VDE_IOCTL_BASE, nr) +#define VDE_IOR(nr, type) _IOR(VDE_IOCTL_BASE, nr, type) +#define VDE_IOW(nr, type) _IOW(VDE_IOCTL_BASE, nr, type) +#define VDE_IOWR(nr, type) _IOWR(VDE_IOCTL_BASE, nr, type) + +#define TEGRA_VDE_DECODE_H264 0x00 + +#define TEGRA_VDE_IOCTL_DECODE_H264 \ + VDE_IOW(TEGRA_VDE_DECODE_H264, struct tegra_vde_h264_decoder_ctx) + +#endif // _UAPI_TEGRA_VDE_H_ |