From patchwork Sun Mar 8 23:12:06 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pavel Machek X-Patchwork-Id: 210727 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.3 required=3.0 tests=HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_SANE_1 autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 621ABC10F27 for ; Sun, 8 Mar 2020 23:12:13 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 3A08D20684 for ; Sun, 8 Mar 2020 23:12:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726414AbgCHXMK (ORCPT ); Sun, 8 Mar 2020 19:12:10 -0400 Received: from jabberwock.ucw.cz ([46.255.230.98]:60504 "EHLO jabberwock.ucw.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726354AbgCHXMK (ORCPT ); Sun, 8 Mar 2020 19:12:10 -0400 Received: by jabberwock.ucw.cz (Postfix, from userid 1017) id C7A091C0316; Mon, 9 Mar 2020 00:12:07 +0100 (CET) Date: Mon, 9 Mar 2020 00:12:06 +0100 From: Pavel Machek To: Merlijn Wajer Cc: "Arthur D." , sakari.ailus@iki.fi, mchehab@kernel.org, linux-media@vger.kernel.org, kernel list Subject: [PATCH] et8ek8: Support for EXPOSURE_ABSOLUTE Message-ID: <20200308231206.GC31247@amd> References: <20200302115446.GA22299@duo.ucw.cz> <01c78e68-c83d-746b-972b-ce71952b9e14@wizzup.org> <20200305205216.GA4712@duo.ucw.cz> <20200307114526.GA21933@duo.ucw.cz> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: User-Agent: Mutt/1.5.23 (2014-03-12) Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org To do a good job taking photos, userland needs to be able to query/set exposure in absolute units. (As scene gets darker, exposure time should be increased to cca 1/100 second -- based on scene type -- then sensitivity should be stepped up to maximum reasonable value, only then time should be increased again). This patch provides neccessary support. Signed-off-by: Pavel Machek --- I have experimental version of sdlcam that can take photos, and this is the most important missing piece. diff --git a/drivers/media/i2c/et8ek8/et8ek8_driver.c b/drivers/media/i2c/et8ek8/et8ek8_driver.c index 256acf73d5ea..82d59691c788 100644 --- a/drivers/media/i2c/et8ek8/et8ek8_driver.c +++ b/drivers/media/i2c/et8ek8/et8ek8_driver.c @@ -50,6 +50,8 @@ struct et8ek8_sensor { struct v4l2_ctrl_handler ctrl_handler; struct v4l2_ctrl *exposure; + struct v4l2_ctrl *exposure_abs; + u32 cur_exposure; struct v4l2_ctrl *pixel_rate; struct et8ek8_reglist *current_reglist; @@ -546,6 +548,65 @@ static int et8ek8_reglist_import(struct i2c_client *client, return 0; } +typedef unsigned int fixpoint8; /* .8 fixed point format. */ + +/* + * Return time of one row in microseconds + * If the sensor is not set to any mode, return zero. + */ +fixpoint8 et8ek8_get_row_time(struct et8ek8_sensor *sensor) +{ + unsigned int clock; /* Pixel clock in Hz>>10 fixed point */ + fixpoint8 rt; /* Row time in .8 fixed point */ + + if (!sensor->current_reglist) + return 0; + + clock = sensor->current_reglist->mode.pixel_clock; + clock = (clock + (1 << 9)) >> 10; + rt = sensor->current_reglist->mode.width * (1000000 >> 2); + rt = (rt + (clock >> 1)) / clock; + + return rt; +} + +/* + * Convert exposure time `us' to rows. Modify `us' to make it to + * correspond to the actual exposure time. + */ +static int et8ek8_exposure_us_to_rows(struct et8ek8_sensor *sensor, u32 *us) +{ + unsigned int rows; /* Exposure value as written to HW (ie. rows) */ + fixpoint8 rt; /* Row time in .8 fixed point */ + + /* Assume that the maximum exposure time is at most ~8 s, + * and the maximum width (with blanking) ~8000 pixels. + * The formula here is in principle as simple as + * rows = exptime / 1e6 / width * pixel_clock + * but to get accurate results while coping with value ranges, + * have to do some fixed point math. + */ + + rt = et8ek8_get_row_time(sensor); + rows = ((*us << 8) + (rt >> 1)) / rt; + + if (rows > sensor->current_reglist->mode.max_exp) + rows = sensor->current_reglist->mode.max_exp; + + /* Set the exposure time to the rounded value */ + *us = (rt * rows + (1 << 7)) >> 8; + + return rows; +} + +/* + * Convert exposure time in rows to microseconds + */ +static int et8ek8_exposure_rows_to_us(struct et8ek8_sensor *sensor, int rows) +{ + return (et8ek8_get_row_time(sensor) * rows + (1 << 7)) >> 8; +} + /* Called to change the V4L2 gain control value. This function * rounds and clamps the given value and updates the V4L2 control value. * If power is on, also updates the sensor analog and digital gains. @@ -637,18 +698,22 @@ static int et8ek8_set_ctrl(struct v4l2_ctrl *ctrl) { struct et8ek8_sensor *sensor = container_of(ctrl->handler, struct et8ek8_sensor, ctrl_handler); + u32 val = ctrl->val; switch (ctrl->id) { case V4L2_CID_GAIN: return et8ek8_set_gain(sensor, ctrl->val); + case V4L2_CID_EXPOSURE_ABSOLUTE: + val = et8ek8_exposure_us_to_rows(sensor, &val); + /* Fall through */ + case V4L2_CID_EXPOSURE: { - struct i2c_client *client = - v4l2_get_subdevdata(&sensor->subdev); - + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); + sensor->cur_exposure = val; return et8ek8_i2c_write_reg(client, ET8EK8_REG_16BIT, 0x1243, - ctrl->val); + val); } case V4L2_CID_TEST_PATTERN: @@ -662,8 +727,28 @@ static int et8ek8_set_ctrl(struct v4l2_ctrl *ctrl) } } +static int et8ek8_get_ctrl(struct v4l2_ctrl *ctrl) +{ + struct et8ek8_sensor *sensor = + container_of(ctrl->handler, struct et8ek8_sensor, ctrl_handler); + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE_ABSOLUTE: + ctrl->val = et8ek8_exposure_rows_to_us(sensor, sensor->cur_exposure); + return 0; + + case V4L2_CID_EXPOSURE: + ctrl->val = sensor->cur_exposure; + return 0; + + default: + return -EINVAL; + } +} + static const struct v4l2_ctrl_ops et8ek8_ctrl_ops = { .s_ctrl = et8ek8_set_ctrl, + .g_volatile_ctrl = et8ek8_get_ctrl, }; static const char * const et8ek8_test_pattern_menu[] = { @@ -697,6 +782,13 @@ static int et8ek8_init_controls(struct et8ek8_sensor *sensor) v4l2_ctrl_new_std(&sensor->ctrl_handler, &et8ek8_ctrl_ops, V4L2_CID_EXPOSURE, min, max, min, max); + + min = et8ek8_exposure_rows_to_us(sensor, 1); + max = et8ek8_exposure_rows_to_us(sensor, max); + + sensor->exposure_abs = + v4l2_ctrl_new_std(&sensor->ctrl_handler, &et8ek8_ctrl_ops, + V4L2_CID_EXPOSURE_ABSOLUTE, min, max, min, max); } /* V4L2_CID_PIXEL_RATE */ @@ -738,8 +830,12 @@ static void et8ek8_update_controls(struct et8ek8_sensor *sensor) */ pixel_rate = ((mode->pixel_clock + (1 << S) - 1) >> S) + mode->width; pixel_rate = mode->window_width * (pixel_rate - 1) / mode->width; + __v4l2_ctrl_modify_range(sensor->exposure, 1, max, 1, max); + + min = et8ek8_exposure_rows_to_us(sensor, 1); + max = et8ek8_exposure_rows_to_us(sensor, max); + __v4l2_ctrl_modify_range(sensor->exposure_abs, min, max, min, max); - __v4l2_ctrl_modify_range(ctrl, min, max, min, max); __v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate, pixel_rate << S); }