[PATCH 3/5] media: v4l2-common: add v4l2_find_closest_fract()
Add a function to locate the closest element in a sorted v4l2_fract array. The implementation is based on find_closest() macro in linux/util_macros.h and the way to compare two v4l2_fract in vivid_vid_cap_s_parm in drivers/media/platform/vivid/vivid-vid-cap.c. Cc: Matt Ranostay Cc: Sakari Ailus Cc: Hans Verkuil Cc: Mauro Carvalho Chehab Signed-off-by: Akinobu Mita --- drivers/media/v4l2-core/v4l2-common.c | 26 ++ include/media/v4l2-common.h | 12 2 files changed, 38 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index b518b92..91bd460 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -387,6 +387,32 @@ __v4l2_find_nearest_size(const void *array, size_t array_size, } EXPORT_SYMBOL_GPL(__v4l2_find_nearest_size); +#define FRACT_CMP(a, OP, b)\ + ((u64)(a).numerator * (b).denominator OP\ +(u64)(b).numerator * (a).denominator) + +int v4l2_find_closest_fract(struct v4l2_fract x, const struct v4l2_fract *array, + size_t num) +{ + int i; + + for (i = 0; i < num - 1; i++) { + struct v4l2_fract a = array[i]; + struct v4l2_fract b = array[i + 1]; + struct v4l2_fract midpoint = { + .numerator = a.numerator * b.denominator + +b.numerator * a.denominator, + .denominator = 2 * a.denominator * b.denominator, + }; + + if (FRACT_CMP(x, <=, midpoint)) + break; + } + + return i; +} +EXPORT_SYMBOL_GPL(v4l2_find_closest_fract); + void v4l2_get_timestamp(struct timeval *tv) { struct timespec ts; diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index cdc87ec..e388f4e 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -350,6 +350,18 @@ __v4l2_find_nearest_size(const void *array, size_t array_size, size_t height_offset, s32 width, s32 height); /** + * v4l2_find_closest_fract - locate the closest element in a sorted array + * @x: The reference value. + * @array: The array in which to look for the closest element. Must be sorted + * in ascending order. + * @num: number of elements in 'array'. + * + * Returns the index of the element closest to 'x'. + */ +int v4l2_find_closest_fract(struct v4l2_fract x, const struct v4l2_fract *array, + size_t num); + +/** * v4l2_get_timestamp - helper routine to get a timestamp to be used when * filling streaming metadata. Internally, it uses ktime_get_ts(), * which is the recommended way to get it. -- 2.7.4
[PATCH 5/5] media: video-i2c: support runtime PM
AMG88xx has a register for setting operating mode. This adds support runtime PM by changing the operating mode. The instruction for changing sleep mode to normal mode is from the reference specifications. https://docid81hrs3j1.cloudfront.net/medialibrary/2017/11/PANA-S-A0002141979-1.pdf Cc: Matt Ranostay Cc: Sakari Ailus Cc: Hans Verkuil Cc: Mauro Carvalho Chehab Signed-off-by: Akinobu Mita --- drivers/media/i2c/video-i2c.c | 140 +- 1 file changed, 138 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c index 916f36e..93822f4 100644 --- a/drivers/media/i2c/video-i2c.c +++ b/drivers/media/i2c/video-i2c.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -94,6 +95,9 @@ struct video_i2c_chip { /* xfer function */ int (*xfer)(struct video_i2c_data *data, char *buf); + /* power control function */ + int (*set_power)(struct video_i2c_data *data, bool on); + /* hwmon init function */ int (*hwmon_init)(struct video_i2c_data *data); }; @@ -104,6 +108,14 @@ static int amg88xx_xfer(struct video_i2c_data *data, char *buf) data->chip->buffer_size); } +#define AMG88XX_REG_PCTL 0x00 +#define AMG88XX_PCTL_NORMAL0x00 +#define AMG88XX_PCTL_SLEEP 0x10 + +#define AMG88XX_REG_RST0x01 +#define AMG88XX_RST_FLAG 0x30 +#define AMG88XX_RST_INIT 0x3f + #define AMG88XX_REG_FPSC 0x02 #define AMG88XX_FPSC_1FPS BIT(0) @@ -120,6 +132,59 @@ static int amg88xx_setup(struct video_i2c_data *data) return regmap_update_bits(data->regmap, AMG88XX_REG_FPSC, mask, val); } +static int amg88xx_set_power_on(struct video_i2c_data *data) +{ + int ret; + + ret = regmap_write(data->regmap, AMG88XX_REG_PCTL, AMG88XX_PCTL_NORMAL); + if (ret) + return ret; + + msleep(50); + + ret = regmap_write(data->regmap, AMG88XX_REG_RST, AMG88XX_RST_INIT); + if (ret) + return ret; + + msleep(2); + + ret = regmap_write(data->regmap, AMG88XX_REG_RST, AMG88XX_RST_FLAG); + if (ret) + return ret; + + /* +* Wait two frames before reading thermistor and temperature registers +*/ + msleep(200); + + return 0; +} + +static int amg88xx_set_power_off(struct video_i2c_data *data) +{ + int ret; + + ret = regmap_write(data->regmap, AMG88XX_REG_PCTL, AMG88XX_PCTL_SLEEP); + if (ret) + return ret; + /* +* Wait for a while to avoid resuming normal mode immediately after +* entering sleep mode, otherwise the device occasionally goes wrong +* (thermistor and temperature registers are not updated at all) +*/ + msleep(100); + + return 0; +} + +static int amg88xx_set_power(struct video_i2c_data *data, bool on) +{ + if (on) + return amg88xx_set_power_on(data); + + return amg88xx_set_power_off(data); +} + #if IS_ENABLED(CONFIG_HWMON) static const u32 amg88xx_temp_config[] = { @@ -151,7 +216,15 @@ static int amg88xx_read(struct device *dev, enum hwmon_sensor_types type, __le16 buf; int tmp; + tmp = pm_runtime_get_sync(regmap_get_device(data->regmap)); + if (tmp < 0) { + pm_runtime_put_noidle(regmap_get_device(data->regmap)); + return tmp; + } + tmp = regmap_bulk_read(data->regmap, 0x0e, , 2); + pm_runtime_mark_last_busy(regmap_get_device(data->regmap)); + pm_runtime_put_autosuspend(regmap_get_device(data->regmap)); if (tmp) return tmp; @@ -210,6 +283,7 @@ static const struct video_i2c_chip video_i2c_chip[] = { .regmap_config = _regmap_config, .setup = _setup, .xfer = _xfer, + .set_power = amg88xx_set_power, .hwmon_init = amg88xx_hwmon_init, }, }; @@ -336,14 +410,21 @@ static void video_i2c_del_list(struct vb2_queue *vq, enum vb2_buffer_state state static int start_streaming(struct vb2_queue *vq, unsigned int count) { struct video_i2c_data *data = vb2_get_drv_priv(vq); + struct device *dev = regmap_get_device(data->regmap); int ret; if (data->kthread_vid_cap) return 0; + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + pm_runtime_put_noidle(dev); + goto error_del_list; + } + ret = data->chip->setup(data); if (ret) - goto error_del_list; + goto error_rpm_put; data->sequence = 0; data->kthread_vid_cap = kthread_run(video_i2c_thread_vid_cap, data, @@ -352
[PATCH 0/5] media: video-i2c: support changing frame interval and runtime PM
This patchset adds support for changing frame interval and runtime PM for video-i2c driver. This also adds an helper function to v4l2 common internal API that is used to to find a suitable frame interval. There are a couple of unrelated changes that are included for simplifying driver initialization code and register accesses. Akinobu Mita (5): media: video-i2c: avoid accessing released memory area when removing driver media: video-i2c: use i2c regmap media: v4l2-common: add v4l2_find_closest_fract() media: video-i2c: support changing frame interval media: video-i2c: support runtime PM drivers/media/i2c/video-i2c.c | 276 -- drivers/media/v4l2-core/v4l2-common.c | 26 include/media/v4l2-common.h | 12 ++ 3 files changed, 267 insertions(+), 47 deletions(-) Cc: Matt Ranostay Cc: Sakari Ailus Cc: Hans Verkuil Cc: Mauro Carvalho Chehab -- 2.7.4
[PATCH 2/5] media: video-i2c: use i2c regmap
Use regmap for i2c register access. This simplifies register accesses and chooses suitable access commands based on the functionality that the adapter supports. Cc: Matt Ranostay Cc: Sakari Ailus Cc: Hans Verkuil Cc: Mauro Carvalho Chehab Signed-off-by: Akinobu Mita --- drivers/media/i2c/video-i2c.c | 54 ++- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c index b7a2af9..90d389b 100644 --- a/drivers/media/i2c/video-i2c.c +++ b/drivers/media/i2c/video-i2c.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -38,7 +39,7 @@ struct video_i2c_buffer { }; struct video_i2c_data { - struct i2c_client *client; + struct regmap *regmap; const struct video_i2c_chip *chip; struct mutex lock; spinlock_t slock; @@ -62,6 +63,12 @@ static const struct v4l2_frmsize_discrete amg88xx_size = { .height = 8, }; +static const struct regmap_config amg88xx_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xff +}; + struct video_i2c_chip { /* video dimensions */ const struct v4l2_fmtdesc *format; @@ -76,6 +83,8 @@ struct video_i2c_chip { /* pixel size in bits */ unsigned int bpp; + const struct regmap_config *regmap_config; + /* xfer function */ int (*xfer)(struct video_i2c_data *data, char *buf); @@ -85,24 +94,8 @@ struct video_i2c_chip { static int amg88xx_xfer(struct video_i2c_data *data, char *buf) { - struct i2c_client *client = data->client; - struct i2c_msg msg[2]; - u8 reg = 0x80; - int ret; - - msg[0].addr = client->addr; - msg[0].flags = 0; - msg[0].len = 1; - msg[0].buf = (char *) - - msg[1].addr = client->addr; - msg[1].flags = I2C_M_RD; - msg[1].len = data->chip->buffer_size; - msg[1].buf = (char *)buf; - - ret = i2c_transfer(client->adapter, msg, 2); - - return (ret == 2) ? 0 : -EIO; + return regmap_bulk_read(data->regmap, 0x80, buf, + data->chip->buffer_size); } #if IS_ENABLED(CONFIG_HWMON) @@ -133,12 +126,15 @@ static int amg88xx_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { struct video_i2c_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - int tmp = i2c_smbus_read_word_data(client, 0x0e); + __le16 buf; + int tmp; - if (tmp < 0) + tmp = regmap_bulk_read(data->regmap, 0x0e, , 2); + if (tmp) return tmp; + tmp = le16_to_cpu(buf); + /* * Check for sign bit, this isn't a two's complement value but an * absolute temperature that needs to be inverted in the case of being @@ -164,8 +160,9 @@ static const struct hwmon_chip_info amg88xx_chip_info = { static int amg88xx_hwmon_init(struct video_i2c_data *data) { - void *hwmon = devm_hwmon_device_register_with_info(>client->dev, - "amg88xx", data, _chip_info, NULL); + struct device *dev = regmap_get_device(data->regmap); + void *hwmon = devm_hwmon_device_register_with_info(dev, "amg88xx", data, + _chip_info, NULL); return PTR_ERR_OR_ZERO(hwmon); } @@ -182,6 +179,7 @@ static const struct video_i2c_chip video_i2c_chip[] = { .max_fps= 10, .buffer_size= 128, .bpp= 16, + .regmap_config = _regmap_config, .xfer = _xfer, .hwmon_init = amg88xx_hwmon_init, }, @@ -350,7 +348,8 @@ static int video_i2c_querycap(struct file *file, void *priv, struct v4l2_capability *vcap) { struct video_i2c_data *data = video_drvdata(file); - struct i2c_client *client = data->client; + struct device *dev = regmap_get_device(data->regmap); + struct i2c_client *client = to_i2c_client(dev); strlcpy(vcap->driver, data->v4l2_dev.name, sizeof(vcap->driver)); strlcpy(vcap->card, data->vdev.name, sizeof(vcap->card)); @@ -527,7 +526,10 @@ static int video_i2c_probe(struct i2c_client *client, else return -ENODEV; - data->client = client; + data->regmap = devm_regmap_init_i2c(client, data->chip->regmap_config); + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); + v4l2_dev = >v4l2_dev; strlcpy(v4l2_dev->name, VIDEO_I2C_DRIVER, sizeof(v4l2_dev->name)); -- 2.7.4
[PATCH 4/5] media: video-i2c: support changing frame interval
AMG88xx has a register for setting frame rate 1 or 10 FPS. This adds support changing frame interval. Reference specifications: https://docid81hrs3j1.cloudfront.net/medialibrary/2017/11/PANA-S-A0002141979-1.pdf Cc: Matt Ranostay Cc: Sakari Ailus Cc: Hans Verkuil Cc: Mauro Carvalho Chehab Signed-off-by: Akinobu Mita --- drivers/media/i2c/video-i2c.c | 76 --- 1 file changed, 64 insertions(+), 12 deletions(-) diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c index 90d389b..916f36e 100644 --- a/drivers/media/i2c/video-i2c.c +++ b/drivers/media/i2c/video-i2c.c @@ -52,6 +52,8 @@ struct video_i2c_data { struct task_struct *kthread_vid_cap; struct list_head vid_cap_active; + + struct v4l2_fract frame_interval; }; static const struct v4l2_fmtdesc amg88xx_format = { @@ -74,8 +76,9 @@ struct video_i2c_chip { const struct v4l2_fmtdesc *format; const struct v4l2_frmsize_discrete *size; - /* max frames per second */ - unsigned int max_fps; + /* available frame intervals */ + const struct v4l2_fract *frame_intervals; + unsigned int num_frame_intervals; /* pixel buffer size */ unsigned int buffer_size; @@ -85,6 +88,9 @@ struct video_i2c_chip { const struct regmap_config *regmap_config; + /* setup function */ + int (*setup)(struct video_i2c_data *data); + /* xfer function */ int (*xfer)(struct video_i2c_data *data, char *buf); @@ -98,6 +104,22 @@ static int amg88xx_xfer(struct video_i2c_data *data, char *buf) data->chip->buffer_size); } +#define AMG88XX_REG_FPSC 0x02 +#define AMG88XX_FPSC_1FPS BIT(0) + +static int amg88xx_setup(struct video_i2c_data *data) +{ + unsigned int mask = AMG88XX_FPSC_1FPS; + unsigned int val; + + if (data->frame_interval.numerator == data->frame_interval.denominator) + val = mask; + else + val = 0; + + return regmap_update_bits(data->regmap, AMG88XX_REG_FPSC, mask, val); +} + #if IS_ENABLED(CONFIG_HWMON) static const u32 amg88xx_temp_config[] = { @@ -172,14 +194,21 @@ static int amg88xx_hwmon_init(struct video_i2c_data *data) #define AMG88XX0 +static const struct v4l2_fract amg88xx_frame_intervals[] = { + { 1, 10 }, + { 1, 1 }, +}; + static const struct video_i2c_chip video_i2c_chip[] = { [AMG88XX] = { .size = _size, .format = _format, - .max_fps= 10, + .frame_intervals= amg88xx_frame_intervals, + .num_frame_intervals= ARRAY_SIZE(amg88xx_frame_intervals), .buffer_size= 128, .bpp= 16, .regmap_config = _regmap_config, + .setup = _setup, .xfer = _xfer, .hwmon_init = amg88xx_hwmon_init, }, @@ -244,7 +273,8 @@ static void buffer_queue(struct vb2_buffer *vb) static int video_i2c_thread_vid_cap(void *priv) { struct video_i2c_data *data = priv; - unsigned int delay = msecs_to_jiffies(1000 / data->chip->max_fps); + unsigned int delay = mult_frac(HZ, data->frame_interval.numerator, + data->frame_interval.denominator); set_freezable(); @@ -306,19 +336,26 @@ static void video_i2c_del_list(struct vb2_queue *vq, enum vb2_buffer_state state static int start_streaming(struct vb2_queue *vq, unsigned int count) { struct video_i2c_data *data = vb2_get_drv_priv(vq); + int ret; if (data->kthread_vid_cap) return 0; + ret = data->chip->setup(data); + if (ret) + goto error_del_list; + data->sequence = 0; data->kthread_vid_cap = kthread_run(video_i2c_thread_vid_cap, data, "%s-vid-cap", data->v4l2_dev.name); - if (!IS_ERR(data->kthread_vid_cap)) + ret = PTR_ERR_OR_ZERO(data->kthread_vid_cap); + if (!ret) return 0; +error_del_list: video_i2c_del_list(vq, VB2_BUF_STATE_QUEUED); - return PTR_ERR(data->kthread_vid_cap); + return ret; } static void stop_streaming(struct vb2_queue *vq) @@ -425,15 +462,14 @@ static int video_i2c_enum_frameintervals(struct file *file, void *priv, const struct video_i2c_data *data = video_drvdata(file); const struct v4l2_frmsize_discrete *size = data->chip->size; - if (fe->index > 0) + if (fe->index >= data->chip->num_frame_intervals) return -EINVAL; if (fe->width != size->width || fe->height != size->height) return -EINVAL; fe->
Re: [PATCH 5/5] media: video-i2c: support runtime PM
2018年9月18日(火) 15:23 Matt Ranostay : > > On Mon, Sep 17, 2018 at 6:03 PM Akinobu Mita wrote: > > > > AMG88xx has a register for setting operating mode. This adds support > > runtime PM by changing the operating mode. > > > > The instruction for changing sleep mode to normal mode is from the > > reference specifications. > > > > https://docid81hrs3j1.cloudfront.net/medialibrary/2017/11/PANA-S-A0002141979-1.pdf > > > > Cc: Matt Ranostay > > Cc: Sakari Ailus > > Cc: Hans Verkuil > > Cc: Mauro Carvalho Chehab > > Signed-off-by: Akinobu Mita > > --- > > drivers/media/i2c/video-i2c.c | 140 > > +- > > 1 file changed, 138 insertions(+), 2 deletions(-) > > > > diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c > > index 916f36e..93822f4 100644 > > --- a/drivers/media/i2c/video-i2c.c > > +++ b/drivers/media/i2c/video-i2c.c > > @@ -17,6 +17,7 @@ > > #include > > #include > > #include > > +#include > > #include > > #include > > #include > > @@ -94,6 +95,9 @@ struct video_i2c_chip { > > /* xfer function */ > > int (*xfer)(struct video_i2c_data *data, char *buf); > > > > + /* power control function */ > > + int (*set_power)(struct video_i2c_data *data, bool on); > > + > > /* hwmon init function */ > > int (*hwmon_init)(struct video_i2c_data *data); > > }; > > @@ -104,6 +108,14 @@ static int amg88xx_xfer(struct video_i2c_data *data, > > char *buf) > > data->chip->buffer_size); > > } > > > > +#define AMG88XX_REG_PCTL 0x00 > > +#define AMG88XX_PCTL_NORMAL0x00 > > +#define AMG88XX_PCTL_SLEEP 0x10 > > + > > +#define AMG88XX_REG_RST0x01 > > +#define AMG88XX_RST_FLAG 0x30 > > +#define AMG88XX_RST_INIT 0x3f > > + > > #define AMG88XX_REG_FPSC 0x02 > > #define AMG88XX_FPSC_1FPS BIT(0) > > > > @@ -120,6 +132,59 @@ static int amg88xx_setup(struct video_i2c_data *data) > > return regmap_update_bits(data->regmap, AMG88XX_REG_FPSC, mask, > > val); > > } > > > > +static int amg88xx_set_power_on(struct video_i2c_data *data) > > +{ > > + int ret; > > + > > + ret = regmap_write(data->regmap, AMG88XX_REG_PCTL, > > AMG88XX_PCTL_NORMAL); > > + if (ret) > > + return ret; > > + > > + msleep(50); > > + > > + ret = regmap_write(data->regmap, AMG88XX_REG_RST, AMG88XX_RST_INIT); > > + if (ret) > > + return ret; > > + > > + msleep(2); > > + > > + ret = regmap_write(data->regmap, AMG88XX_REG_RST, AMG88XX_RST_FLAG); > > + if (ret) > > + return ret; > > + > > + /* > > +* Wait two frames before reading thermistor and temperature > > registers > > +*/ > > + msleep(200); > > + > > + return 0; > > +} > > + > > +static int amg88xx_set_power_off(struct video_i2c_data *data) > > +{ > > + int ret; > > + > > + ret = regmap_write(data->regmap, AMG88XX_REG_PCTL, > > AMG88XX_PCTL_SLEEP); > > + if (ret) > > + return ret; > > + /* > > +* Wait for a while to avoid resuming normal mode immediately after > > +* entering sleep mode, otherwise the device occasionally goes wrong > > +* (thermistor and temperature registers are not updated at all) > > +*/ > > + msleep(100); > > + > > + return 0; > > +} > > + > > +static int amg88xx_set_power(struct video_i2c_data *data, bool on) > > +{ > > + if (on) > > + return amg88xx_set_power_on(data); > > + > > + return amg88xx_set_power_off(data); > > +} > > + > > #if IS_ENABLED(CONFIG_HWMON) > > > > static const u32 amg88xx_temp_config[] = { > > @@ -151,7 +216,15 @@ static int amg88xx_read(struct device *dev, enum > > hwmon_sensor_types type, > > __le16 buf; > > int tmp; > > > > + tmp = pm_runtime_get_sync(regmap_get_device(data->regmap)); > > + if (tmp < 0) { > > + pm_runtime_put_noidle(regmap_get_device(data->regmap)); > > + return tmp; > >
Re: [PATCH 2/5] media: video-i2c: use i2c regmap
2018年9月18日(火) 12:19 Matt Ranostay : > > On Mon, Sep 17, 2018 at 9:03 AM Akinobu Mita wrote: > > > > Use regmap for i2c register access. This simplifies register accesses and > > chooses suitable access commands based on the functionality that the > > adapter supports. > > > > Cc: Matt Ranostay > > Cc: Sakari Ailus > > Cc: Hans Verkuil > > Cc: Mauro Carvalho Chehab > > Signed-off-by: Akinobu Mita > > --- > > drivers/media/i2c/video-i2c.c | 54 > > ++- > > 1 file changed, 28 insertions(+), 26 deletions(-) > > > > diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c > > index b7a2af9..90d389b 100644 > > --- a/drivers/media/i2c/video-i2c.c > > +++ b/drivers/media/i2c/video-i2c.c > > @@ -17,6 +17,7 @@ > > #include > > #include > > #include > > +#include > > #include > > #include > > #include > > @@ -38,7 +39,7 @@ struct video_i2c_buffer { > > }; > > > > struct video_i2c_data { > > - struct i2c_client *client; > > + struct regmap *regmap; > > const struct video_i2c_chip *chip; > > struct mutex lock; > > spinlock_t slock; > > @@ -62,6 +63,12 @@ static const struct v4l2_frmsize_discrete amg88xx_size = > > { > > .height = 8, > > }; > > > > +static const struct regmap_config amg88xx_regmap_config = { > > + .reg_bits = 8, > > + .val_bits = 8, > > + .max_register = 0xff > > +}; > > + > > struct video_i2c_chip { > > /* video dimensions */ > > const struct v4l2_fmtdesc *format; > > @@ -76,6 +83,8 @@ struct video_i2c_chip { > > /* pixel size in bits */ > > unsigned int bpp; > > > > + const struct regmap_config *regmap_config; > > + > > /* xfer function */ > > int (*xfer)(struct video_i2c_data *data, char *buf); > > > > @@ -85,24 +94,8 @@ struct video_i2c_chip { > > > > static int amg88xx_xfer(struct video_i2c_data *data, char *buf) > > { > > - struct i2c_client *client = data->client; > > - struct i2c_msg msg[2]; > > - u8 reg = 0x80; > > - int ret; > > - > > - msg[0].addr = client->addr; > > - msg[0].flags = 0; > > - msg[0].len = 1; > > - msg[0].buf = (char *) > > - > > - msg[1].addr = client->addr; > > - msg[1].flags = I2C_M_RD; > > - msg[1].len = data->chip->buffer_size; > > - msg[1].buf = (char *)buf; > > - > > - ret = i2c_transfer(client->adapter, msg, 2); > > - > > - return (ret == 2) ? 0 : -EIO; > > + return regmap_bulk_read(data->regmap, 0x80, buf, > > + data->chip->buffer_size); > > May as well make 0x80 into a AMG88XX_REG_* define as in the later > patch in this series Sounds good. I'll do in v2. > > } > > > > #if IS_ENABLED(CONFIG_HWMON) > > @@ -133,12 +126,15 @@ static int amg88xx_read(struct device *dev, enum > > hwmon_sensor_types type, > > u32 attr, int channel, long *val) > > { > > struct video_i2c_data *data = dev_get_drvdata(dev); > > - struct i2c_client *client = data->client; > > - int tmp = i2c_smbus_read_word_data(client, 0x0e); > > + __le16 buf; > > + int tmp; > > > > - if (tmp < 0) > > + tmp = regmap_bulk_read(data->regmap, 0x0e, , 2); > > Same with 0x0e OK.