<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body dir="auto">I took pictures with both sensors and posted them to<a href="https://github.com/waydroid/waydroid/issues/519"> https://github.com/waydroid/waydroid/issues/519</a> without any issues. However, someone else following along on the ticket also noted that their pictures were unusable dark as well with the same configuration I used.<div><br></div><div>I was using Megi’s driver and kernel, the same one I submitted for upstreaming. I wonder if there may be some variability in the hardware?<br><div><br></div><div>On Nov 28, 2022, at 11:53 AM, Jacopo Mondi <jacopo@jmondi.org> wrote:<br><div dir="ltr"><blockquote type="cite"><br></blockquote></div><blockquote type="cite"><div dir="ltr"><span>Hi Nicholas,</span><br><span>   a few notes on testing the analogue gain response on your driver</span><br><span></span><br><span>On Sun, Nov 06, 2022 at 11:11:30AM -0600, Nicholas Roth wrote:</span><br><span>[snip]</span><br><span></span><br><blockquote type="cite"><span>+    case V4L2_CID_ANALOGUE_GAIN:</span><br></blockquote><blockquote type="cite"><span>+        ret = ov8858_write_reg(ov8858->client,</span><br></blockquote><blockquote type="cite"><span>+                    OV8858_REG_GAIN_H,</span><br></blockquote><blockquote type="cite"><span>+                    OV8858_REG_VALUE_08BIT,</span><br></blockquote><blockquote type="cite"><span>+                    (ctrl->val >> OV8858_GAIN_H_SHIFT) &</span><br></blockquote><blockquote type="cite"><span>+                    OV8858_GAIN_H_MASK);</span><br></blockquote><blockquote type="cite"><span>+        ret |= ov8858_write_reg(ov8858->client,</span><br></blockquote><blockquote type="cite"><span>+                    OV8858_REG_GAIN_L,</span><br></blockquote><blockquote type="cite"><span>+                    OV8858_REG_VALUE_08BIT,</span><br></blockquote><blockquote type="cite"><span>+                    ctrl->val & OV8858_GAIN_L_MASK);</span><br></blockquote><blockquote type="cite"><span>+        break;</span><br></blockquote><span></span><br><span>I've started this investigation because I have very dark images when</span><br><span>running from this sensor.</span><br><span></span><br><span>I'm running libcamera with the camera sensor helper that you have</span><br><span>submitted, which implements a linear gain model with (m0=1,c1=16)</span><br><span>which seems to match the registers documentation when running in "real</span><br><span>mode". Quoting:</span><br><span></span><br><span>0x3503[2]=0, gain[7:0] is real gain format, where low 4 bits are</span><br><span>fraction bits, for example, 0x10 is 1x gain, 0x28 is 2.5x gain</span><br><span></span><br><span>Unfortunately I don't get usable images with this configuration.</span><br><span>My testing procedure has been</span><br><span></span><br><span>1) Disconnect DelayedControls from RkISP1 pipeline handler to avoid it</span><br><span>   writing controls to the sensor</span><br><span></span><br><span>--- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp</span><br><span>+++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp</span><br><span>@@ -393,9 +393,9 @@ void RkISP1CameraData::paramFilled(unsigned int frame)</span><br><span> }</span><br><span></span><br><span> void RkISP1CameraData::setSensorControls([[maybe_unused]] unsigned int frame,</span><br><span>-                                        const ControlList &sensorControls)</span><br><span>+                                        [[maybe_unused]] const ControlList &sensorControls)</span><br><span> {</span><br><span>-       delayedCtrls_->push(sensorControls);</span><br><span>+       //delayedCtrls_->push(sensorControls);</span><br><span> }</span><br><span></span><br><span>2) Disable Agc and Awb algorithm by removing the entries in</span><br><span>   /usr/share/libcamera/rkisp1/uncalibrated.yaml</span><br><span></span><br><span>3) Run a preview window and observe how it changes by issuing controls</span><br><span>   with v4l2-ctl:</span><br><span></span><br><span>   1) set a reasonable exposure value</span><br><span>      $ v4l2-ctl -c 0x00980911=2047 -d /dev/v4l-subdev4</span><br><span></span><br><span>   2) pump up digital gain a bit</span><br><span>     $ v4l2-ctl -c 0x009f0905=800 -d /dev/v4l-subdev4</span><br><span></span><br><span>   3) Start sending analogue gain controls to see how the image</span><br><span>      response changes</span><br><span></span><br><span>     $ v4l2-ctl -c 0x009e0903=128 -d /dev/v4l-subdev4</span><br><span>     $ v4l2-ctl -c 0x009e0903=512 -d /dev/v4l-subdev4</span><br><span>     $ v4l2-ctl -c 0x009e0903=1024 -d /dev/v4l-subdev4</span><br><span>     $ v4l2-ctl -c 0x009e0903=2048 -d /dev/v4l-subdev4</span><br><span></span><br><span>My observations are that</span><br><span>- The highest gain I can apply is obtained with 8191, which</span><br><span>  corresponds to 0x1fff which matches to the register description</span><br><span>  of:</span><br><span></span><br><span>  0x3508 LONG_GAIN_HIGH = gain[12:6]</span><br><span>  0x3509 LONG_GAIN_LOW = gain[7:0]</span><br><span></span><br><span>  This seems to indicate the gain register is actually 13-bits long</span><br><span>  and not</span><br><span></span><br><span>- The gain is not linear:</span><br><span></span><br><span>  127 is more brilliant than 128</span><br><span>  2047 is more brilliant than 2048</span><br><span></span><br><span>I'm afraid the description we get for the register is not accurate and</span><br><span>doesn't tell exactly how the gain value is assembled in the 0x3508 and</span><br><span>0x3509 register ?</span><br><span></span><br><span>Are you experiencing anything similar ?</span><br><span></span><br><span>Thanks</span><br><span>  j</span><br><span></span><br><blockquote type="cite"><span>+    case V4L2_CID_DIGITAL_GAIN:</span><br></blockquote><blockquote type="cite"><span>+        ret = ov8858_write_reg(ov8858->client,</span><br></blockquote><blockquote type="cite"><span>+                    OV8858_REG_DGAIN_H,</span><br></blockquote><blockquote type="cite"><span>+                    OV8858_REG_VALUE_08BIT,</span><br></blockquote><blockquote type="cite"><span>+                    (ctrl->val >> OV8858_DGAIN_H_SHIFT) &</span><br></blockquote><blockquote type="cite"><span>+                    OV8858_DGAIN_H_MASK);</span><br></blockquote><blockquote type="cite"><span>+        ret |= ov8858_write_reg(ov8858->client,</span><br></blockquote><blockquote type="cite"><span>+                    OV8858_REG_DGAIN_L,</span><br></blockquote><blockquote type="cite"><span>+                    OV8858_REG_VALUE_08BIT,</span><br></blockquote><blockquote type="cite"><span>+                    ctrl->val & OV8858_DGAIN_L_MASK);</span><br></blockquote><blockquote type="cite"><span>+        break;</span><br></blockquote><blockquote type="cite"><span>+    case V4L2_CID_VBLANK:</span><br></blockquote><blockquote type="cite"><span>+        ret = ov8858_write_reg(ov8858->client,</span><br></blockquote><blockquote type="cite"><span>+                    OV8858_REG_VTS,</span><br></blockquote><blockquote type="cite"><span>+                    OV8858_REG_VALUE_16BIT,</span><br></blockquote><blockquote type="cite"><span>+                    ctrl->val + ov8858->cur_mode->height);</span><br></blockquote><blockquote type="cite"><span>+        break;</span><br></blockquote><blockquote type="cite"><span>+    case V4L2_CID_TEST_PATTERN:</span><br></blockquote><blockquote type="cite"><span>+        ret = ov8858_enable_test_pattern(ov8858, ctrl->val);</span><br></blockquote><blockquote type="cite"><span>+        break;</span><br></blockquote><blockquote type="cite"><span>+    default:</span><br></blockquote><blockquote type="cite"><span>+        dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n",</span><br></blockquote><blockquote type="cite"><span>+             __func__, ctrl->id, ctrl->val);</span><br></blockquote><blockquote type="cite"><span>+        break;</span><br></blockquote><blockquote type="cite"><span>+    }</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    pm_runtime_put(&client->dev);</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    return ret;</span><br></blockquote><blockquote type="cite"><span>+}</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+static const struct v4l2_ctrl_ops ov8858_ctrl_ops = {</span><br></blockquote><blockquote type="cite"><span>+    .s_ctrl = ov8858_set_ctrl,</span><br></blockquote><blockquote type="cite"><span>+};</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+static int ov8858_initialize_controls(struct ov8858 *ov8858)</span><br></blockquote><blockquote type="cite"><span>+{</span><br></blockquote><blockquote type="cite"><span>+    const struct ov8858_mode *mode;</span><br></blockquote><blockquote type="cite"><span>+    struct v4l2_ctrl_handler *handler;</span><br></blockquote><blockquote type="cite"><span>+    struct v4l2_ctrl *ctrl;</span><br></blockquote><blockquote type="cite"><span>+    s64 exposure_max, vblank_def;</span><br></blockquote><blockquote type="cite"><span>+    u32 h_blank;</span><br></blockquote><blockquote type="cite"><span>+    int ret;</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    handler = &ov8858->ctrl_handler;</span><br></blockquote><blockquote type="cite"><span>+    mode = ov8858->cur_mode;</span><br></blockquote><blockquote type="cite"><span>+    ret = v4l2_ctrl_handler_init(handler, 8);</span><br></blockquote><blockquote type="cite"><span>+    if (ret)</span><br></blockquote><blockquote type="cite"><span>+        return ret;</span><br></blockquote><blockquote type="cite"><span>+    handler->lock = &ov8858->mutex;</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ,</span><br></blockquote><blockquote type="cite"><span>+                      0, 0, link_freq_menu_items);</span><br></blockquote><blockquote type="cite"><span>+    if (ctrl)</span><br></blockquote><blockquote type="cite"><span>+        ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE,</span><br></blockquote><blockquote type="cite"><span>+              0, ov8858->pixel_rate, 1, ov8858->pixel_rate);</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    h_blank = mode->hts_def - mode->width;</span><br></blockquote><blockquote type="cite"><span>+    ov8858->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,</span><br></blockquote><blockquote type="cite"><span>+                h_blank, h_blank, 1, h_blank);</span><br></blockquote><blockquote type="cite"><span>+    if (ov8858->hblank)</span><br></blockquote><blockquote type="cite"><span>+        ov8858->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    vblank_def = mode->vts_def - mode->height;</span><br></blockquote><blockquote type="cite"><span>+    ov8858->vblank = v4l2_ctrl_new_std(handler, &ov8858_ctrl_ops,</span><br></blockquote><blockquote type="cite"><span>+                V4L2_CID_VBLANK, vblank_def,</span><br></blockquote><blockquote type="cite"><span>+                OV8858_VTS_MAX - mode->height,</span><br></blockquote><blockquote type="cite"><span>+                1, vblank_def);</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    exposure_max = mode->vts_def - 4;</span><br></blockquote><blockquote type="cite"><span>+    ov8858->exposure = v4l2_ctrl_new_std(handler, &ov8858_ctrl_ops,</span><br></blockquote><blockquote type="cite"><span>+                V4L2_CID_EXPOSURE, OV8858_EXPOSURE_MIN,</span><br></blockquote><blockquote type="cite"><span>+                exposure_max, OV8858_EXPOSURE_STEP,</span><br></blockquote><blockquote type="cite"><span>+                mode->exp_def);</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    ov8858->anal_gain = v4l2_ctrl_new_std(handler, &ov8858_ctrl_ops,</span><br></blockquote><blockquote type="cite"><span>+                V4L2_CID_ANALOGUE_GAIN, OV8858_GAIN_MIN,</span><br></blockquote><blockquote type="cite"><span>+                OV8858_GAIN_MAX, OV8858_GAIN_STEP,</span><br></blockquote><blockquote type="cite"><span>+                OV8858_GAIN_DEFAULT);</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    ov8858->digi_gain = v4l2_ctrl_new_std(handler, &ov8858_ctrl_ops,</span><br></blockquote><blockquote type="cite"><span>+                V4L2_CID_DIGITAL_GAIN, OV8858_DGAIN_MIN,</span><br></blockquote><blockquote type="cite"><span>+                OV8858_DGAIN_MAX, OV8858_DGAIN_STEP,</span><br></blockquote><blockquote type="cite"><span>+                OV8858_DGAIN_DEFAULT);</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    ov8858->test_pattern = v4l2_ctrl_new_std_menu_items(handler,</span><br></blockquote><blockquote type="cite"><span>+                &ov8858_ctrl_ops, V4L2_CID_TEST_PATTERN,</span><br></blockquote><blockquote type="cite"><span>+                ARRAY_SIZE(ov8858_test_pattern_menu) - 1,</span><br></blockquote><blockquote type="cite"><span>+                0, 0, ov8858_test_pattern_menu);</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    if (handler->error) {</span><br></blockquote><blockquote type="cite"><span>+        ret = handler->error;</span><br></blockquote><blockquote type="cite"><span>+        dev_err(&ov8858->client->dev,</span><br></blockquote><blockquote type="cite"><span>+            "Failed to init controls(%d)\n", ret);</span><br></blockquote><blockquote type="cite"><span>+        goto err_free_handler;</span><br></blockquote><blockquote type="cite"><span>+    }</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    ov8858->subdev.ctrl_handler = handler;</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    return 0;</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+err_free_handler:</span><br></blockquote><blockquote type="cite"><span>+    v4l2_ctrl_handler_free(handler);</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    return ret;</span><br></blockquote><blockquote type="cite"><span>+}</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+static int ov8858_check_sensor_id(struct ov8858 *ov8858,</span><br></blockquote><blockquote type="cite"><span>+                   struct i2c_client *client)</span><br></blockquote><blockquote type="cite"><span>+{</span><br></blockquote><blockquote type="cite"><span>+    struct device *dev = &ov8858->client->dev;</span><br></blockquote><blockquote type="cite"><span>+    u32 id = 0;</span><br></blockquote><blockquote type="cite"><span>+    int ret;</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    ret = ov8858_read_reg(client, OV8858_REG_CHIP_ID,</span><br></blockquote><blockquote type="cite"><span>+                  OV8858_REG_VALUE_24BIT, &id);</span><br></blockquote><blockquote type="cite"><span>+    if (id != CHIP_ID) {</span><br></blockquote><blockquote type="cite"><span>+        dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret);</span><br></blockquote><blockquote type="cite"><span>+        return ret;</span><br></blockquote><blockquote type="cite"><span>+    }</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    ret = ov8858_read_reg(client, OV8858_CHIP_REVISION_REG,</span><br></blockquote><blockquote type="cite"><span>+                  OV8858_REG_VALUE_08BIT, &id);</span><br></blockquote><blockquote type="cite"><span>+    if (ret) {</span><br></blockquote><blockquote type="cite"><span>+        dev_err(dev, "Read chip revision register error\n");</span><br></blockquote><blockquote type="cite"><span>+        return ret;</span><br></blockquote><blockquote type="cite"><span>+    }</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    dev_info(dev, "Detected OV%06x sensor, REVISION 0x%x\n", CHIP_ID, id);</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    if (id == OV8858_R2A) {</span><br></blockquote><blockquote type="cite"><span>+        if (4 == ov8858->lane_num) {</span><br></blockquote><blockquote type="cite"><span>+            ov8858_global_regs = ov8858_global_regs_r2a_4lane;</span><br></blockquote><blockquote type="cite"><span>+        } else {</span><br></blockquote><blockquote type="cite"><span>+            ov8858_global_regs = ov8858_global_regs_r2a_2lane;</span><br></blockquote><blockquote type="cite"><span>+        }</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+        ov8858->is_r2a = true;</span><br></blockquote><blockquote type="cite"><span>+    } else {</span><br></blockquote><blockquote type="cite"><span>+        ov8858_global_regs = ov8858_global_regs_r1a;</span><br></blockquote><blockquote type="cite"><span>+        ov8858->is_r2a = false;</span><br></blockquote><blockquote type="cite"><span>+        dev_warn(dev, "R1A may not work well current!\n");</span><br></blockquote><blockquote type="cite"><span>+    }</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    return 0;</span><br></blockquote><blockquote type="cite"><span>+}</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+static int ov8858_configure_regulators(struct ov8858 *ov8858)</span><br></blockquote><blockquote type="cite"><span>+{</span><br></blockquote><blockquote type="cite"><span>+    unsigned int i;</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    for (i = 0; i < OV8858_NUM_SUPPLIES; i++)</span><br></blockquote><blockquote type="cite"><span>+        ov8858->supplies[i].supply = ov8858_supply_names[i];</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    return devm_regulator_bulk_get(&ov8858->client->dev,</span><br></blockquote><blockquote type="cite"><span>+                       OV8858_NUM_SUPPLIES,</span><br></blockquote><blockquote type="cite"><span>+                       ov8858->supplies);</span><br></blockquote><blockquote type="cite"><span>+}</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+static int ov8858_parse_of(struct ov8858 *ov8858)</span><br></blockquote><blockquote type="cite"><span>+{</span><br></blockquote><blockquote type="cite"><span>+    struct device *dev = &ov8858->client->dev;</span><br></blockquote><blockquote type="cite"><span>+    struct device_node *endpoint;</span><br></blockquote><blockquote type="cite"><span>+    struct fwnode_handle *fwnode;</span><br></blockquote><blockquote type="cite"><span>+    int rval;</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);</span><br></blockquote><blockquote type="cite"><span>+    if (!endpoint) {</span><br></blockquote><blockquote type="cite"><span>+        dev_err(dev, "Failed to get endpoint\n");</span><br></blockquote><blockquote type="cite"><span>+        return -EINVAL;</span><br></blockquote><blockquote type="cite"><span>+    }</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    fwnode = of_fwnode_handle(endpoint);</span><br></blockquote><blockquote type="cite"><span>+    rval = fwnode_property_read_u32_array(fwnode, "data-lanes", NULL, 0);</span><br></blockquote><blockquote type="cite"><span>+    if (rval <= 0) {</span><br></blockquote><blockquote type="cite"><span>+        dev_warn(dev, " Get mipi lane num failed!\n");</span><br></blockquote><blockquote type="cite"><span>+        return -1;</span><br></blockquote><blockquote type="cite"><span>+    }</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    ov8858->lane_num = rval;</span><br></blockquote><blockquote type="cite"><span>+    if (4 == ov8858->lane_num) {</span><br></blockquote><blockquote type="cite"><span>+        ov8858->cur_mode = &supported_modes_4lane[0];</span><br></blockquote><blockquote type="cite"><span>+        supported_modes = supported_modes_4lane;</span><br></blockquote><blockquote type="cite"><span>+        ov8858->cfg_num = ARRAY_SIZE(supported_modes_4lane);</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+        /* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */</span><br></blockquote><blockquote type="cite"><span>+        ov8858->pixel_rate = MIPI_FREQ * 2U * ov8858->lane_num / 10U;</span><br></blockquote><blockquote type="cite"><span>+        dev_info(dev, "lane_num(%d)  pixel_rate(%u)\n",</span><br></blockquote><blockquote type="cite"><span>+                 ov8858->lane_num, ov8858->pixel_rate);</span><br></blockquote><blockquote type="cite"><span>+    } else {</span><br></blockquote><blockquote type="cite"><span>+        ov8858->cur_mode = &supported_modes_2lane[0];</span><br></blockquote><blockquote type="cite"><span>+        supported_modes = supported_modes_2lane;</span><br></blockquote><blockquote type="cite"><span>+        ov8858->cfg_num = ARRAY_SIZE(supported_modes_2lane);</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+        /*pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */</span><br></blockquote><blockquote type="cite"><span>+        ov8858->pixel_rate = MIPI_FREQ * 2U * (ov8858->lane_num) / 10U;</span><br></blockquote><blockquote type="cite"><span>+        dev_info(dev, "lane_num(%d)  pixel_rate(%u)\n",</span><br></blockquote><blockquote type="cite"><span>+                 ov8858->lane_num, ov8858->pixel_rate);</span><br></blockquote><blockquote type="cite"><span>+    }</span><br></blockquote><blockquote type="cite"><span>+    return 0;</span><br></blockquote><blockquote type="cite"><span>+}</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+static int ov8858_probe(struct i2c_client *client,</span><br></blockquote><blockquote type="cite"><span>+             const struct i2c_device_id *id)</span><br></blockquote><blockquote type="cite"><span>+{</span><br></blockquote><blockquote type="cite"><span>+    struct device *dev = &client->dev;</span><br></blockquote><blockquote type="cite"><span>+    struct device_node *node = dev->of_node;</span><br></blockquote><blockquote type="cite"><span>+    struct ov8858 *ov8858;</span><br></blockquote><blockquote type="cite"><span>+    struct v4l2_subdev *sd;</span><br></blockquote><blockquote type="cite"><span>+    int ret;</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    ov8858 = devm_kzalloc(dev, sizeof(*ov8858), GFP_KERNEL);</span><br></blockquote><blockquote type="cite"><span>+    if (!ov8858)</span><br></blockquote><blockquote type="cite"><span>+        return -ENOMEM;</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    ov8858->client = client;</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    ov8858->xvclk = devm_clk_get(dev, "xvclk");</span><br></blockquote><blockquote type="cite"><span>+    if (IS_ERR(ov8858->xvclk))</span><br></blockquote><blockquote type="cite"><span>+        return dev_err_probe(dev, PTR_ERR(ov8858->xvclk),</span><br></blockquote><blockquote type="cite"><span>+                     "Failed to get xvclk\n");</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    ov8858->reset_gpio = devm_gpiod_get_optional(dev, "reset",</span><br></blockquote><blockquote type="cite"><span>+                             GPIOD_OUT_HIGH);</span><br></blockquote><blockquote type="cite"><span>+    if (IS_ERR(ov8858->reset_gpio))</span><br></blockquote><blockquote type="cite"><span>+        return dev_err_probe(dev, PTR_ERR(ov8858->reset_gpio),</span><br></blockquote><blockquote type="cite"><span>+                     "Failed to get reset gpio\n");</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    ov8858->pwdn_gpio = devm_gpiod_get_optional(dev, "powerdown",</span><br></blockquote><blockquote type="cite"><span>+                            GPIOD_OUT_HIGH);</span><br></blockquote><blockquote type="cite"><span>+    if (IS_ERR(ov8858->pwdn_gpio))</span><br></blockquote><blockquote type="cite"><span>+        return dev_err_probe(dev, PTR_ERR(ov8858->pwdn_gpio),</span><br></blockquote><blockquote type="cite"><span>+                     "Failed to get powerdown gpio\n");</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    ret = ov8858_configure_regulators(ov8858);</span><br></blockquote><blockquote type="cite"><span>+    if (ret)</span><br></blockquote><blockquote type="cite"><span>+        return dev_err_probe(dev, ret,</span><br></blockquote><blockquote type="cite"><span>+                     "Failed to get power regulators\n");</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    ret = ov8858_parse_of(ov8858);</span><br></blockquote><blockquote type="cite"><span>+    if (ret != 0)</span><br></blockquote><blockquote type="cite"><span>+        return -EINVAL;</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    mutex_init(&ov8858->mutex);</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    sd = &ov8858->subdev;</span><br></blockquote><blockquote type="cite"><span>+    v4l2_i2c_subdev_init(sd, client, &ov8858_subdev_ops);</span><br></blockquote><blockquote type="cite"><span>+    ret = ov8858_initialize_controls(ov8858);</span><br></blockquote><blockquote type="cite"><span>+    if (ret)</span><br></blockquote><blockquote type="cite"><span>+        goto err_destroy_mutex;</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    ret = __ov8858_power_on(ov8858);</span><br></blockquote><blockquote type="cite"><span>+    if (ret)</span><br></blockquote><blockquote type="cite"><span>+        goto err_free_handler;</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    ret = ov8858_check_sensor_id(ov8858, client);</span><br></blockquote><blockquote type="cite"><span>+    if (ret)</span><br></blockquote><blockquote type="cite"><span>+        goto err_power_off;</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    sd->internal_ops = &ov8858_internal_ops;</span><br></blockquote><blockquote type="cite"><span>+    sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;</span><br></blockquote><blockquote type="cite"><span>+    ov8858->pad.flags = MEDIA_PAD_FL_SOURCE;</span><br></blockquote><blockquote type="cite"><span>+    sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;</span><br></blockquote><blockquote type="cite"><span>+    ret = media_entity_pads_init(&sd->entity, 1, &ov8858->pad);</span><br></blockquote><blockquote type="cite"><span>+    if (ret < 0)</span><br></blockquote><blockquote type="cite"><span>+        goto err_power_off;</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    ret = v4l2_async_register_subdev_sensor(sd);</span><br></blockquote><blockquote type="cite"><span>+    if (ret) {</span><br></blockquote><blockquote type="cite"><span>+        dev_err(dev, "v4l2 async register subdev failed\n");</span><br></blockquote><blockquote type="cite"><span>+        goto err_clean_entity;</span><br></blockquote><blockquote type="cite"><span>+    }</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    pm_runtime_set_active(dev);</span><br></blockquote><blockquote type="cite"><span>+    pm_runtime_enable(dev);</span><br></blockquote><blockquote type="cite"><span>+    pm_runtime_idle(dev);</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    return 0;</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+err_clean_entity:</span><br></blockquote><blockquote type="cite"><span>+    media_entity_cleanup(&sd->entity);</span><br></blockquote><blockquote type="cite"><span>+err_power_off:</span><br></blockquote><blockquote type="cite"><span>+    __ov8858_power_off(ov8858);</span><br></blockquote><blockquote type="cite"><span>+err_free_handler:</span><br></blockquote><blockquote type="cite"><span>+    v4l2_ctrl_handler_free(&ov8858->ctrl_handler);</span><br></blockquote><blockquote type="cite"><span>+err_destroy_mutex:</span><br></blockquote><blockquote type="cite"><span>+    mutex_destroy(&ov8858->mutex);</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    return ret;</span><br></blockquote><blockquote type="cite"><span>+}</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+static int ov8858_remove(struct i2c_client *client)</span><br></blockquote><blockquote type="cite"><span>+{</span><br></blockquote><blockquote type="cite"><span>+    struct v4l2_subdev *sd = i2c_get_clientdata(client);</span><br></blockquote><blockquote type="cite"><span>+    struct ov8858 *ov8858 = to_ov8858(sd);</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    v4l2_async_unregister_subdev(sd);</span><br></blockquote><blockquote type="cite"><span>+#ifdef CONFIG_MEDIA_CONTROLLER</span><br></blockquote><blockquote type="cite"><span>+    media_entity_cleanup(&sd->entity);</span><br></blockquote><blockquote type="cite"><span>+#endif</span><br></blockquote><blockquote type="cite"><span>+    v4l2_ctrl_handler_free(&ov8858->ctrl_handler);</span><br></blockquote><blockquote type="cite"><span>+    mutex_destroy(&ov8858->mutex);</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    pm_runtime_disable(&client->dev);</span><br></blockquote><blockquote type="cite"><span>+    if (!pm_runtime_status_suspended(&client->dev))</span><br></blockquote><blockquote type="cite"><span>+        __ov8858_power_off(ov8858);</span><br></blockquote><blockquote type="cite"><span>+    pm_runtime_set_suspended(&client->dev);</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+    return 0;</span><br></blockquote><blockquote type="cite"><span>+}</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+#if IS_ENABLED(CONFIG_OF)</span><br></blockquote><blockquote type="cite"><span>+static const struct of_device_id ov8858_of_match[] = {</span><br></blockquote><blockquote type="cite"><span>+    { .compatible = "ovti,ov8858" },</span><br></blockquote><blockquote type="cite"><span>+    {},</span><br></blockquote><blockquote type="cite"><span>+};</span><br></blockquote><blockquote type="cite"><span>+MODULE_DEVICE_TABLE(of, ov8858_of_match);</span><br></blockquote><blockquote type="cite"><span>+#endif</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+static const struct i2c_device_id ov8858_match_id[] = {</span><br></blockquote><blockquote type="cite"><span>+    { "ovti,ov8858", 0 },</span><br></blockquote><blockquote type="cite"><span>+    { },</span><br></blockquote><blockquote type="cite"><span>+};</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+static struct i2c_driver ov8858_i2c_driver = {</span><br></blockquote><blockquote type="cite"><span>+    .driver = {</span><br></blockquote><blockquote type="cite"><span>+        .name = OV8858_NAME,</span><br></blockquote><blockquote type="cite"><span>+        .pm = &ov8858_pm_ops,</span><br></blockquote><blockquote type="cite"><span>+        .of_match_table = of_match_ptr(ov8858_of_match),</span><br></blockquote><blockquote type="cite"><span>+    },</span><br></blockquote><blockquote type="cite"><span>+    .probe        = &ov8858_probe,</span><br></blockquote><blockquote type="cite"><span>+    .remove        = &ov8858_remove,</span><br></blockquote><blockquote type="cite"><span>+    .id_table    = ov8858_match_id,</span><br></blockquote><blockquote type="cite"><span>+};</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+module_i2c_driver(ov8858_i2c_driver);</span><br></blockquote><blockquote type="cite"><span>+</span><br></blockquote><blockquote type="cite"><span>+MODULE_DESCRIPTION("OmniVision ov8858 sensor driver");</span><br></blockquote><blockquote type="cite"><span>+MODULE_LICENSE("GPL v2");</span><br></blockquote><blockquote type="cite"><span>--</span><br></blockquote><blockquote type="cite"><span>2.34.1</span><br></blockquote><blockquote type="cite"><span></span><br></blockquote></div></blockquote></div></div></body></html>