diff --git a/drivers/media/i2c/ov13858.c b/drivers/media/i2c/ov13858.c index bb81aca8d79338..1cff5be69aa2d8 100644 --- a/drivers/media/i2c/ov13858.c +++ b/drivers/media/i2c/ov13858.c @@ -1168,23 +1168,18 @@ static int ov13858_write_reg_list(struct ov13858 *ov13858, return ov13858_write_regs(ov13858, r_list->regs, r_list->num_of_regs); } -/* Open sub-device */ -static int ov13858_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +/* Initialize pad format */ +static int ov13858_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) { struct ov13858 *ov13858 = to_ov13858(sd); - struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_state_get_format(fh->state, - 0); + struct v4l2_mbus_framefmt *format = + v4l2_subdev_state_get_format(sd_state, 0); - mutex_lock(&ov13858->mutex); - - /* Initialize try_fmt */ - try_fmt->width = ov13858->cur_mode->width; - try_fmt->height = ov13858->cur_mode->height; - try_fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; - try_fmt->field = V4L2_FIELD_NONE; - - /* No crop or compose */ - mutex_unlock(&ov13858->mutex); + format->width = ov13858->cur_mode->width; + format->height = ov13858->cur_mode->height; + format->code = MEDIA_BUS_FMT_SGRBG10_1X10; + format->field = V4L2_FIELD_NONE; return 0; } @@ -1360,9 +1355,8 @@ static int ov13858_get_pad_format(struct v4l2_subdev *sd, struct ov13858 *ov13858 = to_ov13858(sd); int ret; - mutex_lock(&ov13858->mutex); + /* The caller holds sd->state_lock, which is ov13858->mutex. */ ret = ov13858_do_get_pad_format(ov13858, sd_state, fmt); - mutex_unlock(&ov13858->mutex); return ret; } @@ -1381,7 +1375,7 @@ ov13858_set_pad_format(struct v4l2_subdev *sd, s64 pixel_rate; s64 link_freq; - mutex_lock(&ov13858->mutex); + /* The caller holds sd->state_lock, which is ov13858->mutex. */ /* Only one raw bayer(GRBG) order is supported */ if (fmt->format.code != MEDIA_BUS_FMT_SGRBG10_1X10) @@ -1418,8 +1412,6 @@ ov13858_set_pad_format(struct v4l2_subdev *sd, h_blank, 1, h_blank); } - mutex_unlock(&ov13858->mutex); - return 0; } @@ -1479,47 +1471,66 @@ static int ov13858_stop_streaming(struct ov13858 *ov13858) OV13858_REG_VALUE_08BIT, OV13858_MODE_STANDBY); } -static int ov13858_set_stream(struct v4l2_subdev *sd, int enable) +static int ov13858_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) { struct ov13858 *ov13858 = to_ov13858(sd); - int ret = 0; + int ret; - mutex_lock(&ov13858->mutex); + /* The caller holds sd->state_lock, which is ov13858->mutex. */ - if (enable && READ_ONCE(ov13858->power_fault)) { + if (READ_ONCE(ov13858->power_fault)) { ret = -EIO; goto err_unlock; } - if (enable) { - ret = pm_runtime_resume_and_get(ov13858->dev); - if (ret < 0) - goto err_unlock; - - /* - * Apply default & customized values - * and then start streaming. - */ - ret = ov13858_start_streaming(ov13858); - if (ret) - goto err_rpm_put; - } else { - ov13858_stop_streaming(ov13858); - pm_runtime_put(ov13858->dev); - } + ret = pm_runtime_resume_and_get(ov13858->dev); + if (ret < 0) + goto err_unlock; - mutex_unlock(&ov13858->mutex); + /* + * Apply default & customized values + * and then start streaming. + */ + ret = ov13858_start_streaming(ov13858); + if (ret) + goto err_rpm_put; - return ret; + return 0; err_rpm_put: pm_runtime_put(ov13858->dev); err_unlock: - mutex_unlock(&ov13858->mutex); - return ret; } +static int ov13858_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct ov13858 *ov13858 = to_ov13858(sd); + int ret; + + /* The caller holds sd->state_lock, which is ov13858->mutex. */ + + ret = ov13858_stop_streaming(ov13858); + if (ret) { + /* + * The standby write failed: the sensor may still be + * streaming. Keep the PM reference so the sensor is + * not powered down mid-stream, and report the failure + * so the framework retains the enabled state and can + * retry the stop (V4L2_SUBDEV_FL_STOP_RETRY). + */ + return ret; + } + + pm_runtime_put(ov13858->dev); + + return 0; +} + /* Verify chip ID */ static int ov13858_identify_module(struct ov13858 *ov13858) { @@ -1547,7 +1558,7 @@ static const struct v4l2_subdev_core_ops ov13858_core_ops = { }; static const struct v4l2_subdev_video_ops ov13858_video_ops = { - .s_stream = ov13858_set_stream, + .s_stream = v4l2_subdev_s_stream_helper, }; static const struct v4l2_subdev_pad_ops ov13858_pad_ops = { @@ -1555,6 +1566,8 @@ static const struct v4l2_subdev_pad_ops ov13858_pad_ops = { .get_fmt = ov13858_get_pad_format, .set_fmt = ov13858_set_pad_format, .enum_frame_size = ov13858_enum_frame_size, + .enable_streams = ov13858_enable_streams, + .disable_streams = ov13858_disable_streams, }; static const struct v4l2_subdev_sensor_ops ov13858_sensor_ops = { @@ -1573,7 +1586,7 @@ static const struct media_entity_operations ov13858_subdev_entity_ops = { }; static const struct v4l2_subdev_internal_ops ov13858_internal_ops = { - .open = ov13858_open, + .init_state = ov13858_init_state, }; /* Initialize control handlers */ @@ -1916,9 +1929,11 @@ static int ov13858_probe(struct i2c_client *client) /* Initialize subdev */ ov13858->sd.internal_ops = &ov13858_internal_ops; ov13858->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | - V4L2_SUBDEV_FL_HAS_EVENTS; + V4L2_SUBDEV_FL_HAS_EVENTS | + V4L2_SUBDEV_FL_STOP_RETRY; ov13858->sd.entity.ops = &ov13858_subdev_entity_ops; ov13858->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + ov13858->sd.state_lock = &ov13858->mutex; /* Initialize source pad */ ov13858->pad.flags = MEDIA_PAD_FL_SOURCE; @@ -1928,6 +1943,13 @@ static int ov13858_probe(struct i2c_client *client) goto error_handler_free; } + ret = v4l2_subdev_init_finalize(&ov13858->sd); + if (ret < 0) { + dev_err(ov13858->dev, "subdev initialization failed: %d\n", + ret); + goto error_media_entity; + } + /* * The driver powers DT devices; i2c-core powers ACPI devices. Hold the * initial reference until async registration has finished, since binding @@ -1935,7 +1957,7 @@ static int ov13858_probe(struct i2c_client *client) */ ret = pm_runtime_set_active(ov13858->dev); if (ret) - goto error_media_entity; + goto error_subdev_cleanup; pm_runtime_get_noresume(ov13858->dev); pm_runtime_enable(ov13858->dev); @@ -1951,6 +1973,9 @@ static int ov13858_probe(struct i2c_client *client) pm_runtime_disable(ov13858->dev); pm_runtime_put_noidle(ov13858->dev); +error_subdev_cleanup: + v4l2_subdev_cleanup(&ov13858->sd); + error_media_entity: media_entity_cleanup(&ov13858->sd.entity); @@ -1974,6 +1999,7 @@ static void ov13858_remove(struct i2c_client *client) int ret = 0; v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); media_entity_cleanup(&sd->entity); ov13858_free_controls(ov13858); diff --git a/drivers/media/i2c/vd55g1.c b/drivers/media/i2c/vd55g1.c index 340da6d64811d7..f859d2501b796b 100644 --- a/drivers/media/i2c/vd55g1.c +++ b/drivers/media/i2c/vd55g1.c @@ -2192,7 +2192,7 @@ static int vd55g1_enable_streams(struct v4l2_subdev *sd, err_rpm_put: pm_runtime_put(sensor->dev); - return -EINVAL; + return ret; } static int vd55g1_disable_streams(struct v4l2_subdev *sd, @@ -3162,6 +3162,42 @@ static void vd55g1_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); struct vd55g1 *sensor = to_vd55g1(sd); + struct v4l2_subdev_state *state; + + /* + * Unregistering the subdev does not stop a live stream: quiesce a + * still-streaming sensor to software standby before cutting power, + * mirroring vd55g1_disable_streams() without the PM put, so the + * exposure cluster is saved and the sensor is not powered down + * mid-stream. + * + * v4l2_subdev_is_streaming() requires the active-state lock to be + * held; keep it across the quiesce so the check cannot race with a + * concurrent stream or control operation. The active state is still + * allocated here: it is freed by vd55g1_subdev_cleanup() below. + */ + state = v4l2_subdev_lock_and_get_active_state(sd); + + if (v4l2_subdev_is_streaming(sd)) { + int stop_ret = 0; + int ret; + + ret = vd55g1_read_expo_cluster(sensor); + if (ret) + dev_warn(sensor->dev, + "Failed to save exposure: %d\n", ret); + + vd55g1_write(sensor, REG_STREAMING, + VD55G1_STREAMING_STOP_STREAM, &stop_ret); + vd55g1_poll_reg(sensor, REG_STREAMING, 0, &stop_ret); + vd55g1_wait_state(sensor, VD55G1_SYSTEM_FSM_SW_STBY, &stop_ret); + if (stop_ret) + dev_warn(sensor->dev, + "Failed to stop stream on remove: %d\n", + stop_ret); + } + + v4l2_subdev_unlock_state(state); vd55g1_subdev_cleanup(sensor);