/* * When cropping, the camera automatically centers the cropped region, there * doesn't seem to be a way to specify an explicit location of the rectangle.
*/ #define RJ54N1_COLUMN_SKIP 0 #define RJ54N1_ROW_SKIP 0 #define RJ54N1_MAX_WIDTH 1600 #define RJ54N1_MAX_HEIGHT 1200
#define PLL_L 2 #define PLL_N 0x31
/* I2C addresses: 0x50, 0x51, 0x60, 0x61 */
/* RJ54N1CB0C has only one fixed colorspace per pixelcode */ struct rj54n1_datafmt {
u32 code; enum v4l2_colorspace colorspace;
};
/* Find a data format by a pixel code in an array */ staticconststruct rj54n1_datafmt *rj54n1_find_datafmt(
u32 code, conststruct rj54n1_datafmt *fmt, int n)
{ int i; for (i = 0; i < n; i++) if (fmt[i].code == code) return fmt + i;
/* * The actual geometry configuration routine. It scales the input window into * the output one, updates the window sizes and returns an error or the resize * coefficient on success. Note: we only use the "Fixed Scaling" on this camera.
*/ staticint rj54n1_sensor_scale(struct v4l2_subdev *sd, s32 *in_w, s32 *in_h,
s32 *out_w, s32 *out_h)
{ struct i2c_client *client = v4l2_get_subdevdata(sd); struct rj54n1 *rj54n1 = to_rj54n1(client); unsignedint skip, resize, input_w = *in_w, input_h = *in_h,
output_w = *out_w, output_h = *out_h;
u16 inc_sel, wb_bit8, wb_left, wb_right, wb_top, wb_bottom; unsignedint peak, peak_50, peak_60; int ret;
/* * We have a problem with crops, where the window is larger than 512x384 * and output window is larger than a half of the input one. In this * case we have to either reduce the input window to equal or below * 512x384 or the output window to equal or below 1/2 of the input.
*/ if (output_w > max(512U, input_w / 2)) { if (2 * output_w > RJ54N1_MAX_WIDTH) {
input_w = RJ54N1_MAX_WIDTH;
output_w = RJ54N1_MAX_WIDTH / 2;
} else {
input_w = output_w * 2;
}
dev_dbg(&client->dev, "Adjusted output width: in %u, out %u\n",
input_w, output_w);
}
dev_dbg(&client->dev, "Adjusted output height: in %u, out %u\n",
input_h, output_h);
}
/* Idea: use the read mode for snapshots, handle separate geometries */
ret = rj54n1_set_rect(client, RJ54N1_X_OUTPUT_SIZE_S_L,
RJ54N1_Y_OUTPUT_SIZE_S_L,
RJ54N1_XY_OUTPUT_SIZE_S_H, output_w, output_h); if (!ret)
ret = rj54n1_set_rect(client, RJ54N1_X_OUTPUT_SIZE_P_L,
RJ54N1_Y_OUTPUT_SIZE_P_L,
RJ54N1_XY_OUTPUT_SIZE_P_H, output_w, output_h);
/* Prohibited value ranges */ switch (resize) { case 2040 ... 2047:
resize = 2039; break; case 4080 ... 4095:
resize = 4079; break; case 8160 ... 8191:
resize = 8159; break; case 16320 ... 16384:
resize = 16319;
}
}
/* Set scaling */
ret = reg_write(client, RJ54N1_RESIZE_HOLD_L, resize & 0xff); if (!ret)
ret = reg_write(client, RJ54N1_RESIZE_HOLD_H, resize >> 8);
if (ret < 0) return ret;
/* * Configure a skipping bitmask. The sensor will select a skipping value * among set bits automatically. This is very unclear in the datasheet * too. I was told, in this register one enables all skipping values, * that are required for a specific resize, and the camera selects * automatically, which ones to use. But it is unclear how to identify, * which cropping values are needed. Secondly, why don't we just set all * bits and let the camera choose? Would it increase processing time and * reduce the framerate? Using 0xfffc for INC_USE_SEL doesn't seem to * improve the image quality or stability for larger frames (see comment * above), but I didn't check the framerate.
*/
skip = min(resize / 1024, 15U);
if (!ret)
ret = reg_write(client, RJ54N1_BIT8_WB, wb_bit8); if (!ret)
ret = reg_write(client, RJ54N1_HCAPS_WB, wb_left); if (!ret)
ret = reg_write(client, RJ54N1_VCAPS_WB, wb_top); if (!ret)
ret = reg_write(client, RJ54N1_HCAPE_WB, wb_right); if (!ret)
ret = reg_write(client, RJ54N1_VCAPE_WB, wb_bottom);
}
if (!ret)
ret = reg_write(client, RJ54N1_PEAK_H,
((peak_50 >> 4) & 0xf0) | (peak_60 >> 8)); if (!ret)
ret = reg_write(client, RJ54N1_PEAK_50, peak_50); if (!ret)
ret = reg_write(client, RJ54N1_PEAK_60, peak_60); if (!ret)
ret = reg_write(client, RJ54N1_PEAK_DIFF, peak / 150);
/* Start resizing */ if (!ret)
ret = reg_write(client, RJ54N1_RESIZE_CONTROL,
RESIZE_HOLD_SEL | RESIZE_GO | 1);
if (ret < 0) return ret;
/* Constant taken from manufacturer's example */
msleep(230);
ret = reg_write(client, RJ54N1_RESIZE_CONTROL, RESIZE_HOLD_SEL | 1); if (ret < 0) return ret;
/* Enable external clock */
ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK | SOFT_STDBY); /* Leave stand-by. Note: use this when implementing suspend / resume */ if (!ret)
ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK);
if (!ret)
ret = reg_write(client, RJ54N1_PLL_L, PLL_L); if (!ret)
ret = reg_write(client, RJ54N1_PLL_N, PLL_N);
/* TGCLK dividers */ if (!ret)
ret = reg_write(client, RJ54N1_RATIO_TG,
rj54n1->clk_div.ratio_tg); if (!ret)
ret = reg_write(client, RJ54N1_RATIO_T,
rj54n1->clk_div.ratio_t); if (!ret)
ret = reg_write(client, RJ54N1_RATIO_R,
rj54n1->clk_div.ratio_r);
/* Enable TGCLK & RAMP */ if (!ret)
ret = reg_write(client, RJ54N1_RAMP_TGCLK_EN, 3);
/* Disable clock output */ if (!ret)
ret = reg_write(client, RJ54N1_OCLK_DSP, 0);
/* Set divisors */ if (!ret)
ret = reg_write(client, RJ54N1_RATIO_OP,
rj54n1->clk_div.ratio_op); if (!ret)
ret = reg_write(client, RJ54N1_RATIO_O,
rj54n1->clk_div.ratio_o);
/* Enable OCLK */ if (!ret)
ret = reg_write(client, RJ54N1_OCLK_SEL_EN, 1);
/* Use PLL for Timing Generator, write 2 to reserved bits */ if (!ret)
ret = reg_write(client, RJ54N1_TG_BYPASS, 2);
/* Take sensor out of reset */ if (!ret)
ret = reg_write(client, RJ54N1_RESET_STANDBY,
E_EXCLK | SEN_RSTX); /* Enable PLL */ if (!ret)
ret = reg_write(client, RJ54N1_PLL_EN, 1);
/* Wait for PLL to stabilise */
msleep(10);
/* Enable clock to frequency divider */ if (!ret)
ret = reg_write(client, RJ54N1_CLK_RST, 1);
if (!ret)
ret = reg_read(client, RJ54N1_CLK_RST); if (ret != 1) {
dev_err(&client->dev, "Resetting RJ54N1CB0C clock failed: %d!\n", ret); return -EIO;
}
/* Start the PLL */
ret = reg_set(client, RJ54N1_OCLK_DSP, 1, 1);
/* Enable OCLK */ if (!ret)
ret = reg_write(client, RJ54N1_OCLK_SEL_EN, 1);
return ret;
}
staticint rj54n1_reg_init(struct i2c_client *client)
{ struct rj54n1 *rj54n1 = to_rj54n1(client); int ret = rj54n1_set_clock(client);
if (!ret)
ret = reg_write_multiple(client, bank_7, ARRAY_SIZE(bank_7)); if (!ret)
ret = reg_write_multiple(client, bank_10, ARRAY_SIZE(bank_10));
/* Set binning divisors */ if (!ret)
ret = reg_write(client, RJ54N1_SCALE_1_2_LEV, 3 | (7 << 4)); if (!ret)
ret = reg_write(client, RJ54N1_SCALE_4_LEV, 0xf);
/* Switch to fixed resize mode */ if (!ret)
ret = reg_write(client, RJ54N1_RESIZE_CONTROL,
RESIZE_HOLD_SEL | 1);
/* Set gain */ if (!ret)
ret = reg_write(client, RJ54N1_Y_GAIN, 0x84);
/* * Mirror the image back: default is upside down and left-to-right... * Set manual preview / still shot switching
*/ if (!ret)
ret = reg_write(client, RJ54N1_MIRROR_STILL_MODE, 0x27);
if (!ret)
ret = reg_write_multiple(client, bank_4, ARRAY_SIZE(bank_4));
/* Auto exposure area */ if (!ret)
ret = reg_write(client, RJ54N1_EXPOSURE_CONTROL, 0x80); /* Check current auto WB config */ if (!ret)
ret = reg_read(client, RJ54N1_WB_SEL_WEIGHT_I); if (ret >= 0) {
rj54n1->auto_wb = ret & 0x80;
ret = reg_write_multiple(client, bank_5, ARRAY_SIZE(bank_5));
} if (!ret)
ret = reg_write_multiple(client, bank_8, ARRAY_SIZE(bank_8));
if (!ret)
ret = reg_write(client, RJ54N1_RESET_STANDBY,
E_EXCLK | DSP_RSTX | SEN_RSTX);
/* Commit init */ if (!ret)
ret = rj54n1_commit(client);
/* Take DSP, TG, sensor out of reset */ if (!ret)
ret = reg_write(client, RJ54N1_RESET_STANDBY,
E_EXCLK | DSP_RSTX | TG_RSTX | SEN_RSTX);
/* Start register update? Same register as 0x?FE in many bank_* sets */ if (!ret)
ret = reg_write(client, RJ54N1_FWFLG, 2);
/* Constant taken from manufacturer's example */
msleep(700);
if (format->which == V4L2_SUBDEV_FORMAT_TRY) return 0;
/* * Verify if the sensor has just been powered on. TODO: replace this * with proper PM, when a suitable API is available.
*/
ret = reg_read(client, RJ54N1_RESET_STANDBY); if (ret < 0) return ret;
if (!(ret & E_EXCLK)) {
ret = rj54n1_reg_init(client); if (ret < 0) return ret;
}
/* RA_SEL_UL is only relevant for raw modes, ignored otherwise. */ switch (mf->code) { case MEDIA_BUS_FMT_YUYV8_2X8:
ret = reg_write(client, RJ54N1_OUT_SEL, 0); if (!ret)
ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); break; case MEDIA_BUS_FMT_YVYU8_2X8:
ret = reg_write(client, RJ54N1_OUT_SEL, 0); if (!ret)
ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8); break; case MEDIA_BUS_FMT_RGB565_2X8_LE:
ret = reg_write(client, RJ54N1_OUT_SEL, 0x11); if (!ret)
ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); break; case MEDIA_BUS_FMT_RGB565_2X8_BE:
ret = reg_write(client, RJ54N1_OUT_SEL, 0x11); if (!ret)
ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8); break; case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE:
ret = reg_write(client, RJ54N1_OUT_SEL, 4); if (!ret)
ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); if (!ret)
ret = reg_write(client, RJ54N1_RA_SEL_UL, 0); break; case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE:
ret = reg_write(client, RJ54N1_OUT_SEL, 4); if (!ret)
ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); if (!ret)
ret = reg_write(client, RJ54N1_RA_SEL_UL, 8); break; case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE:
ret = reg_write(client, RJ54N1_OUT_SEL, 4); if (!ret)
ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8); if (!ret)
ret = reg_write(client, RJ54N1_RA_SEL_UL, 0); break; case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE:
ret = reg_write(client, RJ54N1_OUT_SEL, 4); if (!ret)
ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8); if (!ret)
ret = reg_write(client, RJ54N1_RA_SEL_UL, 8); break; case MEDIA_BUS_FMT_SBGGR10_1X10:
ret = reg_write(client, RJ54N1_OUT_SEL, 5); break; default:
ret = -EINVAL;
}
/* Special case: a raw mode with 10 bits of data per clock tick */ if (!ret)
ret = reg_set(client, RJ54N1_OCLK_SEL_EN,
(mf->code == MEDIA_BUS_FMT_SBGGR10_1X10) << 1, 2);
/* * Interface active, can use i2c. If it fails, it can indeed mean, that * this wasn't our capture interface, so, we wait for the right one
*/ staticint rj54n1_video_probe(struct i2c_client *client, struct rj54n1_pdata *priv)
{ struct rj54n1 *rj54n1 = to_rj54n1(client); int data1, data2; int ret;
ret = rj54n1_s_power(&rj54n1->subdev, 1); if (ret < 0) return ret;
/* Read out the chip version register */
data1 = reg_read(client, RJ54N1_DEV_CODE);
data2 = reg_read(client, RJ54N1_DEV_CODE2);
if (data1 != 0x51 || data2 != 0x10) {
ret = -ENODEV;
dev_info(&client->dev, "No RJ54N1CB0C found, read 0x%x:0x%x\n",
data1, data2); goto done;
}
/* Configure IOCTL polarity from the platform data: 0 or 1 << 7. */
ret = reg_write(client, RJ54N1_IOC, priv->ioctl_high << 7); if (ret < 0) goto done;
dev_info(&client->dev, "Detected a RJ54N1CB0C chip ID 0x%x:0x%x\n",
data1, data2);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.