[media] smiapp: Register async subdev
[deliverable/linux.git] / drivers / media / i2c / smiapp / smiapp-core.c
index b10aaeda2bb4599576849a8a5b786dcc2542aa49..2c65ff3ed769945bfcad09de1c732ed0c5f300e5 100644 (file)
  * 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 St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
  */
 
 #include <linux/clk.h>
@@ -205,12 +199,12 @@ static int smiapp_pll_configure(struct smiapp_sensor *sensor)
        int rval;
 
        rval = smiapp_write(
-               sensor, SMIAPP_REG_U16_VT_PIX_CLK_DIV, pll->vt_pix_clk_div);
+               sensor, SMIAPP_REG_U16_VT_PIX_CLK_DIV, pll->vt.pix_clk_div);
        if (rval < 0)
                return rval;
 
        rval = smiapp_write(
-               sensor, SMIAPP_REG_U16_VT_SYS_CLK_DIV, pll->vt_sys_clk_div);
+               sensor, SMIAPP_REG_U16_VT_SYS_CLK_DIV, pll->vt.sys_clk_div);
        if (rval < 0)
                return rval;
 
@@ -227,20 +221,21 @@ static int smiapp_pll_configure(struct smiapp_sensor *sensor)
        /* Lane op clock ratio does not apply here. */
        rval = smiapp_write(
                sensor, SMIAPP_REG_U32_REQUESTED_LINK_BIT_RATE_MBPS,
-               DIV_ROUND_UP(pll->op_sys_clk_freq_hz, 1000000 / 256 / 256));
+               DIV_ROUND_UP(pll->op.sys_clk_freq_hz, 1000000 / 256 / 256));
        if (rval < 0 || sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0)
                return rval;
 
        rval = smiapp_write(
-               sensor, SMIAPP_REG_U16_OP_PIX_CLK_DIV, pll->op_pix_clk_div);
+               sensor, SMIAPP_REG_U16_OP_PIX_CLK_DIV, pll->op.pix_clk_div);
        if (rval < 0)
                return rval;
 
        return smiapp_write(
-               sensor, SMIAPP_REG_U16_OP_SYS_CLK_DIV, pll->op_sys_clk_div);
+               sensor, SMIAPP_REG_U16_OP_SYS_CLK_DIV, pll->op.sys_clk_div);
 }
 
-static int smiapp_pll_update(struct smiapp_sensor *sensor)
+static int smiapp_pll_try(struct smiapp_sensor *sensor,
+                         struct smiapp_pll *pll)
 {
        struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
        struct smiapp_pll_limits lim = {
@@ -274,19 +269,15 @@ static int smiapp_pll_update(struct smiapp_sensor *sensor)
                .min_line_length_pck_bin = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN],
                .min_line_length_pck = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK],
        };
+
+       return smiapp_pll_calculate(&client->dev, &lim, pll);
+}
+
+static int smiapp_pll_update(struct smiapp_sensor *sensor)
+{
        struct smiapp_pll *pll = &sensor->pll;
        int rval;
 
-       if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) {
-               /*
-                * Fill in operational clock divisors limits from the
-                * video timing ones. On profile 0 sensors the
-                * requirements regarding them are essentially the
-                * same as on VT ones.
-                */
-               lim.op = lim.vt;
-       }
-
        pll->binning_horizontal = sensor->binning_horizontal;
        pll->binning_vertical = sensor->binning_vertical;
        pll->link_freq =
@@ -294,12 +285,12 @@ static int smiapp_pll_update(struct smiapp_sensor *sensor)
        pll->scale_m = sensor->scale_m;
        pll->bits_per_pixel = sensor->csi_format->compressed;
 
-       rval = smiapp_pll_calculate(&client->dev, &lim, pll);
+       rval = smiapp_pll_try(sensor, pll);
        if (rval < 0)
                return rval;
 
        __v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate_parray,
-                                pll->vt_pix_clk_freq_hz);
+                                pll->pixel_rate_pixel_array);
        __v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate_csi, pll->pixel_rate_csi);
 
        return 0;
