📄 sensor_ov9640.c
字号:
DEF_BLUE }, 0, OV9640_BLUE, 0xff, 0 }, { { V4L2_CID_AUTO_WHITE_BALANCE, V4L2_CTRL_TYPE_BOOLEAN, "Auto White Balance", 0,1,0, DEF_AWB }, 0, OV9640_COM8, 0x02, 1 }, { { V4L2_CID_HFLIP, V4L2_CTRL_TYPE_BOOLEAN, "Mirror Image", 0, 1, 0, DEF_HFLIP }, 0, OV9640_MVFP, 0x20, 5 }, { { V4L2_CID_VFLIP, V4L2_CTRL_TYPE_BOOLEAN, "Vertical Flip", 0, 1, 0, DEF_VFLIP }, 0, OV9640_MVFP, 0x10, 4 },};#define NUM_CONTROLS (sizeof(control)/sizeof(control[0]))const static struct ov9640_reg * ov9640_reg_init[NUM_PIXEL_FORMATS][NUM_IMAGE_SIZES] ={ { qqcif_yuv, qqvga_yuv, qcif_yuv, qvga_yuv, cif_yuv, vga_yuv, sxga_yuv }, { qqcif_565, qqvga_565, qcif_565, qvga_565, cif_565, vga_565, sxga_565 }, { qqcif_555, qqvga_555, qcif_555, qvga_555, cif_555, vga_555, sxga_555 },};/* * Read a value from a register in an OV9640 sensor device. The value is * returned in 'val'. * Returns zero if successful, or non-zero otherwise. */static int ov9640_read_reg(struct i2c_client *client, u8 reg, u8 *val){ int err; struct i2c_msg msg[1]; unsigned char data[1]; if (!client->adapter) return -ENODEV; msg->addr = client->addr; msg->flags = 0; msg->len = 1; msg->buf = data; *data = reg; err = i2c_transfer(client->adapter, msg, 1); if (err >= 0) { msg->flags = I2C_M_RD; err = i2c_transfer(client->adapter, msg, 1); } if (err >= 0) { *val = *data; return 0; } return err;}/* Write a value to a register in an OV9640 sensor device. * Returns zero if successful, or non-zero otherwise. */static int ov9640_write_reg(struct i2c_client *client, u8 reg, u8 val){ int err; struct i2c_msg msg[1]; unsigned char data[2]; if (!client->adapter) return -ENODEV; msg->addr = client->addr; msg->flags = 0; msg->len = 2; msg->buf = data; data[0] = reg; data[1] = val; err = i2c_transfer(client->adapter, msg, 1); if (err >= 0) return 0; return err;}static int ov9640_write_reg_mask(struct i2c_client *client, u8 reg, u8 *val, u8 mask){ u8 oldval, newval; int rc; if (mask == 0xff) { newval = *val; } else { /* need to do read - modify - write */ if ((rc = ov9640_read_reg(client, reg, &oldval))) return rc; oldval &= (~mask); /* Clear the masked bits */ *val &= mask; /* Enforce mask on value */ newval = oldval | *val; /* Set the desired bits */ } /* write the new value to the register */ if ((rc = ov9640_write_reg(client, reg, newval))) return rc; if ((rc = ov9640_read_reg(client, reg, &newval))) return rc; *val = newval & mask; return 0;}static int ov9640_read_reg_mask(struct i2c_client *client, u8 reg, u8 *val, u8 mask){ int rc; if ((rc = ov9640_read_reg(client, reg, val))) return rc; (*val) &= mask; return 0;}/* Initialize a list of OV9640 registers. * The list of registers is terminated by the pair of values * { OV9640_REG_TERM, OV9640_VAL_TERM }. * Returns zero if successful, or non-zero otherwise. */static int ov9640_write_regs(struct i2c_client *client, const struct ov9640_reg reglist[]){ int err; const struct ov9640_reg *next = reglist; while (!((next->reg == OV9640_REG_TERM) && (next->val == OV9640_VAL_TERM))) { err = ov9640_write_reg(client, next->reg, next->val); udelay(100); if (err) return err; next++; } return 0;}/* Returns the index of the requested ID from the control structure array */static intfind_vctrl(int id){ int i; if (id < V4L2_CID_BASE) return -EDOM; for (i = NUM_CONTROLS - 1; i >= 0; i--) if (control[i].qc.id == id) break; if (i < 0) i = -EINVAL; return i;}/* Calculate the internal clock divisor (value of the CLKRC register) of the * OV9640 given the image size, the frequency (in Hz) of its XCLK input and a * desired frame period (in seconds). The frame period 'fper' is expressed as * a fraction. The frame period is an input/output parameter. * Returns the value of the OV9640 CLKRC register that will yield the frame * period returned in 'fper' at the specified xclk frequency. The * returned period will be as close to the requested period as possible. */static unsigned charov9640_clkrc(enum image_size isize, unsigned long xclk, struct v4l2_fract *fper){ unsigned long fpm, fpm_max; /* frames per minute */ unsigned long divisor; const unsigned long divisor_max = 64; const static unsigned long clks_per_frame[] = #ifdef CONFIG_ARCH_OMAP24XX { 200000, 400000, 200000, 400000, 400000, 800000, 3200000 }; /* QQCIF QQVGA QCIF QVGA CIF VGA SXGA *actually 199680,400000, 199680, 400000, 399360, 800000, 3200000 */#else { 200000, 200000, 200000, 200000, 400000, 800000, 3200000 };#endif if (fper->numerator > 0) fpm = (fper->denominator*60)/fper->numerator; else fpm = 0xffffffff; fpm_max = (xclk*60)/clks_per_frame[isize]; if (fpm_max == 0) fpm_max = 1; if (fpm > fpm_max) fpm = fpm_max; if (fpm == 0) fpm = 1; divisor = fpm_max/fpm; if (divisor > divisor_max) divisor = divisor_max; fper->numerator = divisor*60; fper->denominator = fpm_max; /* try to reduce the fraction */ while (!(fper->denominator % 5) && !(fper->numerator % 5)) { fper->numerator /= 5; fper->denominator /= 5; } while (!(fper->denominator % 3) && !(fper->numerator % 3)) { fper->numerator /= 3; fper->denominator /= 3; } while (!(fper->denominator % 2) && !(fper->numerator % 2)) { fper->numerator /= 2; fper->denominator /= 2; } if (fper->numerator < fper->denominator) { if (!(fper->denominator % fper->numerator)) { fper->denominator /= fper->numerator; fper->numerator = 1; } } else { if (!(fper->numerator % fper->denominator)) { fper->numerator /= fper->denominator; fper->denominator = 1; } } /* we set bit 7 in CLKRC to enable the digital PLL */ return (0x80 | (divisor - 1));}/* Configure the OV9640 for a specified image size, pixel format, and frame * period. xclk is the frequency (in Hz) of the xclk input to the OV9640. * fper is the frame period (in seconds) expressed as a fraction. * Returns zero if successful, or non-zero otherwise. * The actual frame period is returned in fper. */static intov9640_configure(struct i2c_client *client, enum image_size isize, enum pixel_format pfmt, unsigned long xclk, struct v4l2_fract *fper){ int err; unsigned char clkrc; /* common register initialization */ err = ov9640_write_regs(client, ov9640_common); if (err) return err; /* configure image size and pixel format */ err = ov9640_write_regs(client, ov9640_reg_init[pfmt][isize]); if (err) return err; /* configure frame rate */ clkrc = ov9640_clkrc(isize, xclk, fper); err = ov9640_write_reg(client, OV9640_CLKRC, clkrc); if (err) return err; return 0;}/* Write to GPIO EXPA on the board. * The GPIO expanders need an independent I2C client driver. */static intwrite_gpio_expa(u8 val, int add){ struct i2c_adapter *adap; int err; struct i2c_msg msg[1]; unsigned char data[1]; adap = i2c_get_adapter(0); if (!adap) return -ENODEV; msg->addr = add; /* I2C address of GPIO EXPA */ msg->flags = 0; msg->len = 1; msg->buf = data; data[0] = val; err = i2c_transfer(adap, msg, 1); if (err >= 0) return 0; return err;}/* Read from GPIO EXPA on the board. * The GPIO expanders need an independent I2C client driver. */static intread_gpio_expa(u8 *val, int add){ struct i2c_adapter *adap; int err; struct i2c_msg msg[1]; unsigned char data[1]; adap = i2c_get_adapter(0); if (!adap) return -ENODEV; msg->addr = add; /* I2C address of GPIO EXPA */ msg->flags = I2C_M_RD; msg->len = 1; msg->buf = data; err = i2c_transfer(adap, msg, 1); *val = data[0]; if (err >= 0) return 0; return err;}#ifdef CONFIG_ARCH_OMAP24XX/* Power-up the OV9640 sensor on the H4 board. * Returns 0 if successful, non-zero otherwise. */static intov9640_powerup(void){ unsigned char expa; int err; /* read current state of GPIO EXPA outputs */ if ((err = read_gpio_expa(&expa, 0x20))) { printk(KERN_ERR "Error reading GPIO EXPA\n"); return err; } /* Set GPIO EXPA P3 (CAMERA_MODULE_EN) to power-up sensor */ if ((err = write_gpio_expa(expa | 0x08, 0x20))) { printk(KERN_ERR "Error writing to GPIO EXPA\n"); return err; } /* read current state of GPIO EXPA outputs */ if ((err = read_gpio_expa(&expa, 0x22))) { printk(KERN_ERR "Error reading GPIO EXPA\n"); return err; } /* Clear GPIO EXPA P7 (CAM_RST) */ if ((err = write_gpio_expa(expa & ~0x80, 0x22))) { printk(KERN_ERR "Error writing to GPIO EXPA\n"); return err; } return 0;}/* Power-down the OV9640 sensor on the H4 board. * Returns 0 if successful, non-zero otherwise. */static intov9640_powerdown(void){ unsigned char expa; int err; /* read current state of GPIO EXPA outputs */ if ((err = read_gpio_expa(&expa, 0x20))) { printk(KERN_ERR "Error reading GPIO EXPA\n"); return err; } /* Clear GPIO EXPA P3 (CAMERA_MODULE_EN) to power-down sensor */ if ((err = write_gpio_expa(expa & ~0x08, 0x20))) { printk(KERN_ERR "Error writing to GPIO EXPA\n"); return err; } return 0;}#elsestatic intov9640_powerup(void){ unsigned char expa; int err; if (machine_is_omap_h2()) return 0; /* read the current state of GPIO EXPA output */ if (( err = read_gpio_expa(&expa, 0x27))){ printk(KERN_ERR "Error reading GPIO EXPA \n"); return err; } /* set GPIO EXPA P7 CAMERA_MOD_EN to power-up sensor */ if ((err = write_gpio_expa(expa | 0x80, 0x27))) { printk(KERN_ERR "Error writing to GPIO EXPA \n"); return err; } return 0;}static intov9640_powerdown(void){ unsigned char expa; int err; if (machine_is_omap_h2()) return 0; /* read the current state of GPIO EXPA output */ if (( err = read_gpio_expa(&expa, 0x27))){ printk(KERN_ERR "Error reading GPIO EXPA \n"); return err; } /* clear GPIO EXPA P7 CAMERA_MOD_EN to power-up sensor */ if ((err = write_gpio_expa(expa & ~0x80, 0x27))) { printk(KERN_ERR "Error writing to GPIO EXPA \n"); return err; } return 0;}#endif/* Detect if an OV9640 is present, and if so which revision. * A device is considered to be detected if the manufacturer ID (MIDH and MIDL) * and the product ID (PID) registers match the expected values. * Any value of the version ID (VER) register is accepted. * Here are the version numbers we know about: * 0x48 --> OV9640 Revision 1 or OV9640 Revision 2 * 0x49 --> OV9640 Revision 3 * Returns a negative error number if no device is detected, or the * non-negative value of the version ID register if a device is detected. */static intov9640_detect(struct i2c_client *client){ u8 midh, midl, pid, ver; if (!client) return -ENODEV; if (ov9640_read_reg(client, OV9640_MIDH, &midh)) return -ENODEV; if (ov9640_read_reg(client, OV9640_MIDL, &midl)) return -ENODEV; if (ov9640_read_reg(client, OV9640_PID, &pid)) return -ENODEV; if (ov9640_read_reg(client, OV9640_VER, &ver)) return -ENODEV;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -