summaryrefslogtreecommitdiff
path: root/drivers/media/i2c/ccs/ccs-core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/i2c/ccs/ccs-core.c')
-rw-r--r--drivers/media/i2c/ccs/ccs-core.c858
1 files changed, 459 insertions, 399 deletions
diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index f8523140784c..aa4dd7e7cf5a 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -13,6 +13,7 @@
* Based on smia-sensor.c by Tuukka Toivonen <tuukkat76@gmail.com>
*/
+#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
@@ -25,6 +26,7 @@
#include <linux/slab.h>
#include <linux/smiapp.h>
#include <linux/v4l2-mediabus.h>
+#include <media/mipi-csi2.h>
#include <media/v4l2-cci.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fwnode.h>
@@ -245,6 +247,33 @@ out_err:
return ret;
}
+static u8 ccs_mipi_csi2_data_type(unsigned int bpp)
+{
+ switch (bpp) {
+ case 6:
+ return MIPI_CSI2_DT_RAW6;
+ case 7:
+ return MIPI_CSI2_DT_RAW7;
+ case 8:
+ return MIPI_CSI2_DT_RAW8;
+ case 10:
+ return MIPI_CSI2_DT_RAW10;
+ case 12:
+ return MIPI_CSI2_DT_RAW12;
+ case 14:
+ return MIPI_CSI2_DT_RAW14;
+ case 16:
+ return MIPI_CSI2_DT_RAW16;
+ case 20:
+ return MIPI_CSI2_DT_RAW20;
+ case 24:
+ return MIPI_CSI2_DT_RAW24;
+ default:
+ WARN_ON(1);
+ return 0;
+ }
+}
+
static int ccs_read_frame_fmt(struct ccs_sensor *sensor)
{
struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
@@ -483,16 +512,71 @@ static int ccs_pll_try(struct ccs_sensor *sensor, struct ccs_pll *pll)
return ccs_pll_calculate(&client->dev, &lim, pll);
}
+static void
+ccs_get_binning(struct ccs_sensor *sensor, u8 *binning_mode, u8 *binh, u8 *binv)
+{
+ struct v4l2_subdev_state *state =
+ v4l2_subdev_get_locked_active_state(&sensor->binner->sd);
+ const struct v4l2_rect *sink_crop =
+ v4l2_subdev_state_get_crop(state, CCS_PAD_SINK,
+ CCS_STREAM_PIXEL);
+ const struct v4l2_rect *sink_comp =
+ v4l2_subdev_state_get_compose(state, CCS_PAD_SINK,
+ CCS_STREAM_PIXEL);
+
+ if (binning_mode)
+ *binning_mode = sink_crop->width == sink_comp->width &&
+ sink_crop->height == sink_comp->height ? 0 : 1;
+
+ *binh = sink_crop->width / sink_comp->width;
+ *binv = sink_crop->height / sink_comp->height;
+}
+
+static void ccs_get_scaling(struct ccs_sensor *sensor, u8 *scaling_mode,
+ u8 *scale_m)
+{
+ struct v4l2_subdev_state *state =
+ v4l2_subdev_get_locked_active_state(&sensor->scaler->sd);
+ const struct v4l2_rect *sink_crop =
+ v4l2_subdev_state_get_crop(state, CCS_PAD_SINK,
+ CCS_STREAM_PIXEL);
+ const struct v4l2_rect *sink_comp =
+ v4l2_subdev_state_get_compose(state, CCS_PAD_SINK,
+ CCS_STREAM_PIXEL);
+
+ *scale_m = sink_crop->width * CCS_LIM(sensor, SCALER_N_MIN) /
+ sink_comp->width;
+
+ if (!scaling_mode)
+ return;
+
+ if (sink_crop->width == sink_comp->width)
+ *scaling_mode = CCS_SCALING_MODE_NO_SCALING;
+ else if (sink_crop->height == sink_comp->height)
+ *scaling_mode = CCS_SCALING_MODE_HORIZONTAL;
+ else
+ *scaling_mode = SMIAPP_SCALING_MODE_BOTH;
+}
+
static int ccs_pll_update(struct ccs_sensor *sensor)
{
struct ccs_pll *pll = &sensor->pll;
+ u8 binh, binv;
+ u8 scale_m;
int rval;
- pll->binning_horizontal = sensor->binning_horizontal;
- pll->binning_vertical = sensor->binning_vertical;
+ ccs_get_binning(sensor, NULL, &binh, &binv);
+
+ if (sensor->scaler)
+ ccs_get_scaling(sensor, NULL, &scale_m);
+ else
+ scale_m = CCS_LIM(sensor, SCALER_N_MIN);
+
+ pll->binning_horizontal = binh;
+ pll->binning_vertical = binv;
pll->link_freq =
sensor->link_freq->qmenu_int[sensor->link_freq->val];
- pll->scale_m = sensor->scale_m;
+ pll->scale_m = scale_m;
pll->bits_per_pixel = sensor->csi_format->compressed;
rval = ccs_pll_try(sensor, pll);
@@ -513,12 +597,13 @@ static int ccs_pll_update(struct ccs_sensor *sensor)
*
*/
-static void __ccs_update_exposure_limits(struct ccs_sensor *sensor)
+static void __ccs_update_exposure_limits(struct ccs_sensor *sensor,
+ const struct v4l2_rect *pa_src)
{
struct v4l2_ctrl *ctrl = sensor->exposure;
int max;
- max = sensor->pa_src.height + sensor->vblank->val -
+ max = pa_src->height + sensor->vblank->val -
CCS_LIM(sensor, COARSE_INTEGRATION_TIME_MAX_MARGIN);
__v4l2_ctrl_modify_range(ctrl, ctrl->minimum, max, ctrl->step, max);
@@ -621,12 +706,20 @@ static int ccs_set_ctrl(struct v4l2_ctrl *ctrl)
container_of(ctrl->handler, struct ccs_subdev, ctrl_handler)
->sensor;
struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ struct v4l2_subdev_state *state;
+ const struct v4l2_rect *pa_src = NULL;
int pm_status;
u32 orient = 0;
unsigned int i;
int exposure;
int rval;
+ if (ctrl->id == V4L2_CID_VBLANK || ctrl->id == V4L2_CID_HBLANK) {
+ state = v4l2_subdev_get_locked_active_state(&sensor->pixel_array->sd);
+ pa_src = v4l2_subdev_state_get_crop(state, CCS_PA_PAD_SRC,
+ CCS_STREAM_PIXEL);
+ }
+
switch (ctrl->id) {
case V4L2_CID_HFLIP:
case V4L2_CID_VFLIP:
@@ -645,7 +738,7 @@ static int ccs_set_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_VBLANK:
exposure = sensor->exposure->val;
- __ccs_update_exposure_limits(sensor);
+ __ccs_update_exposure_limits(sensor, pa_src);
if (exposure > sensor->exposure->maximum) {
sensor->exposure->val = sensor->exposure->maximum;
@@ -737,12 +830,12 @@ static int ccs_set_ctrl(struct v4l2_ctrl *ctrl)
break;
case V4L2_CID_VBLANK:
rval = ccs_write(sensor, FRAME_LENGTH_LINES,
- sensor->pa_src.height + ctrl->val);
+ pa_src->height + ctrl->val);
break;
case V4L2_CID_HBLANK:
rval = ccs_write(sensor, LINE_LENGTH_PCK,
- sensor->pa_src.width + ctrl->val);
+ pa_src->width + ctrl->val);
break;
case V4L2_CID_TEST_PATTERN:
@@ -1117,14 +1210,14 @@ static int ccs_get_mbus_formats(struct ccs_sensor *sensor)
dev_dbg(&client->dev, "jolly good! %u\n", j);
- sensor->default_mbus_frame_fmts |= 1 << j;
+ sensor->default_mbus_frame_fmts |= BIT_U64(j);
}
}
/* Figure out which BPP values can be used with which formats. */
pll->binning_horizontal = 1;
pll->binning_vertical = 1;
- pll->scale_m = sensor->scale_m;
+ pll->scale_m = CCS_LIM(sensor, SCALER_N_MIN);
for (i = 0; i < ARRAY_SIZE(ccs_csi_data_formats); i++) {
sensor->compressed_min_bpp =
@@ -1150,7 +1243,7 @@ static int ccs_get_mbus_formats(struct ccs_sensor *sensor)
f->compressed - sensor->compressed_min_bpp];
unsigned int j;
- if (!(sensor->default_mbus_frame_fmts & 1 << i))
+ if (!(sensor->default_mbus_frame_fmts & BIT_U64(i)))
continue;
pll->bits_per_pixel = f->compressed;
@@ -1195,14 +1288,18 @@ static int ccs_get_mbus_formats(struct ccs_sensor *sensor)
return 0;
}
-static void ccs_update_blanking(struct ccs_sensor *sensor)
+static void ccs_update_blanking(struct ccs_sensor *sensor,
+ const struct v4l2_rect *pa_src)
{
struct v4l2_ctrl *vblank = sensor->vblank;
struct v4l2_ctrl *hblank = sensor->hblank;
u16 min_fll, max_fll, min_llp, max_llp, min_lbp;
int min, max;
+ u8 binh, binv;
+
+ ccs_get_binning(sensor, NULL, &binh, &binv);
- if (sensor->binning_vertical > 1 || sensor->binning_horizontal > 1) {
+ if (binv > 1 || binh > 1) {
min_fll = CCS_LIM(sensor, MIN_FRAME_LENGTH_LINES_BIN);
max_fll = CCS_LIM(sensor, MAX_FRAME_LENGTH_LINES_BIN);
min_llp = CCS_LIM(sensor, MIN_LINE_LENGTH_PCK_BIN);
@@ -1218,21 +1315,26 @@ static void ccs_update_blanking(struct ccs_sensor *sensor)
min = max_t(int,
CCS_LIM(sensor, MIN_FRAME_BLANKING_LINES),
- min_fll - sensor->pa_src.height);
- max = max_fll - sensor->pa_src.height;
+ min_fll - pa_src->height);
+ max = max_fll - pa_src->height;
__v4l2_ctrl_modify_range(vblank, min, max, vblank->step, min);
- min = max_t(int, min_llp - sensor->pa_src.width, min_lbp);
- max = max_llp - sensor->pa_src.width;
+ min = max_t(int, min_llp - pa_src->width, min_lbp);
+ max = max_llp - pa_src->width;
__v4l2_ctrl_modify_range(hblank, min, max, hblank->step, min);
- __ccs_update_exposure_limits(sensor);
+ __ccs_update_exposure_limits(sensor, pa_src);
}
static int ccs_pll_blanking_update(struct ccs_sensor *sensor)
{
+ struct v4l2_subdev_state *state =
+ v4l2_subdev_get_locked_active_state(&sensor->pixel_array->sd);
+ const struct v4l2_rect *pa_src =
+ v4l2_subdev_state_get_crop(state, CCS_PA_PAD_SRC,
+ CCS_STREAM_PIXEL);
struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
int rval;
@@ -1241,15 +1343,15 @@ static int ccs_pll_blanking_update(struct ccs_sensor *sensor)
return rval;
/* Output from pixel array, including blanking */
- ccs_update_blanking(sensor);
+ ccs_update_blanking(sensor, pa_src);
dev_dbg(&client->dev, "vblank\t\t%d\n", sensor->vblank->val);
dev_dbg(&client->dev, "hblank\t\t%d\n", sensor->hblank->val);
dev_dbg(&client->dev, "real timeperframe\t100/%d\n",
sensor->pll.pixel_rate_pixel_array /
- ((sensor->pa_src.width + sensor->hblank->val) *
- (sensor->pa_src.height + sensor->vblank->val) / 100));
+ ((pa_src->width + sensor->hblank->val) *
+ (pa_src->height + sensor->vblank->val) / 100));
return 0;
}
@@ -1704,72 +1806,122 @@ static int ccs_power_off(struct device *dev)
usleep_range(5000, 5000);
regulator_bulk_disable(ARRAY_SIZE(ccs_regulators),
sensor->regulators);
- sensor->streaming = false;
return 0;
}
/* -----------------------------------------------------------------------------
- * Video stream management
+ * V4L2 subdev video operations
*/
-static int ccs_start_streaming(struct ccs_sensor *sensor)
+static int ccs_pm_get_init(struct ccs_sensor *sensor)
{
struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
- unsigned int binning_mode;
int rval;
- mutex_lock(&sensor->mutex);
+ /*
+ * It can't use pm_runtime_resume_and_get() here, as the driver
+ * relies at the returned value to detect if the device was already
+ * active or not.
+ */
+ rval = pm_runtime_get_sync(&client->dev);
+ if (rval < 0)
+ goto error;
+
+ /* Device was already active, so don't set controls */
+ if (rval == 1 && !sensor->handler_setup_needed)
+ return 0;
+
+ sensor->handler_setup_needed = false;
+
+ /* Restore V4L2 controls to the previously suspended device */
+ rval = __v4l2_ctrl_handler_setup(&sensor->pixel_array->ctrl_handler);
+ if (rval)
+ goto error;
+
+ rval = __v4l2_ctrl_handler_setup(&sensor->src->ctrl_handler);
+ if (rval)
+ goto error;
+
+ /* Keep PM runtime usage_count incremented on success */
+ return 0;
+
+error:
+ pm_runtime_put(&client->dev);
+ return rval;
+}
+
+static int ccs_enable_streams(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct ccs_sensor *sensor = to_ccs_sensor(subdev);
+ struct v4l2_subdev_state *pa_state =
+ v4l2_subdev_get_locked_active_state(&sensor->pixel_array->sd);
+ struct v4l2_subdev_state *src_state =
+ v4l2_subdev_get_locked_active_state(&sensor->src->sd);
+ const struct v4l2_rect *pa_src =
+ v4l2_subdev_state_get_crop(pa_state, CCS_PA_PAD_SRC,
+ CCS_STREAM_PIXEL);
+ const struct v4l2_rect *src_src =
+ v4l2_subdev_state_get_crop(src_state, CCS_PAD_SRC,
+ CCS_STREAM_PIXEL);
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ u8 binning_mode, binh, binv;
+ int rval;
+
+ if (pad != CCS_PAD_SRC)
+ return -EINVAL;
+
+ if (sensor->streaming) {
+ sensor->streaming |= streams_mask;
+ return 0;
+ }
+
+ rval = ccs_pm_get_init(sensor);
+ if (rval)
+ return rval;
rval = ccs_write(sensor, CSI_DATA_FORMAT,
(sensor->csi_format->width << 8) |
sensor->csi_format->compressed);
if (rval)
- goto out;
+ goto err_pm_put;
/* Binning configuration */
- if (sensor->binning_horizontal == 1 &&
- sensor->binning_vertical == 1) {
- binning_mode = 0;
- } else {
- u8 binning_type =
- (sensor->binning_horizontal << 4)
- | sensor->binning_vertical;
+ ccs_get_binning(sensor, &binning_mode, &binh, &binv);
- rval = ccs_write(sensor, BINNING_TYPE, binning_type);
+ if (binning_mode) {
+ rval = ccs_write(sensor, BINNING_TYPE, (binh << 4) | binv);
if (rval < 0)
- goto out;
-
- binning_mode = 1;
+ goto err_pm_put;
}
rval = ccs_write(sensor, BINNING_MODE, binning_mode);
if (rval < 0)
- goto out;
+ goto err_pm_put;
/* Set up PLL */
rval = ccs_pll_configure(sensor);
if (rval)
- goto out;
+ goto err_pm_put;
/* Analog crop start coordinates */
- rval = ccs_write(sensor, X_ADDR_START, sensor->pa_src.left);
+ rval = ccs_write(sensor, X_ADDR_START, pa_src->left);
if (rval < 0)
- goto out;
+ goto err_pm_put;
- rval = ccs_write(sensor, Y_ADDR_START, sensor->pa_src.top);
+ rval = ccs_write(sensor, Y_ADDR_START, pa_src->top);
if (rval < 0)
- goto out;
+ goto err_pm_put;
/* Analog crop end coordinates */
- rval = ccs_write(sensor, X_ADDR_END,
- sensor->pa_src.left + sensor->pa_src.width - 1);
+ rval = ccs_write(sensor, X_ADDR_END, pa_src->left + pa_src->width - 1);
if (rval < 0)
- goto out;
+ goto err_pm_put;
- rval = ccs_write(sensor, Y_ADDR_END,
- sensor->pa_src.top + sensor->pa_src.height - 1);
+ rval = ccs_write(sensor, Y_ADDR_END, pa_src->top + pa_src->height - 1);
if (rval < 0)
- goto out;
+ goto err_pm_put;
/*
* Output from pixel array, including blanking, is set using
@@ -1779,46 +1931,57 @@ static int ccs_start_streaming(struct ccs_sensor *sensor)
/* Digital crop */
if (CCS_LIM(sensor, DIGITAL_CROP_CAPABILITY)
== CCS_DIGITAL_CROP_CAPABILITY_INPUT_CROP) {
+ struct v4l2_subdev_state *scaler_state =
+ v4l2_subdev_get_locked_active_state(&sensor->scaler->sd);
+ const struct v4l2_rect *scaler_sink =
+ v4l2_subdev_state_get_crop(scaler_state,
+ sensor->scaler->sink_pad,
+ CCS_STREAM_PIXEL);
+
rval = ccs_write(sensor, DIGITAL_CROP_X_OFFSET,
- sensor->scaler_sink.left);
+ scaler_sink->left);
if (rval < 0)
- goto out;
+ goto err_pm_put;
rval = ccs_write(sensor, DIGITAL_CROP_Y_OFFSET,
- sensor->scaler_sink.top);
+ scaler_sink->top);
if (rval < 0)
- goto out;
+ goto err_pm_put;
rval = ccs_write(sensor, DIGITAL_CROP_IMAGE_WIDTH,
- sensor->scaler_sink.width);
+ scaler_sink->width);
if (rval < 0)
- goto out;
+ goto err_pm_put;
rval = ccs_write(sensor, DIGITAL_CROP_IMAGE_HEIGHT,
- sensor->scaler_sink.height);
+ scaler_sink->height);
if (rval < 0)
- goto out;
+ goto err_pm_put;
}
/* Scaling */
if (CCS_LIM(sensor, SCALING_CAPABILITY)
!= CCS_SCALING_CAPABILITY_NONE) {
- rval = ccs_write(sensor, SCALING_MODE, sensor->scaling_mode);
+ u8 scaling_mode, scale_m;
+
+ ccs_get_scaling(sensor, &scaling_mode, &scale_m);
+
+ rval = ccs_write(sensor, SCALING_MODE, scaling_mode);
if (rval < 0)
- goto out;
+ goto err_pm_put;
- rval = ccs_write(sensor, SCALE_M, sensor->scale_m);
+ rval = ccs_write(sensor, SCALE_M, scale_m);
if (rval < 0)
- goto out;
+ goto err_pm_put;
}
/* Output size from sensor */
- rval = ccs_write(sensor, X_OUTPUT_SIZE, sensor->src_src.width);
+ rval = ccs_write(sensor, X_OUTPUT_SIZE, src_src->width);
if (rval < 0)
- goto out;
- rval = ccs_write(sensor, Y_OUTPUT_SIZE, sensor->src_src.height);
+ goto err_pm_put;
+ rval = ccs_write(sensor, Y_OUTPUT_SIZE, src_src->height);
if (rval < 0)
- goto out;
+ goto err_pm_put;
if (CCS_LIM(sensor, FLASH_MODE_CAPABILITY) &
(CCS_FLASH_MODE_CAPABILITY_SINGLE_STROBE |
@@ -1827,109 +1990,53 @@ static int ccs_start_streaming(struct ccs_sensor *sensor)
sensor->hwcfg.strobe_setup->trigger != 0) {
rval = ccs_setup_flash_strobe(sensor);
if (rval)
- goto out;
+ goto err_pm_put;
}
rval = ccs_call_quirk(sensor, pre_streamon);
if (rval) {
dev_err(&client->dev, "pre_streamon quirks failed\n");
- goto out;
+ goto err_pm_put;
}
rval = ccs_write(sensor, MODE_SELECT, CCS_MODE_SELECT_STREAMING);
-out:
- mutex_unlock(&sensor->mutex);
-
- return rval;
-}
-
-static int ccs_stop_streaming(struct ccs_sensor *sensor)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
- int rval;
-
- mutex_lock(&sensor->mutex);
- rval = ccs_write(sensor, MODE_SELECT, CCS_MODE_SELECT_SOFTWARE_STANDBY);
- if (rval)
- goto out;
-
- rval = ccs_call_quirk(sensor, post_streamoff);
- if (rval)
- dev_err(&client->dev, "post_streamoff quirks failed\n");
-
-out:
- mutex_unlock(&sensor->mutex);
- return rval;
-}
-
-/* -----------------------------------------------------------------------------
- * V4L2 subdev video operations
- */
-
-static int ccs_pm_get_init(struct ccs_sensor *sensor)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
- int rval;
-
- /*
- * It can't use pm_runtime_resume_and_get() here, as the driver
- * relies at the returned value to detect if the device was already
- * active or not.
- */
- rval = pm_runtime_get_sync(&client->dev);
- if (rval < 0)
- goto error;
+ sensor->streaming |= streams_mask;
- /* Device was already active, so don't set controls */
- if (rval == 1 && !sensor->handler_setup_needed)
- return 0;
-
- sensor->handler_setup_needed = false;
-
- /* Restore V4L2 controls to the previously suspended device */
- rval = v4l2_ctrl_handler_setup(&sensor->pixel_array->ctrl_handler);
- if (rval)
- goto error;
+ return 0;
- rval = v4l2_ctrl_handler_setup(&sensor->src->ctrl_handler);
- if (rval)
- goto error;
+err_pm_put:
+ pm_runtime_put_autosuspend(&client->dev);
- /* Keep PM runtime usage_count incremented on success */
- return 0;
-error:
- pm_runtime_put(&client->dev);
return rval;
}
-static int ccs_set_stream(struct v4l2_subdev *subdev, int enable)
+static int ccs_disable_streams(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
{
struct ccs_sensor *sensor = to_ccs_sensor(subdev);
struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
int rval;
- if (!enable) {
- ccs_stop_streaming(sensor);
- sensor->streaming = false;
- pm_runtime_put_autosuspend(&client->dev);
+ if (pad != CCS_PAD_SRC)
+ return -EINVAL;
+ sensor->streaming &= ~streams_mask;
+ if (sensor->streaming)
return 0;
- }
- rval = ccs_pm_get_init(sensor);
+ rval = ccs_write(sensor, MODE_SELECT, CCS_MODE_SELECT_SOFTWARE_STANDBY);
if (rval)
return rval;
- sensor->streaming = true;
+ rval = ccs_call_quirk(sensor, post_streamoff);
+ if (rval)
+ dev_err(&client->dev, "post_streamoff quirks failed\n");
- rval = ccs_start_streaming(sensor);
- if (rval < 0) {
- sensor->streaming = false;
- pm_runtime_put_autosuspend(&client->dev);
- }
+ pm_runtime_put_autosuspend(&client->dev);
- return rval;
+ return 0;
}
static int ccs_pre_streamon(struct v4l2_subdev *subdev, u32 flags)
@@ -1955,7 +2062,8 @@ static int ccs_pre_streamon(struct v4l2_subdev *subdev, u32 flags)
}
}
- rval = ccs_pm_get_init(sensor);
+ scoped_guard(mutex, &sensor->mutex)
+ rval = ccs_pm_get_init(sensor);
if (rval)
return rval;
@@ -1974,7 +2082,23 @@ static int ccs_post_streamoff(struct v4l2_subdev *subdev)
struct ccs_sensor *sensor = to_ccs_sensor(subdev);
struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
- return pm_runtime_put(&client->dev);
+ pm_runtime_put(&client->dev);
+
+ return 0;
+}
+
+static const struct ccs_csi_data_format
+*ccs_validate_csi_data_format(struct ccs_sensor *sensor, u32 code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(ccs_csi_data_formats); i++) {
+ if (sensor->mbus_frame_fmts & BIT_U64(i) &&
+ ccs_csi_data_formats[i].code == code)
+ return &ccs_csi_data_formats[i];
+ }
+
+ return sensor->csi_format;
}
static int ccs_enum_mbus_code(struct v4l2_subdev *subdev,
@@ -1985,42 +2109,35 @@ static int ccs_enum_mbus_code(struct v4l2_subdev *subdev,
struct ccs_sensor *sensor = to_ccs_sensor(subdev);
unsigned int i;
int idx = -1;
- int rval = -EINVAL;
-
- mutex_lock(&sensor->mutex);
dev_err(&client->dev, "subdev %s, pad %u, index %u\n",
subdev->name, code->pad, code->index);
if (subdev != &sensor->src->sd || code->pad != CCS_PAD_SRC) {
if (code->index)
- goto out;
+ return -EINVAL;
code->code = sensor->internal_csi_format->code;
- rval = 0;
- goto out;
+
+ return 0;
}
for (i = 0; i < ARRAY_SIZE(ccs_csi_data_formats); i++) {
- if (sensor->mbus_frame_fmts & (1 << i))
+ if (sensor->mbus_frame_fmts & BIT_U64(i))
idx++;
if (idx == code->index) {
code->code = ccs_csi_data_formats[i].code;
dev_err(&client->dev, "found index %u, i %u, code %x\n",
code->index, i, code->code);
- rval = 0;
- break;
+ return 0;
}
}
-out:
- mutex_unlock(&sensor->mutex);
-
- return rval;
+ return -EINVAL;
}
-static u32 __ccs_get_mbus_code(struct v4l2_subdev *subdev, unsigned int pad)
+static u32 ccs_get_mbus_code(struct v4l2_subdev *subdev, unsigned int pad)
{
struct ccs_sensor *sensor = to_ccs_sensor(subdev);
@@ -2030,102 +2147,47 @@ static u32 __ccs_get_mbus_code(struct v4l2_subdev *subdev, unsigned int pad)
return sensor->internal_csi_format->code;
}
-static int __ccs_get_format(struct v4l2_subdev *subdev,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *fmt)
-{
- fmt->format = *v4l2_subdev_state_get_format(sd_state, fmt->pad);
- fmt->format.code = __ccs_get_mbus_code(subdev, fmt->pad);
-
- return 0;
-}
-
static int ccs_get_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
- struct ccs_sensor *sensor = to_ccs_sensor(subdev);
- int rval;
-
- mutex_lock(&sensor->mutex);
- rval = __ccs_get_format(subdev, sd_state, fmt);
- mutex_unlock(&sensor->mutex);
-
- return rval;
-}
-
-static void ccs_get_crop_compose(struct v4l2_subdev *subdev,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_rect **crops,
- struct v4l2_rect **comps)
-{
- struct ccs_subdev *ssd = to_ccs_subdev(subdev);
- unsigned int i;
+ fmt->format = *v4l2_subdev_state_get_format(sd_state, fmt->pad);
+ fmt->format.code = ccs_get_mbus_code(subdev, fmt->pad);
- if (crops)
- for (i = 0; i < subdev->entity.num_pads; i++)
- crops[i] =
- v4l2_subdev_state_get_crop(sd_state, i);
- if (comps)
- *comps = v4l2_subdev_state_get_compose(sd_state,
- ssd->sink_pad);
+ return 0;
}
/* Changes require propagation only on sink pad. */
static void ccs_propagate(struct v4l2_subdev *subdev,
- struct v4l2_subdev_state *sd_state, int which,
- int target)
+ struct v4l2_subdev_state *sd_state, int target)
{
- struct ccs_sensor *sensor = to_ccs_sensor(subdev);
struct ccs_subdev *ssd = to_ccs_subdev(subdev);
- struct v4l2_rect *comp, *crops[CCS_PADS];
+ struct v4l2_rect *comp, *crop;
struct v4l2_mbus_framefmt *fmt;
- ccs_get_crop_compose(subdev, sd_state, crops, &comp);
-
+ comp = v4l2_subdev_state_get_compose(sd_state, ssd->sink_pad,
+ CCS_STREAM_PIXEL);
switch (target) {
case V4L2_SEL_TGT_CROP:
- comp->width = crops[CCS_PAD_SINK]->width;
- comp->height = crops[CCS_PAD_SINK]->height;
- if (which == V4L2_SUBDEV_FORMAT_ACTIVE) {
- if (ssd == sensor->scaler) {
- sensor->scale_m = CCS_LIM(sensor, SCALER_N_MIN);
- sensor->scaling_mode =
- CCS_SCALING_MODE_NO_SCALING;
- sensor->scaler_sink = *comp;
- } else if (ssd == sensor->binner) {
- sensor->binning_horizontal = 1;
- sensor->binning_vertical = 1;
- }
- }
+ crop = v4l2_subdev_state_get_crop(sd_state, CCS_PAD_SINK,
+ CCS_STREAM_PIXEL);
+ comp->width = crop->width;
+ comp->height = crop->height;
fallthrough;
case V4L2_SEL_TGT_COMPOSE:
- *crops[CCS_PAD_SRC] = *comp;
- fmt = v4l2_subdev_state_get_format(sd_state, CCS_PAD_SRC);
+ crop = v4l2_subdev_state_get_crop(sd_state, CCS_PAD_SRC,
+ CCS_STREAM_PIXEL);
+ *crop = *comp;
+ fmt = v4l2_subdev_state_get_format(sd_state, CCS_PAD_SRC,
+ CCS_STREAM_PIXEL);
fmt->width = comp->width;
fmt->height = comp->height;
- if (which == V4L2_SUBDEV_FORMAT_ACTIVE && ssd == sensor->src)
- sensor->src_src = *crops[CCS_PAD_SRC];
break;
default:
WARN_ON_ONCE(1);
}
}
-static const struct ccs_csi_data_format
-*ccs_validate_csi_data_format(struct ccs_sensor *sensor, u32 code)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(ccs_csi_data_formats); i++) {
- if (sensor->mbus_frame_fmts & (1 << i) &&
- ccs_csi_data_formats[i].code == code)
- return &ccs_csi_data_formats[i];
- }
-
- return sensor->csi_format;
-}
-
static int ccs_set_format_source(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
@@ -2138,7 +2200,7 @@ static int ccs_set_format_source(struct v4l2_subdev *subdev,
unsigned int i;
int rval;
- rval = __ccs_get_format(subdev, sd_state, fmt);
+ rval = ccs_get_format(subdev, sd_state, fmt);
if (rval)
return rval;
@@ -2185,22 +2247,19 @@ static int ccs_set_format(struct v4l2_subdev *subdev,
{
struct ccs_sensor *sensor = to_ccs_sensor(subdev);
struct ccs_subdev *ssd = to_ccs_subdev(subdev);
- struct v4l2_rect *crops[CCS_PADS];
-
- mutex_lock(&sensor->mutex);
+ struct v4l2_rect *crop;
if (fmt->pad == ssd->source_pad) {
int rval;
rval = ccs_set_format_source(subdev, sd_state, fmt);
- mutex_unlock(&sensor->mutex);
-
return rval;
}
/* Sink pad. Width and height are changeable here. */
- fmt->format.code = __ccs_get_mbus_code(subdev, fmt->pad);
+ fmt->format.code = ccs_get_mbus_code(subdev, fmt->pad);
+
fmt->format.width &= ~1;
fmt->format.height &= ~1;
fmt->format.field = V4L2_FIELD_NONE;
@@ -2214,15 +2273,14 @@ static int ccs_set_format(struct v4l2_subdev *subdev,
CCS_LIM(sensor, MIN_Y_OUTPUT_SIZE),
CCS_LIM(sensor, MAX_Y_OUTPUT_SIZE));
- ccs_get_crop_compose(subdev, sd_state, crops, NULL);
-
- crops[ssd->sink_pad]->left = 0;
- crops[ssd->sink_pad]->top = 0;
- crops[ssd->sink_pad]->width = fmt->format.width;
- crops[ssd->sink_pad]->height = fmt->format.height;
- ccs_propagate(subdev, sd_state, fmt->which, V4L2_SEL_TGT_CROP);
+ crop = v4l2_subdev_state_get_crop(sd_state, ssd->sink_pad,
+ CCS_STREAM_PIXEL);
- mutex_unlock(&sensor->mutex);
+ crop->left = 0;
+ crop->top = 0;
+ crop->width = fmt->format.width;
+ crop->height = fmt->format.height;
+ ccs_propagate(subdev, sd_state, V4L2_SEL_TGT_CROP);
return 0;
}
@@ -2274,26 +2332,23 @@ static int scaling_goodness(struct v4l2_subdev *subdev, int w, int ask_w,
static void ccs_set_compose_binner(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel,
- struct v4l2_rect **crops,
- struct v4l2_rect *comp)
+ const struct v4l2_rect *sink_crop)
{
struct ccs_sensor *sensor = to_ccs_sensor(subdev);
unsigned int i;
unsigned int binh = 1, binv = 1;
- int best = scaling_goodness(
- subdev,
- crops[CCS_PAD_SINK]->width, sel->r.width,
- crops[CCS_PAD_SINK]->height, sel->r.height, sel->flags);
+ int best = scaling_goodness(subdev, sink_crop->width, sel->r.width,
+ sink_crop->height, sel->r.height,
+ sel->flags);
for (i = 0; i < sensor->nbinning_subtypes; i++) {
- int this = scaling_goodness(
- subdev,
- crops[CCS_PAD_SINK]->width
- / sensor->binning_subtypes[i].horizontal,
- sel->r.width,
- crops[CCS_PAD_SINK]->height
- / sensor->binning_subtypes[i].vertical,
- sel->r.height, sel->flags);
+ int this = scaling_goodness(subdev,
+ sink_crop->width
+ / sensor->binning_subtypes[i].horizontal,
+ sel->r.width,
+ sink_crop->height
+ / sensor->binning_subtypes[i].vertical,
+ sel->r.height, sel->flags);
if (this > best) {
binh = sensor->binning_subtypes[i].horizontal;
@@ -2301,13 +2356,9 @@ static void ccs_set_compose_binner(struct v4l2_subdev *subdev,
best = this;
}
}
- if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
- sensor->binning_vertical = binv;
- sensor->binning_horizontal = binh;
- }
- sel->r.width = (crops[CCS_PAD_SINK]->width / binh) & ~1;
- sel->r.height = (crops[CCS_PAD_SINK]->height / binv) & ~1;
+ sel->r.width = (sink_crop->width / binh) & ~1;
+ sel->r.height = (sink_crop->height / binv) & ~1;
}
/*
@@ -2322,8 +2373,7 @@ static void ccs_set_compose_binner(struct v4l2_subdev *subdev,
static void ccs_set_compose_scaler(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel,
- struct v4l2_rect **crops,
- struct v4l2_rect *comp)
+ const struct v4l2_rect *sink_crop)
{
struct i2c_client *client = v4l2_get_subdevdata(subdev);
struct ccs_sensor *sensor = to_ccs_sensor(subdev);
@@ -2335,18 +2385,14 @@ static void ccs_set_compose_scaler(struct v4l2_subdev *subdev,
unsigned int i;
int best = INT_MIN;
- sel->r.width = min_t(unsigned int, sel->r.width,
- crops[CCS_PAD_SINK]->width);
- sel->r.height = min_t(unsigned int, sel->r.height,
- crops[CCS_PAD_SINK]->height);
+ sel->r.width = min_t(unsigned int, sel->r.width, sink_crop->width);
+ sel->r.height = min_t(unsigned int, sel->r.height, sink_crop->height);
- a = crops[CCS_PAD_SINK]->width
- * CCS_LIM(sensor, SCALER_N_MIN) / sel->r.width;
- b = crops[CCS_PAD_SINK]->height
- * CCS_LIM(sensor, SCALER_N_MIN) / sel->r.height;
- max_m = crops[CCS_PAD_SINK]->width
+ a = sink_crop->width * CCS_LIM(sensor, SCALER_N_MIN) / sel->r.width;
+ b = sink_crop->height * CCS_LIM(sensor, SCALER_N_MIN) / sel->r.height;
+ max_m = sink_crop->width
* CCS_LIM(sensor, SCALER_N_MIN)
- / CCS_LIM(sensor, MIN_X_OUTPUT_SIZE);
+ / (CCS_LIM(sensor, MIN_X_OUTPUT_SIZE) ?: 1);
a = clamp(a, CCS_LIM(sensor, SCALER_M_MIN),
CCS_LIM(sensor, SCALER_M_MAX));
@@ -2376,14 +2422,12 @@ static void ccs_set_compose_scaler(struct v4l2_subdev *subdev,
}
for (i = 0; i < ntry; i++) {
- int this = scaling_goodness(
- subdev,
- crops[CCS_PAD_SINK]->width
- / try[i] * CCS_LIM(sensor, SCALER_N_MIN),
- sel->r.width,
- crops[CCS_PAD_SINK]->height,
- sel->r.height,
- sel->flags);
+ int this = scaling_goodness(subdev,
+ sink_crop->width
+ / try[i]
+ * CCS_LIM(sensor, SCALER_N_MIN),
+ sel->r.width, sink_crop->height,
+ sel->r.height, sel->flags);
dev_dbg(&client->dev, "trying factor %u (%u)\n", try[i], i);
@@ -2398,12 +2442,10 @@ static void ccs_set_compose_scaler(struct v4l2_subdev *subdev,
continue;
this = scaling_goodness(
- subdev, crops[CCS_PAD_SINK]->width
- / try[i]
+ subdev, sink_crop->width / try[i]
* CCS_LIM(sensor, SCALER_N_MIN),
sel->r.width,
- crops[CCS_PAD_SINK]->height
- / try[i]
+ sink_crop->height / try[i]
* CCS_LIM(sensor, SCALER_N_MIN),
sel->r.height,
sel->flags);
@@ -2415,23 +2457,13 @@ static void ccs_set_compose_scaler(struct v4l2_subdev *subdev,
}
}
- sel->r.width =
- (crops[CCS_PAD_SINK]->width
- / scale_m
- * CCS_LIM(sensor, SCALER_N_MIN)) & ~1;
+ sel->r.width = (sink_crop->width / scale_m
+ * CCS_LIM(sensor, SCALER_N_MIN)) & ~1;
if (mode == SMIAPP_SCALING_MODE_BOTH)
- sel->r.height =
- (crops[CCS_PAD_SINK]->height
- / scale_m
- * CCS_LIM(sensor, SCALER_N_MIN))
- & ~1;
+ sel->r.height = (sink_crop->height / scale_m
+ * CCS_LIM(sensor, SCALER_N_MIN)) & ~1;
else
- sel->r.height = crops[CCS_PAD_SINK]->height;
-
- if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
- sensor->scale_m = scale_m;
- sensor->scaling_mode = mode;
- }
+ sel->r.height = sink_crop->height;
}
/* We're only called on source pads. This function sets scaling. */
static int ccs_set_compose(struct v4l2_subdev *subdev,
@@ -2440,20 +2472,24 @@ static int ccs_set_compose(struct v4l2_subdev *subdev,
{
struct ccs_sensor *sensor = to_ccs_sensor(subdev);
struct ccs_subdev *ssd = to_ccs_subdev(subdev);
- struct v4l2_rect *comp, *crops[CCS_PADS];
+ const struct v4l2_rect *sink_crop;
+ struct v4l2_rect *comp;
- ccs_get_crop_compose(subdev, sd_state, crops, &comp);
+ sink_crop = v4l2_subdev_state_get_crop(sd_state, CCS_PAD_SINK,
+ CCS_STREAM_PIXEL);
+ comp = v4l2_subdev_state_get_compose(sd_state, ssd->sink_pad,
+ CCS_STREAM_PIXEL);
sel->r.top = 0;
sel->r.left = 0;
if (ssd == sensor->binner)
- ccs_set_compose_binner(subdev, sd_state, sel, crops, comp);
+ ccs_set_compose_binner(subdev, sd_state, sel, sink_crop);
else
- ccs_set_compose_scaler(subdev, sd_state, sel, crops, comp);
+ ccs_set_compose_scaler(subdev, sd_state, sel, sink_crop);
*comp = sel->r;
- ccs_propagate(subdev, sd_state, sel->which, V4L2_SEL_TGT_COMPOSE);
+ ccs_propagate(subdev, sd_state, V4L2_SEL_TGT_COMPOSE);
if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE)
return ccs_pll_blanking_update(sensor);
@@ -2505,9 +2541,13 @@ static int ccs_set_crop(struct v4l2_subdev *subdev,
{
struct ccs_sensor *sensor = to_ccs_sensor(subdev);
struct ccs_subdev *ssd = to_ccs_subdev(subdev);
- struct v4l2_rect src_size = { 0 }, *crops[CCS_PADS], *comp;
+ struct v4l2_rect src_size = { 0 }, *crop;
+ const struct v4l2_rect *comp;
- ccs_get_crop_compose(subdev, sd_state, crops, &comp);
+ crop = v4l2_subdev_state_get_crop(sd_state, sel->pad,
+ CCS_STREAM_PIXEL);
+ comp = v4l2_subdev_state_get_compose(sd_state, ssd->sink_pad,
+ CCS_STREAM_PIXEL);
if (sel->pad == ssd->sink_pad) {
struct v4l2_mbus_framefmt *mfmt =
@@ -2530,13 +2570,10 @@ static int ccs_set_crop(struct v4l2_subdev *subdev,
sel->r.left = min_t(int, sel->r.left, src_size.width - sel->r.width);
sel->r.top = min_t(int, sel->r.top, src_size.height - sel->r.height);
- *crops[sel->pad] = sel->r;
+ *crop = sel->r;
if (ssd != sensor->pixel_array && sel->pad == CCS_PAD_SINK)
- ccs_propagate(subdev, sd_state, sel->which, V4L2_SEL_TGT_CROP);
- else if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE &&
- ssd == sensor->pixel_array)
- sensor->pa_src = sel->r;
+ ccs_propagate(subdev, sd_state, V4L2_SEL_TGT_CROP);
return 0;
}
@@ -2555,14 +2592,17 @@ static int ccs_get_selection(struct v4l2_subdev *subdev,
{
struct ccs_sensor *sensor = to_ccs_sensor(subdev);
struct ccs_subdev *ssd = to_ccs_subdev(subdev);
- struct v4l2_rect *comp, *crops[CCS_PADS];
+ const struct v4l2_rect *crop, *comp;
int ret;
ret = ccs_sel_supported(subdev, sel);
if (ret)
return ret;
- ccs_get_crop_compose(subdev, sd_state, crops, &comp);
+ crop = v4l2_subdev_state_get_crop(sd_state, sel->pad,
+ CCS_STREAM_PIXEL);
+ comp = v4l2_subdev_state_get_compose(sd_state, ssd->sink_pad,
+ CCS_STREAM_PIXEL);
switch (sel->target) {
case V4L2_SEL_TGT_CROP_BOUNDS:
@@ -2582,7 +2622,7 @@ static int ccs_get_selection(struct v4l2_subdev *subdev,
break;
case V4L2_SEL_TGT_CROP:
case V4L2_SEL_TGT_COMPOSE_BOUNDS:
- sel->r = *crops[sel->pad];
+ sel->r = *crop;
break;
case V4L2_SEL_TGT_COMPOSE:
sel->r = *comp;
@@ -2603,8 +2643,6 @@ static int ccs_set_selection(struct v4l2_subdev *subdev,
if (ret)
return ret;
- mutex_lock(&sensor->mutex);
-
sel->r.left = max(0, sel->r.left & ~1);
sel->r.top = max(0, sel->r.top & ~1);
sel->r.width = CCS_ALIGN_DIM(sel->r.width, sel->flags);
@@ -2626,10 +2664,44 @@ static int ccs_set_selection(struct v4l2_subdev *subdev,
ret = -EINVAL;
}
- mutex_unlock(&sensor->mutex);
return ret;
}
+static int ccs_get_frame_desc(struct v4l2_subdev *subdev, unsigned int pad,
+ struct v4l2_mbus_frame_desc *desc)
+{
+ struct ccs_sensor *sensor = to_ccs_sensor(subdev);
+ struct v4l2_mbus_frame_desc_entry *entry = desc->entry;
+ struct v4l2_subdev_state *sd_state;
+
+ switch (sensor->hwcfg.csi_signalling_mode) {
+ case CCS_CSI_SIGNALING_MODE_CSI_2_DPHY:
+ case CCS_CSI_SIGNALING_MODE_CSI_2_CPHY:
+ desc->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2;
+ break;
+ default:
+ /* FIXME: CCP2 support */
+ return -EINVAL;
+ }
+
+ sd_state = v4l2_subdev_lock_and_get_active_state(subdev);
+ if (!sd_state)
+ return -EINVAL;
+
+ entry->pixelcode = sensor->csi_format->code;
+ entry->stream = CCS_STREAM_PIXEL;
+ entry->bus.csi2.dt =
+ sensor->csi_format->width == sensor->csi_format->compressed ?
+ ccs_mipi_csi2_data_type(sensor->csi_format->width) :
+ CCS_DEFAULT_COMPRESSED_DT;
+ entry++;
+ desc->num_entries++;
+
+ v4l2_subdev_unlock_state(sd_state);
+
+ return 0;
+}
+
static int ccs_get_skip_frames(struct v4l2_subdev *subdev, u32 *frames)
{
struct ccs_sensor *sensor = to_ccs_sensor(subdev);
@@ -2940,6 +3012,8 @@ static void ccs_cleanup(struct ccs_sensor *sensor)
ccs_free_controls(sensor);
}
+static const struct v4l2_subdev_internal_ops ccs_internal_ops;
+
static int ccs_init_subdev(struct ccs_sensor *sensor,
struct ccs_subdev *ssd, const char *name,
unsigned short num_pads, u32 function,
@@ -2952,11 +3026,14 @@ static int ccs_init_subdev(struct ccs_sensor *sensor,
if (!ssd)
return 0;
- if (ssd != sensor->src)
+ if (ssd != sensor->src) {
v4l2_subdev_init(&ssd->sd, &ccs_ops);
+ ssd->sd.internal_ops = &ccs_internal_ops;
+ }
ssd->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
ssd->sd.entity.function = function;
+ ssd->sd.state_lock = &sensor->mutex;
ssd->sensor = sensor;
ssd->npads = num_pads;
@@ -3002,9 +3079,8 @@ static int ccs_init_state(struct v4l2_subdev *sd,
v4l2_subdev_state_get_format(sd_state, pad);
struct v4l2_rect *crop =
v4l2_subdev_state_get_crop(sd_state, pad);
- bool is_active = !sd->active_state || sd->active_state == sd_state;
- mutex_lock(&sensor->mutex);
+ guard(mutex)(&sensor->mutex);
ccs_get_native_size(ssd, crop);
@@ -3013,28 +3089,21 @@ static int ccs_init_state(struct v4l2_subdev *sd,
fmt->code = sensor->internal_csi_format->code;
fmt->field = V4L2_FIELD_NONE;
- if (ssd == sensor->pixel_array) {
- if (is_active)
- sensor->pa_src = *crop;
-
- mutex_unlock(&sensor->mutex);
+ if (ssd == sensor->pixel_array)
return 0;
- }
fmt = v4l2_subdev_state_get_format(sd_state, CCS_PAD_SRC);
fmt->code = ssd == sensor->src ?
sensor->csi_format->code : sensor->internal_csi_format->code;
fmt->field = V4L2_FIELD_NONE;
- ccs_propagate(sd, sd_state, is_active, V4L2_SEL_TGT_CROP);
-
- mutex_unlock(&sensor->mutex);
+ ccs_propagate(sd, sd_state, V4L2_SEL_TGT_CROP);
return 0;
}
static const struct v4l2_subdev_video_ops ccs_video_ops = {
- .s_stream = ccs_set_stream,
+ .s_stream = v4l2_subdev_s_stream_helper,
.pre_streamon = ccs_pre_streamon,
.post_streamoff = ccs_post_streamoff,
};
@@ -3045,6 +3114,9 @@ static const struct v4l2_subdev_pad_ops ccs_pad_ops = {
.set_fmt = ccs_set_format,
.get_selection = ccs_get_selection,
.set_selection = ccs_set_selection,
+ .enable_streams = ccs_enable_streams,
+ .disable_streams = ccs_disable_streams,
+ .get_frame_desc = ccs_get_frame_desc,
};
static const struct v4l2_subdev_sensor_ops ccs_sensor_ops = {
@@ -3062,6 +3134,10 @@ static const struct media_entity_operations ccs_entity_ops = {
.link_validate = v4l2_subdev_link_validate,
};
+static const struct v4l2_subdev_internal_ops ccs_internal_ops = {
+ .init_state = ccs_init_state,
+};
+
static const struct v4l2_subdev_internal_ops ccs_internal_src_ops = {
.init_state = ccs_init_state,
.registered = ccs_registered,
@@ -3074,11 +3150,9 @@ static const struct v4l2_subdev_internal_ops ccs_internal_src_ops = {
static int ccs_get_hwconfig(struct ccs_sensor *sensor, struct device *dev)
{
- struct ccs_hwconfig *hwcfg = &sensor->hwcfg;
struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = V4L2_MBUS_UNKNOWN };
- struct fwnode_handle *ep;
- struct fwnode_handle *fwnode = dev_fwnode(dev);
- unsigned int i;
+ struct fwnode_handle *fwnode = dev_fwnode(dev), *ep;
+ struct ccs_hwconfig *hwcfg = &sensor->hwcfg;
int rval;
ep = fwnode_graph_get_endpoint_by_id(fwnode, 0, 0,
@@ -3105,9 +3179,9 @@ static int ccs_get_hwconfig(struct ccs_sensor *sensor, struct device *dev)
break;
case V4L2_MBUS_CSI1:
case V4L2_MBUS_CCP2:
- hwcfg->csi_signalling_mode = (bus_cfg.bus.mipi_csi1.strobe) ?
- SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_STROBE :
- SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_CLOCK;
+ hwcfg->csi_signalling_mode = bus_cfg.bus.mipi_csi1.strobe ?
+ SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_STROBE :
+ SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_CLOCK;
hwcfg->lanes = 1;
break;
default:
@@ -3116,11 +3190,7 @@ static int ccs_get_hwconfig(struct ccs_sensor *sensor, struct device *dev)
goto out_err;
}
- rval = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",
- &hwcfg->ext_clk);
-
- dev_dbg(dev, "clk %u, mode %u\n", hwcfg->ext_clk,
- hwcfg->csi_signalling_mode);
+ dev_dbg(dev, "signalling mode: %u\n", hwcfg->csi_signalling_mode);
if (!bus_cfg.nr_of_link_frequencies) {
dev_warn(dev, "no link frequencies defined\n");
@@ -3128,23 +3198,22 @@ static int ccs_get_hwconfig(struct ccs_sensor *sensor, struct device *dev)
goto out_err;
}
- hwcfg->op_sys_clock = devm_kcalloc(
- dev, bus_cfg.nr_of_link_frequencies + 1 /* guardian */,
- sizeof(*hwcfg->op_sys_clock), GFP_KERNEL);
+ hwcfg->op_sys_clock =
+ devm_kcalloc(dev,
+ bus_cfg.nr_of_link_frequencies + 1 /* guardian */,
+ sizeof(*hwcfg->op_sys_clock), GFP_KERNEL);
if (!hwcfg->op_sys_clock) {
rval = -ENOMEM;
goto out_err;
}
- for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++) {
+ for (unsigned int i = 0; i < bus_cfg.nr_of_link_frequencies; i++) {
hwcfg->op_sys_clock[i] = bus_cfg.link_frequencies[i];
dev_dbg(dev, "freq %u: %lld\n", i, hwcfg->op_sys_clock[i]);
}
- v4l2_fwnode_endpoint_free(&bus_cfg);
- fwnode_handle_put(ep);
-
- return 0;
+ fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",
+ &hwcfg->ext_clk);
out_err:
v4l2_fwnode_endpoint_free(&bus_cfg);
@@ -3232,47 +3301,30 @@ static int ccs_probe(struct i2c_client *client)
return rval;
}
- sensor->ext_clk = devm_clk_get(&client->dev, NULL);
- if (PTR_ERR(sensor->ext_clk) == -ENOENT) {
- dev_info(&client->dev, "no clock defined, continuing...\n");
- sensor->ext_clk = NULL;
- } else if (IS_ERR(sensor->ext_clk)) {
- dev_err(&client->dev, "could not get clock (%pe)\n",
- sensor->ext_clk);
- return -EPROBE_DEFER;
- }
+ sensor->ext_clk = devm_v4l2_sensor_clk_get(&client->dev, NULL);
+ if (IS_ERR(sensor->ext_clk))
+ return dev_err_probe(&client->dev, PTR_ERR(sensor->ext_clk),
+ "could not get clock\n");
- if (sensor->ext_clk) {
- if (sensor->hwcfg.ext_clk) {
- unsigned long rate;
+ if (sensor->hwcfg.ext_clk) {
+ unsigned long rate;
- rval = clk_set_rate(sensor->ext_clk,
- sensor->hwcfg.ext_clk);
- if (rval < 0) {
- dev_err(&client->dev,
- "unable to set clock freq to %u\n",
- sensor->hwcfg.ext_clk);
- return rval;
- }
-
- rate = clk_get_rate(sensor->ext_clk);
- if (rate != sensor->hwcfg.ext_clk) {
- dev_err(&client->dev,
- "can't set clock freq, asked for %u but got %lu\n",
- sensor->hwcfg.ext_clk, rate);
- return -EINVAL;
- }
- } else {
- sensor->hwcfg.ext_clk = clk_get_rate(sensor->ext_clk);
- dev_dbg(&client->dev, "obtained clock freq %u\n",
- sensor->hwcfg.ext_clk);
+ rval = clk_set_rate(sensor->ext_clk, sensor->hwcfg.ext_clk);
+ if (rval < 0)
+ return dev_err_probe(&client->dev, rval,
+ "unable to set clock freq to %u\n",
+ sensor->hwcfg.ext_clk);
+
+ rate = clk_get_rate(sensor->ext_clk);
+ if (rate != sensor->hwcfg.ext_clk) {
+ return dev_err_probe(&client->dev, -EINVAL,
+ "can't set clock freq, asked for %u but got %lu\n",
+ sensor->hwcfg.ext_clk, rate);
}
- } else if (sensor->hwcfg.ext_clk) {
- dev_dbg(&client->dev, "assuming clock freq %u\n",
- sensor->hwcfg.ext_clk);
} else {
- dev_err(&client->dev, "unable to obtain clock freq\n");
- return -EINVAL;
+ sensor->hwcfg.ext_clk = clk_get_rate(sensor->ext_clk);
+ dev_dbg(&client->dev, "obtained clock freq %u\n",
+ sensor->hwcfg.ext_clk);
}
if (!sensor->hwcfg.ext_clk) {
@@ -3384,8 +3436,6 @@ static int ccs_probe(struct i2c_client *client)
sensor->binning_subtypes[i].vertical);
}
}
- sensor->binning_horizontal = 1;
- sensor->binning_vertical = 1;
if (device_create_file(&client->dev, &dev_attr_ident) != 0) {
dev_err(&client->dev, "sysfs ident entry creation failed\n");
@@ -3422,10 +3472,22 @@ static int ccs_probe(struct i2c_client *client)
sensor->pixel_array = &sensor->ssds[sensor->ssds_used];
sensor->ssds_used++;
- sensor->scale_m = CCS_LIM(sensor, SCALER_N_MIN);
-
/* prepare PLL configuration input values */
- sensor->pll.bus_type = CCS_PLL_BUS_TYPE_CSI2_DPHY;
+ switch (sensor->hwcfg.csi_signalling_mode) {
+ case CCS_CSI_SIGNALING_MODE_CSI_2_CPHY:
+ sensor->pll.bus_type = CCS_PLL_BUS_TYPE_CSI2_CPHY;
+ break;
+ case CCS_CSI_SIGNALING_MODE_CSI_2_DPHY:
+ case SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_CLOCK:
+ case SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_STROBE:
+ sensor->pll.bus_type = CCS_PLL_BUS_TYPE_CSI2_DPHY;
+ break;
+ default:
+ dev_err(&client->dev, "unsupported signalling mode %u\n",
+ sensor->hwcfg.csi_signalling_mode);
+ rval = -EINVAL;
+ goto out_cleanup;
+ }
sensor->pll.csi2.lanes = sensor->hwcfg.lanes;
if (CCS_LIM(sensor, CLOCK_CALCULATION) &
CCS_CLOCK_CALCULATION_LANE_SPEED) {
@@ -3517,15 +3579,13 @@ static int ccs_probe(struct i2c_client *client)
goto out_cleanup;
}
- mutex_lock(&sensor->mutex);
- rval = ccs_pll_blanking_update(sensor);
- mutex_unlock(&sensor->mutex);
+ scoped_guard(mutex, &sensor->mutex)
+ rval = ccs_pll_blanking_update(sensor);
if (rval) {
dev_err(&client->dev, "update mode failed\n");
goto out_cleanup;
}
- sensor->streaming = false;
sensor->dev_init_done = true;
sensor->handler_setup_needed = true;