@@ -333,22 +324,22 @@ static void __smiapp_update_exposure_limits(struct smiapp_sensor *sensor)
  *    orders must be defined.
  */
 static const struct smiapp_csi_data_format smiapp_csi_data_formats[] = {
-       { V4L2_MBUS_FMT_SGRBG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GRBG, },
-       { V4L2_MBUS_FMT_SRGGB12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_RGGB, },
-       { V4L2_MBUS_FMT_SBGGR12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_BGGR, },
-       { V4L2_MBUS_FMT_SGBRG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GBRG, },
-       { V4L2_MBUS_FMT_SGRBG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GRBG, },
-       { V4L2_MBUS_FMT_SRGGB10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_RGGB, },
-       { V4L2_MBUS_FMT_SBGGR10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_BGGR, },
-       { V4L2_MBUS_FMT_SGBRG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GBRG, },
-       { V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GRBG, },
-       { V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_RGGB, },
-       { V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_BGGR, },
-       { V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GBRG, },
-       { V4L2_MBUS_FMT_SGRBG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GRBG, },
-       { V4L2_MBUS_FMT_SRGGB8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_RGGB, },
-       { V4L2_MBUS_FMT_SBGGR8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_BGGR, },
-       { V4L2_MBUS_FMT_SGBRG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GBRG, },
+       { MEDIA_BUS_FMT_SGRBG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GRBG, },
+       { MEDIA_BUS_FMT_SRGGB12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_RGGB, },
+       { MEDIA_BUS_FMT_SBGGR12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_BGGR, },
+       { MEDIA_BUS_FMT_SGBRG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GBRG, },
+       { MEDIA_BUS_FMT_SGRBG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GRBG, },
+       { MEDIA_BUS_FMT_SRGGB10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_RGGB, },
+       { MEDIA_BUS_FMT_SBGGR10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_BGGR, },
+       { MEDIA_BUS_FMT_SGBRG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GBRG, },
+       { MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GRBG, },
+       { MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_RGGB, },
+       { MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_BGGR, },
+       { MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GBRG, },
+       { MEDIA_BUS_FMT_SGRBG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GRBG, },
+       { MEDIA_BUS_FMT_SRGGB8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_RGGB, },
+       { MEDIA_BUS_FMT_SBGGR8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_BGGR, },
+       { MEDIA_BUS_FMT_SGBRG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GBRG, },
 };
 
 const char *pixel_order_str[] = { "GRBG", "RGGB", "BGGR", "GBRG" };
