1. /* 第一个参数名字用于匹配,第二个参数是摄像头地址 */
  2. static struct i2c_board_info cmos_ov7740_info = {
  3. /* 由芯片手册可知,写 -- 0x42(01000010),读 -- 0x43(01000011)
  4. * 将读地址或者写地址,向右移动一位即可。
  5. */
  6. I2C_BOARD_INFO("cmos_ov7740", 0x21),
  7. };
  8. static struct i2c_client *cmos_ov7740_client;
  9. static int cmos_ov7740_dev_init(void)
  10. {
  11. struct i2c_adapter *i2c_adap;
  12. /* 获得适配器,由2440原理图可知,2440只有一个iic控制器,所以iic控制器为适配器0,
  13. * 也就是0号iic总线上
  14. */
  15. i2c_adap = i2c_get_adapter(0);
  16. cmos_ov7740_client = i2c_new_device(i2c_adap, &cmos_ov7740_info);
  17. /* 适配器使用完后要释放他 */
  18. i2c_put_adapter(i2c_adap);
  19. return 0;
  20. }
  21. static void cmos_ov7740_dev_exit(void)
  22. {
  23. i2c_unregister_device(cmos_ov7740_client);
  24. }
  25. module_init(cmos_ov7740_dev_init);
  26. module_exit(cmos_ov7740_dev_exit);
  27. MODULE_LICENSE("GPL");
  1. cmos_ov7740_drv.c
  1. #define OV7740_INIT_REGS_SIZE (sizeof(ov7740_setting_30fps_VGA_640_480)/sizeof(ov7740_setting_30fps_VGA_640_480[0]))
  2. #define CAM_SRC_HSIZE   (640)
  3. #define CAM_SRC_VSIZE   (480)
  4. #define CAM_ORDER_YCbYCr (0)
  5. #define CAM_ORDER_YCrYCb (1)
  6. #define CAM_ORDER_CbYCrY (2)
  7. #define CAM_ORDER_CrYCbY (3)
  8. #define WinHorOfst      (0)
  9. #define WinVerOfst      (0)
  10. struct cmos_ov7740_scaler {
  11. unsigned int PreHorRatio;//水平变比
  12. unsigned int PreVerRatio;//垂直变比
  13. unsigned int H_Shift;//水平比
  14. unsigned int V_Shift;//垂直比
  15. unsigned int PreDstWidth;//目标宽度
  16. unsigned int PreDstHeight;//目标高度
  17. unsigned int MainHorRatio;//预览主缩放的水平比
  18. unsigned int MainVerRatio;//预览主缩放的垂直比
  19. unsigned int SHfactor;//缩放变比
  20. unsigned int ScaleUpDown;//放大缩小标志
  21. };
  22. static struct cmos_ov7740_scaler sc;
  23. typedef struct cmos_ov7740_i2c_value {
  24. unsigned char regaddr;
  25. unsigned char value;
  26. }ov7740_t;
  27. /* init: 640x480,30fps的,YUV422输出格式 */
  28. ov7740_t ov7740_setting_30fps_VGA_640_480[] =
  29. {
  30. {0x12, 0x80},
  31. {0x47, 0x02},
  32. {0x17, 0x27},
  33. {0x04, 0x40},
  34. {0x1B, 0x81},
  35. {0x29, 0x17},
  36. {0x5F, 0x03},
  37. {0x3A, 0x09},
  38. {0x33, 0x44},
  39. {0x68, 0x1A},
  40. {0x14, 0x38},
  41. {0x5F, 0x04},
  42. {0x64, 0x00},
  43. {0x67, 0x90},
  44. {0x27, 0x80},
  45. {0x45, 0x41},
  46. {0x4B, 0x40},
  47. {0x36, 0x2f},
  48. {0x11, 0x01},
  49. {0x36, 0x3f},
  50. {0x0c, 0x12},
  51. {0x12, 0x00},
  52. {0x17, 0x25},
  53. {0x18, 0xa0},
  54. {0x1a, 0xf0},
  55. {0x31, 0xa0},
  56. {0x32, 0xf0},
  57. {0x85, 0x08},
  58. {0x86, 0x02},
  59. {0x87, 0x01},
  60. {0xd5, 0x10},
  61. {0x0d, 0x34},
  62. {0x19, 0x03},
  63. {0x2b, 0xf8},
  64. {0x2c, 0x01},
  65. {0x53, 0x00},
  66. {0x89, 0x30},
  67. {0x8d, 0x30},
  68. {0x8f, 0x85},
  69. {0x93, 0x30},
  70. {0x95, 0x85},
  71. {0x99, 0x30},
  72. {0x9b, 0x85},
  73. {0xac, 0x6E},
  74. {0xbe, 0xff},
  75. {0xbf, 0x00},
  76. {0x38, 0x14},
  77. {0xe9, 0x00},
  78. {0x3D, 0x08},
  79. {0x3E, 0x80},
  80. {0x3F, 0x40},
  81. {0x40, 0x7F},
  82. {0x41, 0x6A},
  83. {0x42, 0x29},
  84. {0x49, 0x64},
  85. {0x4A, 0xA1},
  86. {0x4E, 0x13},
  87. {0x4D, 0x50},
  88. {0x44, 0x58},
  89. {0x4C, 0x1A},
  90. {0x4E, 0x14},
  91. {0x38, 0x11},
  92. {0x84, 0x70}
  93. };
  94. struct cmos_ov7740_fmt {
  95. char  *name;
  96. u32   fourcc;          /* v4l2 format id */
  97. int   depth;
  98. };
  99. /* 这两个格式仅仅是预览通道支持的格式,由控制器的芯片手册可知 */
  100. static struct cmos_ov7740_fmt formats[] = {
  101. {
  102. .name     = "RGB565",
  103. .fourcc   = V4L2_PIX_FMT_RGB565,
  104. .depth    = 16,
  105. },
  106. {
  107. .name     = "PACKED_RGB_888",
  108. .fourcc   = V4L2_PIX_FMT_RGB24,
  109. .depth    = 24,
  110. },
  111. };
  112. struct camif_buffer
  113. {
  114. unsigned int order;
  115. unsigned long virt_base;
  116. unsigned long phy_base;
  117. };
  118. struct camif_buffer img_buff[] =
  119. {
  120. {
  121. .order = 0,
  122. .virt_base = (unsigned long)NULL,
  123. .phy_base = (unsigned long)NULL
  124. },
  125. {
  126. .order = 0,
  127. .virt_base = (unsigned long)NULL,
  128. .phy_base = (unsigned long)NULL
  129. },
  130. {
  131. .order = 0,
  132. .virt_base = (unsigned long)NULL,
  133. .phy_base = (unsigned long)NULL
  134. },
  135. {
  136. .order = 0,
  137. .virt_base = (unsigned long)NULL,
  138. .phy_base = (unsigned long)NULL
  139. }
  140. };
  141. static struct i2c_client *cmos_ov7740_client;
  142. // CAMIF GPIO
  143. static unsigned long *GPJCON;
  144. static unsigned long *GPJDAT;
  145. static unsigned long *GPJUP;
  146. // CAMIF
  147. static unsigned long *CISRCFMT;
  148. static unsigned long *CIWDOFST;
  149. static unsigned long *CIGCTRL;
  150. static unsigned long *CIPRCLRSA1;
  151. static unsigned long *CIPRCLRSA2;
  152. static unsigned long *CIPRCLRSA3;
  153. static unsigned long *CIPRCLRSA4;
  154. static unsigned long *CIPRTRGFMT;
  155. static unsigned long *CIPRCTRL;
  156. static unsigned long *CIPRSCPRERATIO;
  157. static unsigned long *CIPRSCPREDST;
  158. static unsigned long *CIPRSCCTRL;
  159. static unsigned long *CIPRTAREA;
  160. static unsigned long *CIIMGCPT;
  161. // IRQ
  162. static unsigned long *SRCPND;
  163. static unsigned long *INTPND;
  164. static unsigned long *SUBSRCPND;
  165. static unsigned int SRC_Width, SRC_Height;
  166. static unsigned int TargetHsize_Pr, TargetVsize_Pr;
  167. static unsigned long buf_size;
  168. static unsigned int bytesperline;
  169. static DECLARE_WAIT_QUEUE_HEAD(cam_wait_queue);
  170. /* 中断标志 */
  171. static volatile int ev_cam = 0;
  172. static irqreturn_t cmos_ov7740_camif_irq_c(int irq, void *dev_id)
  173. {
  174. return IRQ_HANDLED;
  175. }
  176. static irqreturn_t cmos_ov7740_camif_irq_p(int irq, void *dev_id)
  177. {
  178. /* 清中断,因为不是使用上升沿或者下降沿触发,所以我们要清楚中断标志位
  179. * 确保下次能继续产生中断。由中断控制器的手册394页可知,向相应的寄存器写入1
  180. * 就是清除相应的中断标识,写入0表示保持原来的标志不变。
  181. */
  182. *SRCPND = 1<<6;
  183. *INTPND = 1<<6;
  184. *SUBSRCPND = 1<<12 ev_cam="1;" wake_up_interruptible="" cam_wait_queue="" return="" irq_handled="" a2="" uvc_v4l2_do_ioctl="" static="" int="" cmos_ov7740_vidioc_querycap="" struct="" file="" file="" void="" priv="" struct="" v4l2_capability="" cap="" memset="" cap="" 0="" sizeof="" cap="" strcpy="" cap-="">driver, "cmos_ov7740");
  185. strcpy(cap->card, "cmos_ov7740");
  186. cap->version = 2;
  187. /* V4L2_CAP_READWRITE表示获取摄像头数据使用过读写操作,不是通过流操作
  188. * 所谓流操作就是通过qbuf,dqbuf,mmap,poll等函数来获得数据的
  189. */
  190. cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
  191. return 0;
  192. }
  193. /* A3 列举支持哪种格式
  194. * 参考: uvc_fmts 数组
  195. */
  196. static int cmos_ov7740_vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
  197. struct v4l2_fmtdesc *f)
  198. {
  199. struct cmos_ov7740_fmt *fmt;
  200. if (f->index >= ARRAY_SIZE(formats))
  201. return -EINVAL;
  202. fmt = &formats[f->index];
  203. strlcpy(f->description, fmt->name, sizeof(f->description));
  204. f->pixelformat = fmt->fourcc;
  205. return 0;
  206. }
  207. /* A4 返回当前所使用的格式 */
  208. static int cmos_ov7740_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
  209. struct v4l2_format *f)
  210. {
  211. return 0;
  212. }
  213. /* A5 测试驱动程序是否支持某种格式, 强制设置该格式
  214. * 参考: uvc_v4l2_try_format
  215. *       myvivi_vidioc_try_fmt_vid_cap
  216. */
  217. static int cmos_ov7740_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
  218. struct v4l2_format *f)
  219. {
  220. /* 判断是否为摄像头设备 */
  221. if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
  222. {
  223. return -EINVAL;
  224. }
  225. /* 在coms中的CICOSCCTRL寄存器中可知,在预览通道只支持RGB16/RGB24 */
  226. if ((f->fmt.pix.pixelformat != V4L2_PIX_FMT_RGB565) && (f->fmt.pix.pixelformat != V4L2_PIX_FMT_RGB24))
  227. return -EINVAL;
  228. return 0;
  229. }
  230. /* A6 参考 myvivi_vidioc_s_fmt_vid_cap
  231. * 设置摄像头数据格式
  232. */
  233. static int cmos_ov7740_vidioc_s_fmt_vid_cap(struct file *file, void *priv,
  234. struct v4l2_format *f)
  235. {
  236. int ret = cmos_ov7740_vidioc_try_fmt_vid_cap(file, NULL, f);
  237. if (ret < 0)
  238. return ret;
  239. /* 目标图像的水平像素大小 */
  240. TargetHsize_Pr = f->fmt.pix.width;
  241. /* 目标图像的垂直像素大小 */
  242. TargetVsize_Pr = f->fmt.pix.height;
  243. if(f->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565)
  244. {
  245. /* bit[30]: 设置图像输出格式是RGB16、RGB24 */
  246. *CIPRSCCTRL &= ~(1<<30 f-="">fmt.pix.bytesperline = (f->fmt.pix.width * 16) >> 3;
  247. f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
  248. buf_size = f->fmt.pix.sizeimage;
  249. bytesperline = f->fmt.pix.bytesperline;
  250. }
  251. else if(f->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24)
  252. {
  253. /* bit[30]: 设置图像输出格式是RGB16、RGB24 */
  254. *CIPRSCCTRL |= (1<<30 rgb24="" 32="" s3c2440="" 515="" f-="">fmt.pix.bytesperline = (f->fmt.pix.width * 32) >> 3;
  255. f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
  256. buf_size = f->fmt.pix.sizeimage;
  257. bytesperline = f->fmt.pix.bytesperline;
  258. }
  259. /*
  260. CIPRTRGFMT:
  261. bit[28:16] -- 表示目标图片的水平像素大小(TargetHsize_Pr)
  262. bit[15:14] -- 是否旋转,我们这个驱动就不选择了
  263. bit[12:0]    -- 表示目标图片的垂直像素大小(TargetVsize_Pr)
  264. */
  265. *CIPRTRGFMT = (TargetHsize_Pr<<16)|(0x0<<14)|(TargetVsize_Pr<<0 return="" 0="" ciprclrsa1="" static="" int="" cmos_ov7740_vidioc_reqbufs="" struct="" file="" file="" void="" priv="" struct="" v4l2_requestbuffers="" p="" unsigned="" int="" order="" __get_free_pages="" get_order="" order="get_order(buf_size);" img_buff="" 0="" order="order;" __get_free_pages="" 4m="" kmalloc="" 128k="" img_buff="" 0="" virt_base="__get_free_pages(GFP_KERNEL|__GFP_DMA," img_buff="" 0="" order="" if="" img_buff="" 0="" virt_base="=" unsigned="" long="" null="" printk="" error0="" n="" goto="" error0="" ciprclrsa1="" img_buff="" 0="" phy_base="__virt_to_phys(img_buff[0].virt_base);" img_buff="" 1="" order="order;" img_buff="" 1="" virt_base="__get_free_pages(GFP_KERNEL|__GFP_DMA," img_buff="" 1="" order="" if="" img_buff="" 1="" virt_base="=" unsigned="" long="" null="" printk="" error1="" n="" goto="" error1="" img_buff="" 1="" phy_base="__virt_to_phys(img_buff[1].virt_base);" img_buff="" 2="" order="order;" img_buff="" 2="" virt_base="__get_free_pages(GFP_KERNEL|__GFP_DMA," img_buff="" 2="" order="" if="" img_buff="" 2="" virt_base="=" unsigned="" long="" null="" printk="" error2="" n="" goto="" error2="" img_buff="" 2="" phy_base="__virt_to_phys(img_buff[2].virt_base);" img_buff="" 3="" order="order;" img_buff="" 3="" virt_base="__get_free_pages(GFP_KERNEL|__GFP_DMA," img_buff="" 3="" order="" if="" img_buff="" 3="" virt_base="=" unsigned="" long="" null="" printk="" error3="" n="" goto="" error3="" img_buff="" 3="" phy_base="__virt_to_phys(img_buff[3].virt_base);" ciprclrsa1="" ciprclrsa1="img_buff[0].phy_base;" ciprclrsa2="img_buff[1].phy_base;" ciprclrsa3="img_buff[2].phy_base;" ciprclrsa4="img_buff[3].phy_base;" return="" 0="" error3:="" free_pages="" img_buff="" 2="" virt_base="" order="" img_buff="" 2="" phy_base="(unsigned" long="" null="" error2:="" free_pages="" img_buff="" 1="" virt_base="" order="" img_buff="" 1="" phy_base="(unsigned" long="" null="" error1:="" free_pages="" img_buff="" 0="" virt_base="" order="" img_buff="" 0="" phy_base="(unsigned" long="" null="" error0:="" return="" -enomem="" 2440="" 532="" static="" void="" calculateburstsize="" unsigned="" int="" hsize="" unsigned="" int="" mainbusrtsize="" unsigned="" int="" remainedbustsize="" unsigned="" int="" tmp="" 2="" 4="" 8="" 16="" 16="" tmp="(hSize/4)%16;" switch="" tmp="" case="" 0:="" mainbusrtsize="16;" remainedbustsize="16;" break="" case="" 4:="" mainbusrtsize="16;" remainedbustsize="4;" break="" case="" 8:="" mainbusrtsize="16;" remainedbustsize="8;" break="" 16="" 8="" default:="" tmp="(hSize/4)%8;" switch="" tmp="" case="" 0:="" mainbusrtsize="8;" remainedbustsize="8;" break="" case="" 4:="" mainbusrtsize="8;" remainedbustsize="4;" break="" 8="" 4="" default:="" mainbusrtsize="4;" tmp="(hSize/4)%4;" remainedbustsize="(tmp)?tmp:4;" break="" break="" jz2440="" 527="" static="" void="" camif_get_scaler_factor="" u32="" src="" u32="" tar="" u32="" ratio="" u32="" shift="" if="" src="">= 64*tar) {return;}
  266. else if(src >= 32*tar) {*ratio = 32; *shift = 5;}
  267. else if(src >= 16*tar) {*ratio = 16; *shift = 4;}
  268. else if(src >= 8*tar) {*ratio = 8; *shift = 3;}
  269. else if(src >= 4*tar) {*ratio = 4; *shift = 2;}
  270. else if(src >= 2*tar) {*ratio = 2; *shift = 1;}
  271. else {*ratio = 1; *shift = 0;}
  272. }
  273. /* 计算公式在2440用户手册的527页 */
  274. static void cmos_ov7740_calculate_scaler_info(void)
  275. {
  276. unsigned int sx, sy, tx, ty;
  277. /* 这里的源宽度和源高度是经过窗口剪切的图像数据
  278. * 这里的目标宽度和高度是应用程序在cmos_ov7740_vidioc_s_fmt_vid_cap函数
  279. * 设置数据格式时传入的,表示应用程序想要获得的图像分辨率
  280. */
  281. sx = SRC_Width;
  282. sy = SRC_Height;
  283. tx = TargetHsize_Pr;
  284. ty = TargetVsize_Pr;
  285. printk("%s: SRC_in(%d, %d), Target_out(%d, %d)\n", __func__, sx, sy, tx, ty);
  286. camif_get_scaler_factor(sx, tx, &sc.PreHorRatio, &sc.H_Shift);
  287. camif_get_scaler_factor(sy, ty, &sc.PreVerRatio, &sc.V_Shift);
  288. sc.PreDstWidth = sx / sc.PreHorRatio;
  289. sc.PreDstHeight = sy / sc.PreVerRatio;
  290. sc.MainHorRatio = (sx << 8) / (tx << sc.H_Shift);
  291. sc.MainVerRatio = (sy << 8) / (ty << sc.V_Shift);
  292. sc.SHfactor = 10 - (sc.H_Shift + sc.V_Shift);
  293. sc.ScaleUpDown = (tx>=sx)?1:0;
  294. }
  295. /* A11 启动传输
  296. * 参考: uvc_video_enable(video, 1):
  297. *           uvc_commit_video
  298. *           uvc_init_video
  299. */
  300. static int cmos_ov7740_vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
  301. {
  302. unsigned int Main_burst, Remained_burst;
  303. /*
  304. CISRCFMT:
  305. bit[31] -- 选择传输方式为BT601或者BT656
  306. bit[30] -- 设置偏移值(0 = +0 (正常情况下) - for YCbCr)
  307. bit[29] -- 保留位,必须设置为0
  308. bit[28:16]  -- 设置源图片的水平像素值(640)
  309. bit[15:14]  -- 设置源图片的颜色顺序(0x0c --> 0x2),由摄像头地址为0xc的寄存器
  310. 可知,输出的顺序为UYVYUYVY------>CbYCrYCbYCrY,
  311. bit[12:0]       -- 设置源图片的垂直像素值(480)
  312. 水平和垂直的像素值是摄像头模块输入的,查看摄像头模块输出水平和垂直的像素值即可
  313. */
  314. *CISRCFMT |= (0<<30)|(0<<29)|(CAM_SRC_HSIZE<<16)|(CAM_ORDER_CbYCrY<<14)|(CAM_SRC_VSIZE<<0);
  315. /*
  316. CIWDOFST:
  317. bit[31]     -- 1 = 使能窗口功能、0 = 不使用窗口功能
  318. bit[30、15:12]-- 清除溢出标志位
  319. bit[26:16]  -- 水平方向的裁剪的大小
  320. bit[10:0]       -- 垂直方向的裁剪的大小
  321. */
  322. *CIWDOFST |=(1<<30)|(0xf<<12);
  323. *CIWDOFST |= (1<<31)|(WinHorOfst<<16)|(WinVerOfst<<0);
  324. /* 裁剪后的图像宽度 = 原始图像宽度 - 2*水平窗口宽度 */
  325. SRC_Width = CAM_SRC_HSIZE - 2*WinHorOfst;
  326. /* 裁剪后的图像高度 = 原始图像高度 - 2*水平窗口高度 */
  327. SRC_Height = CAM_SRC_VSIZE - 2*WinVerOfst;
  328. /*
  329. CIGCTRL:
  330. bit[31]     -- 软件复位CAMIF控制器
  331. bit[30]     -- 用于复位外部摄像头模块
  332. bit[29]     -- 保留位,必须设置为1
  333. bit[28:27]  -- 用于选择信号源(00 = 输入源来自摄像头模块)
  334. bit[26]     -- 设置像素时钟的极性(猜0)
  335. bit[25]     -- 设置VSYNC的极性(0)
  336. bit[24]     -- 设置HREF的极性(0),行同步信号
  337. */
  338. *CIGCTRL |= (1<<29)|(0<<27)|(0<<26)|(0<<25)|(0<<24);
  339. /*
  340. CIPRCTRL:
  341. bit[23:19] -- 主突发长度(Main_burst)
  342. bit[18:14] -- 剩余突发长度(Remained_burst)
  343. bit[2]    -- 是否使能LastIRQ功能(不使能)
  344. */
  345. CalculateBurstSize(bytesperline, &Main_burst, &Remained_burst);
  346. *CIPRCTRL = (Main_burst<<19)|(Remained_burst<<14)|(0<<2);
  347. /*
  348. CIPRSCPRERATIO:
  349. bit[31:28]: 预览缩放的变化系数(SHfactor_Pr)
  350. bit[22:16]: 预览缩放的水平比(PreHorRatio_Pr)
  351. bit[6:0]: 预览缩放的垂直比(PreVerRatio_Pr)
  352. CIPRSCPREDST:
  353. bit[27:16]: 预览缩放的目标宽度(PreDstWidth_Pr)
  354. bit[11:0]: 预览缩放的目标高度(PreDstHeight_Pr)
  355. CIPRSCCTRL:
  356. bit[29:28]: 告诉摄像头控制器(图片是缩小、放大)(ScaleUpDown_Pr)
  357. bit[24:16]: 预览主缩放的水平比(MainHorRatio_Pr)
  358. bit[8:0]: 预览主缩放的垂直比(MainVerRatio_Pr)
  359. bit[31]: 必须固定设置为1
  360. bit[30]: 设置图像输出格式是RGB16、RGB24
  361. bit[15]: 预览缩放开始
  362. //bit[30]在cmos_ov7740_vidioc_s_fmt_vid_cap函数中设置
  363. */
  364. cmos_ov7740_calculate_scaler_info();
  365. *CIPRSCPRERATIO = (sc.SHfactor<<28)|(sc.PreHorRatio<<16)|(sc.PreVerRatio<<0);
  366. *CIPRSCPREDST = (sc.PreDstWidth<<16)|(sc.PreDstHeight<<0);
  367. *CIPRSCCTRL |= (1<<31)|(sc.ScaleUpDown<<28)|(sc.MainHorRatio<<16)|(sc.MainVerRatio<<0);
  368. /*
  369. CIPRTAREA:
  370. 表示预览通道的目标区域
  371. */
  372. *CIPRTAREA = TargetHsize_Pr * TargetVsize_Pr;
  373. /*
  374. CIIMGCPT:
  375. bit[31]: 用来使能摄像头控制器
  376. bit[30]: 使能编码通道
  377. bit[29]: 使能预览通道
  378. */
  379. *CIIMGCPT = (1<<31)|(1<<29);
  380. *CIPRSCCTRL |= (1<<15);
  381. return 0;
  382. }
  383. /* A17 停止
  384. * 参考 : uvc_video_enable(video, 0)
  385. */
  386. static int cmos_ov7740_vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type t)
  387. {
  388. *CIPRSCCTRL &= ~(1<<15);
  389. *CIIMGCPT &= ~((1<<31)|(1<<29));
  390. return 0;
  391. }
  392. static const struct v4l2_ioctl_ops cmos_ov7740_ioctl_ops = {
  393. // 表示它是一个摄像头设备
  394. .vidioc_querycap      = cmos_ov7740_vidioc_querycap,
  395. /* 用于列举、获得、测试、设置摄像头的数据的格式 */
  396. .vidioc_enum_fmt_vid_cap  = cmos_ov7740_vidioc_enum_fmt_vid_cap,
  397. .vidioc_g_fmt_vid_cap     = cmos_ov7740_vidioc_g_fmt_vid_cap,
  398. .vidioc_try_fmt_vid_cap   = cmos_ov7740_vidioc_try_fmt_vid_cap,
  399. .vidioc_s_fmt_vid_cap     = cmos_ov7740_vidioc_s_fmt_vid_cap,
  400. /* 缓冲区操作: 申请/查询/放入队列/取出队列 */
  401. .vidioc_reqbufs       = cmos_ov7740_vidioc_reqbufs,
  402. /* 说明: 因为我们是通过读的方式来获得摄像头数据,因此查询/放入队列/取出队列这些操作函数将不在需要 */
  403. #if 0
  404. .vidioc_querybuf      = myuvc_vidioc_querybuf,
  405. .vidioc_qbuf          = myuvc_vidioc_qbuf,
  406. .vidioc_dqbuf         = myuvc_vidioc_dqbuf,
  407. #endif
  408. // 启动/停止
  409. .vidioc_streamon      = cmos_ov7740_vidioc_streamon,
  410. .vidioc_streamoff     = cmos_ov7740_vidioc_streamoff,
  411. };
  412. /* A1 */
  413. static int cmos_ov7740_open(struct file *file)
  414. {
  415. return 0;
  416. }
  417. /* A18 关闭 */
  418. static int cmos_ov7740_close(struct file *file)
  419. {
  420. return 0;
  421. }
  422. /* 应用程序通过读的方式读取摄像头的数据 */
  423. static ssize_t cmos_ov7740_read(struct file *filep, char __user *buf, size_t count, loff_t *pos)
  424. {
  425. size_t end;
  426. int i;
  427. /* 取一帧数据的大小和用户想要读取数据的大小的最小值 */
  428. end = min_t(size_t, buf_size, count);
  429. wait_event_interruptible(cam_wait_queue, ev_cam);
  430. for(i=0; i<4; i++)
  431. {
  432. if(copy_to_user(buf, (void *)img_buff[i].virt_base, end))
  433. return -EFAULT;
  434. }
  435. ev_cam = 0;
  436. return end;
  437. }
  438. /* 应用程序使用read函数来获取摄像头数据,所以不再需要mmap,poll函数。 */
  439. static const struct v4l2_file_operations cmos_ov7740_fops = {
  440. .owner          = THIS_MODULE,
  441. .open               = cmos_ov7740_open,
  442. .release            = cmos_ov7740_close,
  443. /* video_ioctl2是内核提供的函数,只是起到中转的作用,最后会调用video_device中的ioctl_ops */
  444. .unlocked_ioctl         = video_ioctl2,
  445. .read           = cmos_ov7740_read,
  446. };
  447. /*
  448. 注意:
  449. 该函数是必须的,否则在insmod的时候,会出错
  450. */
  451. static void cmos_ov7740_release(struct video_device *vdev)
  452. {
  453. unsigned int order;
  454. order = get_order(buf_size);
  455. free_pages(img_buff[0].virt_base, order);
  456. img_buff[0].phy_base = (unsigned long)NULL;
  457. free_pages(img_buff[1].virt_base, order);
  458. img_buff[1].phy_base = (unsigned long)NULL;
  459. free_pages(img_buff[2].virt_base, order);
  460. img_buff[2].phy_base = (unsigned long)NULL;
  461. free_pages(img_buff[3].virt_base, order);
  462. img_buff[3].phy_base = (unsigned long)NULL;
  463. }
  464. /* 2.1. 分配、设置一个video_device结构体
  465. * 这里采用的是静态分配结构体,使用video_device_alloc()是动态分配摄像头结构体
  466. */
  467. static struct video_device cmos_ov7740_vdev = {
  468. .fops       = &cmos_ov7740_fops,
  469. .ioctl_ops      = &cmos_ov7740_ioctl_ops,
  470. .release        = cmos_ov7740_release,
  471. .name       = "cmos_ov7740",
  472. };
  473. static void cmos_ov7740_gpio_cfg(void)
  474. {
  475. /* 设置相应的GPIO用于CAMIF */
  476. *GPJCON = 0x2aaaaaa;
  477. /* 将数据位清0 */
  478. *GPJDAT = 0;
  479. /* 为了让信号稳定,使能上拉电阻 */
  480. *GPJUP = 0;
  481. }
  482. static void cmos_ov7740_camif_reset(void)
  483. {
  484. /* 传输方式为BT601 */
  485. *CISRCFMT |= (1<<31);
  486. /* 复位CAMIF控制器 */
  487. *CIGCTRL |= (1<<31);
  488. /* 稍微延迟一下 */
  489. mdelay(10);
  490. /* 使摄像头恢复正常状态 */
  491. *CIGCTRL &= ~(1<<31 mdelay="" 10="" static="" void="" cmos_ov7740_clk_cfg="" void="" struct="" clk="" camif_clk="" struct="" clk="" camif_upll_clk="" camif="" clk_get="" soc="" source="" insight="" camif="" s3c2440="" camif_clk="clk_get(NULL," camif="" if="" camif_clk="" is_err="" camif_clk="" printk="" kern_info="" failed="" to="" get="" camif="" clock="" source="" n="" clk_enable="" camif_clk="" camclk="24MHz,设置外部摄像头的时钟" camif-upll="" usb="" soc="" usb="" soc="" camif_upll_clk="clk_get(NULL," camif-upll="" clk_set_rate="" camif_upll_clk="" 24000000="" mdelay="" 100="" :="" 1="" s3c2440="" camrst="" :0-="">1->0(0:表示正常工作的电平、1:表示复位电平)
  492. 但是,实验证明,该复位时序与我们的OV7740需要的复位时序(1->0->1)不符合。
  493. 2.因此,我们就应该结合OV7740的具体复位时序,来设置相应的寄存器。
  494. */
  495. static void cmos_ov7740_reset(void)
  496. {
  497. *CIGCTRL |= (1<<30);
  498. mdelay(30);
  499. *CIGCTRL &= ~(1<<30);
  500. mdelay(30);
  501. *CIGCTRL |= (1<<30);
  502. mdelay(30);
  503. }
  504. static void cmos_ov7740_init(void)
  505. {
  506. unsigned int mid;
  507. int i;
  508. /* 读
  509. * 0xa是摄像头模块的厂家id的高8位,0xb是厂家id的低8位
  510. * i2c_smbus_read_byte_data函数是内核提供的函数,也可是使用i2c_transfer函数
  511. */
  512. mid = i2c_smbus_read_byte_data(cmos_ov7740_client, 0x0a)<<8;
  513. mid |= i2c_smbus_read_byte_data(cmos_ov7740_client, 0x0b);
  514. printk("manufacture ID = 0x%4x\n", mid);
  515. /* 写
  516. * 初始化摄像头模块
  517. */
  518. for(i = 0; i < OV7740_INIT_REGS_SIZE; i++)
  519. {
  520. i2c_smbus_write_byte_data(cmos_ov7740_client, ov7740_setting_30fps_VGA_640_480[i].regaddr, ov7740_setting_30fps_VGA_640_480[i].value);
  521. mdelay(2);
  522. }
  523. }
  524. static int __devinit cmos_ov7740_probe(struct i2c_client *client,
  525. const struct i2c_device_id *id)
  526. {
  527. printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
  528. /* 2.3 硬件相关 */
  529. /* 2.3.1 映射相应的寄存器 */
  530. GPJCON = ioremap(0x560000d0, 4);
  531. GPJDAT = ioremap(0x560000d4, 4);
  532. /* 上拉电阻相关的寄存器 */
  533. GPJUP = ioremap(0x560000d8, 4);
  534. /* 以下是摄像头控制器的寄存器 */
  535. CISRCFMT = ioremap(0x4F000000, 4);
  536. CIWDOFST = ioremap(0x4F000004, 4);
  537. CIGCTRL = ioremap(0x4F000008, 4);
  538. CIPRCLRSA1 = ioremap(0x4F00006C, 4);
  539. CIPRCLRSA2 = ioremap(0x4F000070, 4);
  540. CIPRCLRSA3 = ioremap(0x4F000074, 4);
  541. CIPRCLRSA4 = ioremap(0x4F000078, 4);
  542. CIPRTRGFMT = ioremap(0x4F00007C, 4);
  543. CIPRCTRL = ioremap(0x4F000080, 4);
  544. CIPRSCPRERATIO = ioremap(0x4F000084, 4);
  545. CIPRSCPREDST = ioremap(0x4F000088, 4);
  546. CIPRSCCTRL = ioremap(0x4F00008C, 4);
  547. CIPRTAREA = ioremap(0x4F000090, 4);
  548. CIIMGCPT = ioremap(0x4F0000A0, 4);
  549. SRCPND = ioremap(0X4A000000, 4);
  550. INTPND = ioremap(0X4A000010, 4);
  551. SUBSRCPND = ioremap(0X4A000018, 4);
  552. /* 2.3.2 设置相应的GPIO用于CAMIF */
  553. cmos_ov7740_gpio_cfg();
  554. /* 2.3.3 复位一下CAMIF控制器
  555. * 接下来要操作摄像头控制器,因此要先复位一下控制器
  556. */
  557. cmos_ov7740_camif_reset();
  558. /* 2.3.4 设置、使能时钟(使能HCLK用于摄像头控制器、使能并设置CAMCLK = 24MHz用于外部摄像头) */
  559. cmos_ov7740_clk_cfg();
  560. /* 2.3.5 复位一下摄像头模块 */
  561. cmos_ov7740_reset();
  562. /* 2.3.6 通过IIC总线,初始化摄像头模块 */
  563. cmos_ov7740_client = client;
  564. cmos_ov7740_init();
  565. /* 2.3.7 注册中断
  566. * IRQF_DISABLED - keep irqs disabled when calling the action handler
  567. */
  568. if (request_irq(IRQ_S3C2440_CAM_C, cmos_ov7740_camif_irq_c, IRQF_DISABLED , "CAM_C", NULL))
  569. printk("%s:request_irq failed\n", __func__);
  570. if (request_irq(IRQ_S3C2440_CAM_P, cmos_ov7740_camif_irq_p, IRQF_DISABLED , "CAM_P", NULL))
  571. printk("%s:request_irq failed\n", __func__);
  572. /* 3. 注册 ,以下信息是通过追踪源代码得到的结论
  573. * 参数-1表示分配第一个可用的号
  574. * which device node number (0 == /dev/video0, 1 == /dev/video1, ...
  575. *            -1 == first free)
  576. */
  577. if(video_register_device(&cmos_ov7740_vdev, VFL_TYPE_GRABBER, -1))
  578. {
  579. printk("unable to register video device\n");
  580. }
  581. return 0;
  582. }
  583. static int __devexit cmos_ov7740_remove(struct i2c_client *client)
  584. {
  585. printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
  586. iounmap(GPJCON);
  587. iounmap(GPJDAT);
  588. iounmap(GPJUP);
  589. iounmap(CISRCFMT);
  590. iounmap(CIWDOFST);
  591. iounmap(CIGCTRL);
  592. iounmap(CIPRCLRSA1);
  593. iounmap(CIPRCLRSA2);
  594. iounmap(CIPRCLRSA3);
  595. iounmap(CIPRCLRSA4);
  596. iounmap(CIPRTRGFMT);
  597. iounmap(CIPRCTRL);
  598. iounmap(CIPRSCPRERATIO);
  599. iounmap(CIPRSCPREDST);
  600. iounmap(CIPRSCCTRL);
  601. iounmap(CIPRTAREA);
  602. iounmap(CIIMGCPT);
  603. iounmap(SRCPND);
  604. iounmap(INTPND);
  605. iounmap(SUBSRCPND);
  606. free_irq(IRQ_S3C2440_CAM_C, NULL);
  607. free_irq(IRQ_S3C2440_CAM_P, NULL);
  608. video_unregister_device(&cmos_ov7740_vdev);
  609. return 0;
  610. }
  611. static const struct i2c_device_id cmos_ov7740_id_table[] = {
  612. { "cmos_ov7740", 0 },
  613. {}
  614. };
  615. /* 1.1. 分配、设置一个i2c_driver */
  616. static struct i2c_driver cmos_ov7740_driver = {
  617. .driver = {
  618. /* 用于和iic设备进行匹配,必须和iic_dev中的名字一样,否则匹配不成功 */
  619. .name   = "cmos_ov7740",
  620. .owner  = THIS_MODULE,
  621. },
  622. .probe      = cmos_ov7740_probe,
  623. .remove     = __devexit_p(cmos_ov7740_remove),
  624. .id_table   = cmos_ov7740_id_table,
  625. };
  626. static int cmos_ov7740_drv_init(void)
  627. {
  628. /* 1.2.注册 */
  629. i2c_add_driver(&cmos_ov7740_driver);
  630. return 0;
  631. }
  632. static void cmos_ov7740_drv_exit(void)
  633. {
  634. i2c_del_driver(&cmos_ov7740_driver);
  635. }
  636. module_init(cmos_ov7740_drv_init);
  637. module_exit(cmos_ov7740_drv_exit);
  638. MODULE_LICENSE("GPL");

coms_ov7740驱动相关推荐

  1. 嵌入式Linux设备驱动程序:在运行时读取驱动程序状态

    嵌入式Linux设备驱动程序:在运行时读取驱动程序状态 Embedded Linux device drivers: Reading driver state at runtime 在运行时了解驱动程 ...

  2. 0、Spring 注解驱动开发

    0.Spring注解驱动开发 0.1 简介 <Spring注解驱动开发>是一套帮助我们深入了解Spring原理机制的教程: 现今SpringBoot.SpringCloud技术非常火热,作 ...

  3. 安装 Python MySQL 驱动(mysql-connector-python、MySQL-python)

    1. 安装 由于 MySQL 服务器以独立的进程运行,并通过网络对外服务,所以,需要支持 Python 的MySQL 驱动来连接到 MySQL 服务器. 目前,有两个MySQL驱动: mysql-co ...

  4. Linux驱动框架之framebuffer驱动框架

    1.什么是framebuffer? (1)framebuffer帧缓冲(一屏幕数据)(简称fb)是linux内核中虚拟出的一个设备,framebuffer向应用层提供一个统一标准接口的显示设备.帧缓冲 ...

  5. [Ubuntu] 安装/卸载 声卡驱动

    卸载 sudo apt-get --purge remove linux-sound-base alsa-base alsa-utils 安装 sudo apt-get install linux-s ...

  6. pci串口驱动安装失败_TSC TTP-243E Pluse装LTP并口驱动无法安装

    一.前言描述 接到客户报修说,电脑无法开机,即到哥上门维修立即安排人员到现场查看原因,到现场后,查看的确是电脑系统问题,重新安装系统,安装完成系统后,发现打印机无法使用.这个打印机型号是TSC TTP ...

  7. linux mipi驱动分析_寒武纪社招内推数字IC设计、DSI驱动、软件架构、产品经理、芯片架构、工具链开发、深度学习、FAE工程师...

    点击上方蓝字关注我吧! 为什么内推更靠谱?内推是基于人脉关系链的推荐,其背后有一定的信用背书,靠谱的人推荐的人相对也会比较靠谱,所以企业一般职位都是从内部开始分享的,相较于自己海投简历,内推的效率和成 ...

  8. i7 7700hq安装Linux,黑苹果安装火影金刚4K VULCAN JinGang GTX i7-7700HQ 独显驱动 Hackintosh...

    严格来说,这不是一台笔记本,更像是一台移动一体机.火影金刚4K,独显GTX 1060,核显HD 630,在BIOS里是可以屏蔽核显的.所以这一次安装黑苹果时,直接当是一台IMAC来安装,连机型都是选择 ...

  9. java获取达梦数据库_Java连接达梦数据库驱动dm_jdbc

    [实例简介] Java连接达梦数据库驱动dm_jdbc: dm_jdbc\com.dameng.floader.jar dm_jdbc\com.dameng.impexp.jar dm_jdbc\Dm ...

最新文章

  1. luoguP3387 【模板】缩点
  2. 解决Ubuntu,su: 认证失败
  3. 后端:50 个 经典 Spring 面试题,值得收藏!
  4. 洛谷 P3102 [USACO14FEB]秘密代码Secret Code
  5. [摘自MSDN] ASP.Net2.0学习 [2] 主题 1 :ASP.NET 主题和外观概述
  6. 在线等差数列项生成器
  7. Python数据分析师特训营84节
  8. it试用评估_it试用期员工自我评价
  9. Hazelcast IMDG参考中文版手册-第七章-分布式数据结构
  10. 程序员github头像_给新程序员的5个GitHub技巧
  11. Mac下的Adobe卸载后无法重装
  12. 【SeedLab】ARP Cache Poisoning Attack Lab
  13. 2017年6月大学英语六级真题(第一套)阅读理解(一)(每日一摸)
  14. 前端人脸识别解决方案
  15. 单片机百科知识点---杂七杂八系列
  16. 分享proj4js中经纬度和兰伯特投影的转换代码
  17. 第三周作业——广州视源电子科技有限公司面试感想
  18. 浏览器 下载文件/课件
  19. 如何建立自己的知识仓库?
  20. 聊一聊直播利器,连麦互动背后的混流方案:到底该怎么混?

热门文章

  1. 亚马逊AWS与凯捷、富通云腾、神州泰岳达成战略合作
  2. Caused by: java.lang.ClassNotFoundException: net.sf.ezmorph.Morpher
  3. go第三方日志系统-seelog-Basic sections
  4. Java / JavaScript 用正则表达式去匹配代码中的单行注释,用以查找或删除注释。
  5. Eclipse显示空白符,如空格、制表符、换行符等
  6. Android变脸幕后的魔法师:各巨头…
  7. java分布式库存系统_这个是真的厉害,高并发场景下的订单和库存处理方案,讲的很详细了!...
  8. 态势丨黑客侵扰加剧,靶场为网络安全架设“防御盾”
  9. go strings包_Go中的Strings包简介
  10. word2vec Parameter Learning Explained(Hierarchical Softmax,Negative Sampling)