<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>