/* * OMAP3 ISP library - OMAP3 ISP controls * * Copyright (C) 2010-2011 Ideas on board SPRL * * Contact: Laurent Pinchart * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or (at * your option) any later version. * * This library is distributed in the hope that it will be useful, but 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 Lesser General Public License * along with this library; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include "controls.h" #include "omap3isp.h" #include "omap3isp-priv.h" #include "subdev.h" /* ----------------------------------------------------------------------------- * Matrix algebra helper functions */ struct matrix { float coeff[3][3]; }; static void matrix_zero(struct matrix *m) { unsigned int i, j; for (i = 0; i < 3; ++i) { for (j = 0; j < 3; ++j) m->coeff[i][j] = 0.0; } } static void matrix_invert(struct matrix *m) { /* Invert the matrix using the transpose of the matrix of cofactors. The * Gauss-Jordan elimination would be faster in the general case, but we * know that the matrix is 3x3. */ const float eps = 1e-6; struct matrix out; unsigned int i, j; float det; out.coeff[0][0] = m->coeff[1][1] * m->coeff[2][2] - m->coeff[1][2] * m->coeff[2][1]; out.coeff[0][1] = m->coeff[0][2] * m->coeff[2][1] - m->coeff[0][1] * m->coeff[2][2]; out.coeff[0][2] = m->coeff[0][1] * m->coeff[1][2] - m->coeff[0][2] * m->coeff[1][1]; out.coeff[1][0] = m->coeff[1][2] * m->coeff[2][0] - m->coeff[1][0] * m->coeff[2][2]; out.coeff[1][1] = m->coeff[0][0] * m->coeff[2][2] - m->coeff[0][2] * m->coeff[2][0]; out.coeff[1][2] = m->coeff[0][2] * m->coeff[1][0] - m->coeff[0][0] * m->coeff[1][2]; out.coeff[2][0] = m->coeff[1][0] * m->coeff[2][1] - m->coeff[1][1] * m->coeff[2][0]; out.coeff[2][1] = m->coeff[0][1] * m->coeff[2][0] - m->coeff[0][0] * m->coeff[2][1]; out.coeff[2][2] = m->coeff[0][0] * m->coeff[1][1] - m->coeff[1][0] * m->coeff[0][1]; det = m->coeff[0][0] * out.coeff[0][0] + m->coeff[0][1] * out.coeff[1][0] + m->coeff[0][2] * out.coeff[2][0]; if (det < eps) return; det = 1/det; for (i = 0; i < 3; ++i) for (j = 0; j < 3; ++j) m->coeff[i][j] = out.coeff[i][j] * det; } static void matrix_multiply(struct matrix *a, const struct matrix *b) { struct matrix out; /* Compute a * b and return the result in a. */ out.coeff[0][0] = a->coeff[0][0] * b->coeff[0][0] + a->coeff[0][1] * b->coeff[1][0] + a->coeff[0][2] * b->coeff[2][0]; out.coeff[0][1] = a->coeff[0][0] * b->coeff[0][1] + a->coeff[0][1] * b->coeff[1][1] + a->coeff[0][2] * b->coeff[2][1]; out.coeff[0][2] = a->coeff[0][0] * b->coeff[0][2] + a->coeff[0][1] * b->coeff[1][2] + a->coeff[0][2] * b->coeff[2][2]; out.coeff[1][0] = a->coeff[1][0] * b->coeff[0][0] + a->coeff[1][1] * b->coeff[1][0] + a->coeff[1][2] * b->coeff[2][0]; out.coeff[1][1] = a->coeff[1][0] * b->coeff[0][1] + a->coeff[1][1] * b->coeff[1][1] + a->coeff[1][2] * b->coeff[2][1]; out.coeff[1][2] = a->coeff[1][0] * b->coeff[0][2] + a->coeff[1][1] * b->coeff[1][2] + a->coeff[1][2] * b->coeff[2][2]; out.coeff[2][0] = a->coeff[2][0] * b->coeff[0][0] + a->coeff[2][1] * b->coeff[1][0] + a->coeff[2][2] * b->coeff[2][0]; out.coeff[2][1] = a->coeff[2][0] * b->coeff[0][1] + a->coeff[2][1] * b->coeff[1][1] + a->coeff[2][2] * b->coeff[2][1]; out.coeff[2][2] = a->coeff[2][0] * b->coeff[0][2] + a->coeff[2][1] * b->coeff[1][2] + a->coeff[2][2] * b->coeff[2][2]; *a = out; } static void matrix_float_to_s10q8(__u16 out[3][3], const struct matrix *in) { unsigned int i, j; for (i = 0; i < 3; ++i) { for (j = 0; j < 3; ++j) out[i][j] = (__u16)((__s16)(in->coeff[i][j] * 256) & 0x3ff); } } static void matrix_float_to_s12q8(__u16 out[3][3], const struct matrix *in) { unsigned int i, j; for (i = 0; i < 3; ++i) { for (j = 0; j < 3; ++j) out[i][j] = (__u16)((__s16)(in->coeff[i][j] * 256) & 0xfff); } } /* ----------------------------------------------------------------------------- * CCDC parameters configuration */ int omap3_isp_ccdc_set_black_level(struct omap3_isp_device *isp, unsigned int value) { struct omap3isp_ccdc_update_config config; struct omap3isp_ccdc_bclamp bclamp; int ret; v4l2_subdev_open(isp->ccdc.entity); memset(&config, 0, sizeof config); config.update = OMAP3ISP_CCDC_BLCLAMP; config.flag = 0; config.bclamp = &bclamp; memset(&bclamp, 0, sizeof bclamp); bclamp.dcsubval = value; ret = ioctl(isp->ccdc.entity->fd, VIDIOC_OMAP3ISP_CCDC_CFG, &config); if (ret < 0) { printf("%s: %s (%d)\n", __func__, strerror(errno), errno); return -errno; } return ret; } /* ----------------------------------------------------------------------------- * Preview engine parameters configuration */ static const struct matrix omap3isp_preview_csc = { .coeff = { /* Default values. */ { 0.2968750, 0.5937500, 0.1093750 }, { -0.1718750, -0.3281250, 0.5000000 }, { 0.5000000, -0.3828125, -0.0781250 }, #if 0 /* Default values for fluorescent light. */ { 0.25781250, 0.50390625, 0.09765625 }, { -0.14843750, -0.29296875, 0.43750000 }, { 0.43750000, -0.36718750, -0.07031250 }, #endif }, }; static const struct matrix omap3isp_preview_rgb2rgb = { .coeff = { /* Default values. */ { 1.0, 0.0, 0.0 }, { 0.0, 1.0, 0.0 }, { 0.0, 0.0, 1.0 }, #if 0 /* Default values for fluorescent light. */ { 1.88281250, -0.812500, -0.07031250 }, { -0.39453125, 1.671875, -0.27734375 }, { -0.12500000, -1.250000, 2.37500000 }, #endif }, }; int omap3_isp_preview_set_contrast(struct omap3_isp_device *isp, unsigned int value) { int contrast = value; int ret; ret = v4l2_subdev_set_control(isp->preview.entity, V4L2_CID_CONTRAST, &contrast); if (ret < 0) return -errno; return ret; } int omap3_isp_preview_set_saturation(struct omap3_isp_device *isp, float value) { struct omap3isp_prev_update_config config; struct omap3isp_prev_rgbtorgb rgb2rgb; struct matrix saturation; struct matrix gain; int ret; matrix_zero(&gain); gain.coeff[0][0] = 1.0; gain.coeff[1][1] = value; gain.coeff[2][2] = value; saturation = omap3isp_preview_csc; matrix_invert(&saturation); matrix_multiply(&saturation, &gain); matrix_multiply(&saturation, &omap3isp_preview_csc); memset(&config, 0, sizeof config); config.update = OMAP3ISP_PREV_RGB2RGB; config.flag = OMAP3ISP_PREV_RGB2RGB; config.rgb2rgb = &rgb2rgb; memset(&rgb2rgb, 0, sizeof rgb2rgb); matrix_float_to_s12q8(rgb2rgb.matrix, &saturation); ret = ioctl(isp->preview.entity->fd, VIDIOC_OMAP3ISP_PRV_CFG, &config); if (ret < 0) return -errno; return ret; } int omap3_isp_preview_update_white_balance(struct omap3_isp_device *isp) { struct omap3isp_prev_update_config config; int ret; memset(&config, 0, sizeof config); config.update = OMAP3ISP_PREV_WB; config.flag = OMAP3ISP_PREV_WB; config.wbal = &isp->preview.wbal; ret = ioctl(isp->preview.entity->fd, VIDIOC_OMAP3ISP_PRV_CFG, &config); if (ret < 0) { printf("%s: %s (%d)\n", __func__, strerror(errno), errno); return -errno; } return ret; } int omap3_isp_preview_set_gain(struct omap3_isp_device *isp, float gain) { isp->preview.wbal.dgain = (__u16)(gain * (1 << 8)) & 0x3ff; return omap3_isp_preview_update_white_balance(isp); } int omap3_isp_preview_set_white_balance(struct omap3_isp_device *isp, float gains[4]) { isp->preview.wbal.coef0 = (__u8)clamp(gains[0] * (1 << 5), 0.0, 255.0); isp->preview.wbal.coef1 = (__u8)clamp(gains[1] * (1 << 5), 0.0, 255.0); isp->preview.wbal.coef2 = (__u8)clamp(gains[2] * (1 << 5), 0.0, 255.0); isp->preview.wbal.coef3 = (__u8)clamp(gains[3] * (1 << 5), 0.0, 255.0); return omap3_isp_preview_update_white_balance(isp); } int omap3_isp_preview_setup(struct omap3_isp_device *isp) { struct omap3isp_prev_update_config config; struct omap3isp_prev_rgbtorgb rgb2rgb; struct omap3isp_prev_csc csc; int ret; memset(&config, 0, sizeof config); config.update = OMAP3ISP_PREV_WB | OMAP3ISP_PREV_RGB2RGB | OMAP3ISP_PREV_COLOR_CONV; config.flag = OMAP3ISP_PREV_WB | OMAP3ISP_PREV_RGB2RGB | OMAP3ISP_PREV_COLOR_CONV; config.wbal = &isp->preview.wbal; config.rgb2rgb = &rgb2rgb; config.csc = &csc; isp->preview.wbal.dgain = 1 << 8; isp->preview.wbal.coef0 = 1 << 5; isp->preview.wbal.coef1 = 1 << 5; isp->preview.wbal.coef2 = 1 << 5; isp->preview.wbal.coef3 = 1 << 5; memset(&rgb2rgb, 0, sizeof rgb2rgb); matrix_float_to_s12q8(rgb2rgb.matrix, &omap3isp_preview_rgb2rgb); memset(&csc, 0, sizeof csc); matrix_float_to_s10q8(csc.matrix, &omap3isp_preview_csc); v4l2_subdev_open(isp->preview.entity); ret = ioctl(isp->preview.entity->fd, VIDIOC_OMAP3ISP_PRV_CFG, &config); if (ret < 0) { printf("%s: %s (%d)\n", __func__, strerror(errno), errno); return -errno; } return ret; } /* ----------------------------------------------------------------------------- * Sensor parameters configuration */ #define V4L2_CID_GAIN_RED (V4L2_CTRL_CLASS_CAMERA | 0x1001) #define V4L2_CID_GAIN_GREEN1 (V4L2_CTRL_CLASS_CAMERA | 0x1002) #define V4L2_CID_GAIN_GREEN2 (V4L2_CTRL_CLASS_CAMERA | 0x1003) #define V4L2_CID_GAIN_BLUE (V4L2_CTRL_CLASS_CAMERA | 0x1004) int omap3_isp_sensor_get_exposure(struct omap3_isp_device *isp, unsigned int *exposure) { struct v4l2_ext_control ctrls[1]; int ret; ctrls[0].id = V4L2_CID_EXPOSURE; ret = v4l2_subdev_set_controls(isp->sensor.entity, ARRAY_SIZE(ctrls), ctrls); if (ret < 0) return ret; *exposure = ctrls[0].value; return 0; } int omap3_isp_sensor_set_exposure(struct omap3_isp_device *isp, unsigned int exposure) { struct v4l2_ext_control ctrls[1]; ctrls[0].id = V4L2_CID_EXPOSURE; ctrls[0].value = exposure; return v4l2_subdev_set_controls(isp->sensor.entity, ARRAY_SIZE(ctrls), ctrls); } int omap3_isp_sensor_set_gains(struct omap3_isp_device *isp, unsigned int red, unsigned int green, unsigned int blue) { struct v4l2_ext_control ctrls[4]; ctrls[0].id = V4L2_CID_GAIN_RED; ctrls[0].value = red; ctrls[1].id = V4L2_CID_GAIN_GREEN1; ctrls[1].value = green; ctrls[2].id = V4L2_CID_GAIN_GREEN2; ctrls[2].value = green; ctrls[3].id = V4L2_CID_GAIN_BLUE; ctrls[3].value = blue; return v4l2_subdev_set_controls(isp->sensor.entity, ARRAY_SIZE(ctrls), ctrls); }