@@ -526,6 +517,8 @@ static const struct v4l2_ctrl_ops smiapp_ctrl_ops = {
 static int smiapp_init_controls(struct smiapp_sensor *sensor)
 {
        struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+       unsigned long *valid_link_freqs = &sensor->valid_link_freqs[
+               sensor->csi_format->compressed - SMIAPP_COMPRESSED_BASE];
        unsigned int max, i;
        int rval;
 
@@ -608,8 +601,8 @@ static int smiapp_init_controls(struct smiapp_sensor *sensor)
 
        sensor->link_freq = v4l2_ctrl_new_int_menu(
                &sensor->src->ctrl_handler, &smiapp_ctrl_ops,
-               V4L2_CID_LINK_FREQ, max, 0,
-               sensor->platform_data->op_sys_clock);
+               V4L2_CID_LINK_FREQ, __fls(*valid_link_freqs),
+               __ffs(*valid_link_freqs), sensor->platform_data->op_sys_clock);
 
        sensor->pixel_rate_csi = v4l2_ctrl_new_std(
                &sensor->src->ctrl_handler, &smiapp_ctrl_ops,
@@ -745,6 +738,7 @@ static int smiapp_get_limits_binning(struct smiapp_sensor *sensor)
 static int smiapp_get_mbus_formats(struct smiapp_sensor *sensor)
 {
        struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+       struct smiapp_pll *pll = &sensor->pll;
        unsigned int type, n;
        unsigned int i, pixel_order;
        int rval;
@@ -808,14 +802,57 @@ static int smiapp_get_mbus_formats(struct smiapp_sensor *sensor)
                        dev_dbg(&client->dev, "jolly good! %d\n", j);
 
                        sensor->default_mbus_frame_fmts |= 1 << j;
-                       if (!sensor->csi_format
-                           || f->width > sensor->csi_format->width
-                           || (f->width == sensor->csi_format->width
-                               && f->compressed
-                               > sensor->csi_format->compressed)) {
-                               sensor->csi_format = f;
-                               sensor->internal_csi_format = f;
-                       }
+               }
+       }
+
+       /* 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;
+
+       for (i = 0; i < ARRAY_SIZE(smiapp_csi_data_formats); i++) {
+               const struct smiapp_csi_data_format *f =
+                       &smiapp_csi_data_formats[i];
+               unsigned long *valid_link_freqs =
+                       &sensor->valid_link_freqs[
+                               f->compressed - SMIAPP_COMPRESSED_BASE];
+               unsigned int j;
+
+               BUG_ON(f->compressed < SMIAPP_COMPRESSED_BASE);
+               BUG_ON(f->compressed > SMIAPP_COMPRESSED_MAX);
+
+               if (!(sensor->default_mbus_frame_fmts & 1 << i))
+                       continue;
+
+               pll->bits_per_pixel = f->compressed;
+
+               for (j = 0; sensor->platform_data->op_sys_clock[j]; j++) {
+                       pll->link_freq = sensor->platform_data->op_sys_clock[j];
+
+                       rval = smiapp_pll_try(sensor, pll);
+                       dev_dbg(&client->dev, "link freq %u Hz, bpp %u %s\n",
+                               pll->link_freq, pll->bits_per_pixel,
+                               rval ? "not ok" : "ok");
+                       if (rval)
+                               continue;
+
+                       set_bit(j, valid_link_freqs);
+               }
+
+               if (!*valid_link_freqs) {
+                       dev_info(&client->dev,
+                                "no valid link frequencies for %u bpp\n",
+                                f->compressed);
+                       sensor->default_mbus_frame_fmts &= ~BIT(i);
+                       continue;
+               }
+
+               if (!sensor->csi_format
+                   || f->width > sensor->csi_format->width
+                   || (f->width == sensor->csi_format->width
+                       && f->compressed > sensor->csi_format->compressed)) {
+                       sensor->csi_format = f;
+                       sensor->internal_csi_format = f;
                }
        }
 
@@ -904,7 +941,7 @@ static int smiapp_update_mode(struct smiapp_sensor *sensor)
        dev_dbg(&client->dev, "hblank\t\t%d\n", sensor->hblank->val);
 
        dev_dbg(&client->dev, "real timeperframe\t100/%d\n",
-               sensor->pll.vt_pix_clk_freq_hz /
+               sensor->pll.pixel_rate_pixel_array /
                ((sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width
                  + sensor->hblank->val) *
                 (sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height
@@ -1687,51 +1724,77 @@ static const struct smiapp_csi_data_format
        return csi_format;
 }
 
-static int smiapp_set_format(struct v4l2_subdev *subdev,
-                            struct v4l2_subdev_fh *fh,
-                            struct v4l2_subdev_format *fmt)
+static int smiapp_set_format_source(struct v4l2_subdev *subdev,
+                                   struct v4l2_subdev_fh *fh,
+                                   struct v4l2_subdev_format *fmt)
 {
        struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
-       struct smiapp_subdev *ssd = to_smiapp_subdev(subdev);
-       struct v4l2_rect *crops[SMIAPP_PADS];
+       const struct smiapp_csi_data_format *csi_format,
+               *old_csi_format = sensor->csi_format;
+       unsigned long *valid_link_freqs;
+       u32 code = fmt->format.code;
+       unsigned int i;
+       int rval;
 
-       mutex_lock(&sensor->mutex);
+       rval = __smiapp_get_format(subdev, fh, fmt);
+       if (rval)
+               return rval;
 
        /*
         * Media bus code is changeable on src subdev's source pad. On
         * other source pads we just get format here.
         */
-       if (fmt->pad == ssd->source_pad) {
-               u32 code = fmt->format.code;
-               int rval = __smiapp_get_format(subdev, fh, fmt);
-               bool range_changed = false;
-               unsigned int i;
+       if (subdev != &sensor->src->sd)
+               return 0;
 
-               if (!rval && subdev == &sensor->src->sd) {
-                       const struct smiapp_csi_data_format *csi_format =
-                               smiapp_validate_csi_data_format(sensor, code);
+       csi_format = smiapp_validate_csi_data_format(sensor, code);
 
-                       if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
-                               if (csi_format->width !=
-                                   sensor->csi_format->width)
-                                       range_changed = true;
+       fmt->format.code = csi_format->code;
 
-                               sensor->csi_format = csi_format;
-                       }
-
-                       fmt->format.code = csi_format->code;
-               }
+       if (fmt->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+               return 0;
 
-               mutex_unlock(&sensor->mutex);
-               if (rval || !range_changed)
-                       return rval;
+       sensor->csi_format = csi_format;
 
+       if (csi_format->width != old_csi_format->width)
                for (i = 0; i < ARRAY_SIZE(sensor->test_data); i++)
-                       v4l2_ctrl_modify_range(
-                               sensor->test_data[i],
-                               0, (1 << sensor->csi_format->width) - 1, 1, 0);
+                       __v4l2_ctrl_modify_range(
+                               sensor->test_data[i], 0,
+                               (1 << csi_format->width) - 1, 1, 0);
 
+       if (csi_format->compressed == old_csi_format->compressed)
                return 0;
+
+       valid_link_freqs = 
+               &sensor->valid_link_freqs[sensor->csi_format->compressed
+                                         - SMIAPP_COMPRESSED_BASE];
+
+       __v4l2_ctrl_modify_range(
+               sensor->link_freq, 0,
+               __fls(*valid_link_freqs), ~*valid_link_freqs,
+               __ffs(*valid_link_freqs));
+
+       return smiapp_pll_update(sensor);
+}
+
+static int smiapp_set_format(struct v4l2_subdev *subdev,
+                            struct v4l2_subdev_fh *fh,
+                            struct v4l2_subdev_format *fmt)
+{
+       struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+       struct smiapp_subdev *ssd = to_smiapp_subdev(subdev);
+       struct v4l2_rect *crops[SMIAPP_PADS];
+
+       mutex_lock(&sensor->mutex);
+
+       if (fmt->pad == ssd->source_pad) {
+               int rval;
+
+               rval = smiapp_set_format_source(subdev, fh, fmt);
+
+               mutex_unlock(&sensor->mutex);
+
+               return rval;
        }
 
        /* Sink pad. Width and height are changeable here. */
@@ -2023,6 +2086,11 @@ static int __smiapp_sel_supported(struct v4l2_subdev *subdev,
                    == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP)
                        return 0;
                return -EINVAL;
+       case V4L2_SEL_TGT_NATIVE_SIZE:
+               if (ssd == sensor->pixel_array
+                   && sel->pad == SMIAPP_PA_PAD_SRC)
+                       return 0;
+               return -EINVAL;
        case V4L2_SEL_TGT_COMPOSE:
        case V4L2_SEL_TGT_COMPOSE_BOUNDS:
                if (sel->pad == ssd->source_pad)
@@ -2121,7 +2189,9 @@ static int __smiapp_get_selection(struct v4l2_subdev *subdev,
 
        switch (sel->target) {
        case V4L2_SEL_TGT_CROP_BOUNDS:
+       case V4L2_SEL_TGT_NATIVE_SIZE:
                if (ssd == sensor->pixel_array) {
+                       sel->r.left = sel->r.top = 0;
                        sel->r.width =
                                sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1;
                        sel->r.height =
@@ -2388,8 +2458,6 @@ static int smiapp_identify_module(struct v4l2_subdev *subdev)
                minfo->name, minfo->manufacturer_id, minfo->model_id,
                minfo->revision_number_major);
 
-       strlcpy(subdev->name, sensor->minfo.name, sizeof(subdev->name));
-
        return 0;
 }
 
@@ -2482,12 +2550,6 @@ static int smiapp_registered(struct v4l2_subdev *subdev)
                goto out_power_off;
        }
 
-       rval = smiapp_get_mbus_formats(sensor);
-       if (rval) {
-               rval = -ENODEV;
-               goto out_power_off;
-       }
-
        if (sensor->limits[SMIAPP_LIMIT_BINNING_CAPABILITY]) {
                u32 val;
 
@@ -2569,6 +2631,22 @@ static int smiapp_registered(struct v4l2_subdev *subdev)
 
        sensor->scale_m = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN];
 
+       /* prepare PLL configuration input values */
+       pll->bus_type = SMIAPP_PLL_BUS_TYPE_CSI2;
+       pll->csi2.lanes = sensor->platform_data->lanes;
+       pll->ext_clk_freq_hz = sensor->platform_data->ext_clk;
+       pll->flags = smiapp_call_quirk(sensor, pll_flags);
+       pll->scale_n = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN];
+       /* Profile 0 sensors have no separate OP clock branch. */
+       if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0)
+               pll->flags |= SMIAPP_PLL_FLAG_NO_OP_CLOCKS;
+
+       rval = smiapp_get_mbus_formats(sensor);
+       if (rval) {
+               rval = -ENODEV;
+               goto out_nvm_release;
+       }
+
        for (i = 0; i < SMIAPP_SUBDEVS; i++) {
                struct {
                        struct smiapp_subdev *ssd;
@@ -2666,18 +2744,9 @@ static int smiapp_registered(struct v4l2_subdev *subdev)
        if (rval < 0)
                goto out_nvm_release;
 
-       /* prepare PLL configuration input values */
-       pll->bus_type = SMIAPP_PLL_BUS_TYPE_CSI2;
-       pll->csi2.lanes = sensor->platform_data->lanes;
-       pll->ext_clk_freq_hz = sensor->platform_data->ext_clk;
-       pll->flags = smiapp_call_quirk(sensor, pll_flags);
-
-       /* Profile 0 sensors have no separate OP clock branch. */
-       if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0)
-               pll->flags |= SMIAPP_PLL_FLAG_NO_OP_CLOCKS;
-       pll->scale_n = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN];
-
+       mutex_lock(&sensor->mutex);
        rval = smiapp_update_mode(sensor);
+       mutex_unlock(&sensor->mutex);
        if (rval) {
                dev_err(&client->dev, "update mode failed\n");
                goto out_nvm_release;
@@ -2873,8 +2942,21 @@ static int smiapp_probe(struct i2c_client *client,
        sensor->src->sensor = sensor;
 
        sensor->src->pads[0].flags = MEDIA_PAD_FL_SOURCE;
-       return media_entity_init(&sensor->src->sd.entity, 2,
+       rval = media_entity_init(&sensor->src->sd.entity, 2,
                                 sensor->src->pads, 0);
+       if (rval < 0)
+               return rval;
+
+       rval = v4l2_async_register_subdev(&sensor->src->sd);
+       if (rval < 0)
+               goto out_media_entity_cleanup;
+
+       return 0;
+
+out_media_entity_cleanup:
+       media_entity_cleanup(&sensor->src->sd.entity);
+
+       return rval;
 }
 
 static int smiapp_remove(struct i2c_client *client)
@@ -2883,6 +2965,8 @@ static int smiapp_remove(struct i2c_client *client)
        struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
        unsigned int i;
 
+       v4l2_async_unregister_subdev(subdev);
+
        if (sensor->power_count) {
                if (gpio_is_valid(sensor->platform_data->xshutdown))
                        gpio_set_value(sensor->platform_data->xshutdown, 0);
This page took 0.032487 seconds and 5 git commands to generate.