三星平台的capture设备节点是/dev/fimc0,但是平台可能存在多个摄像头,更特殊的情况是一个video ADC芯片外接多个模拟摄像头,这几天研究了下如何选择capture的摄像头,做个总结。
先对摄像头做个分类,1. CMM CameraCompact Module 摄像头模组; 2. VIDEO ADC + 模拟摄像头
视频源的选择是通过VIDIOC_S_INPUT来实现的,这里的视频源既可以是上面第一类的CMM,也可以是第二类的ADC加一个模拟摄像头,video ADC挂几个模拟摄像头就对应几个视频源。
三星FIMC框架为每一个视频源定义了一个结构,以mach-smdkc110.c为例。
- 1901 static struct s3c_platform_fimc fimc_plat_lsi = {
- 1902 .srclk_name = "mout_mpll",
- 1903 .clk_name = "sclk_fimc",
- 1904 .lclk_name = "sclk_fimc_lclk",
- 1905 .clk_rate = 166750000,
- 1906 #if defined(CONFIG_VIDEO_S5K4EA)
- 1907 .default_cam = CAMERA_CSI_C,
- 1908 #else
- 1909 #ifdef CAM_ITU_CH_A
- 1910 .default_cam = CAMERA_PAR_A,
- 1911 #else
- 1912 .default_cam = CAMERA_PAR_B,
- 1913 #endif
- 1914 #endif
- 1915 .camera = {
- 1916 #ifdef CONFIG_VIDEO_S5K4ECGX
- 1917 &s5k4ecgx,
- 1918 #endif
- 1919 #ifdef CONFIG_VIDEO_S5KA3DFX
- 1920 &s5ka3dfx,
- 1921 #endif
- 1922 #ifdef CONFIG_VIDEO_S5K4BA
- 1923 &s5k4ba,
- 1924 #endif
- 1925 #ifdef CONFIG_VIDEO_S5K4EA
- 1926 &s5k4ea,
- 1927 #endif
- 1928 #ifdef CONFIG_VIDEO_CAM8000
- 1929 &cam8000,
- 1930 #endif
- 1931 #ifdef CONFIG_VIDEO_TW9912
- 1932 &tw9912_1,
- 1933 &tw9912_2,
- 1934 #endif
- 1935 },
- 1936 .hw_ver = 0x43,
- 1937 };
1918 ~ 1938 .camera成员定义了系统可能的视频源,其中s5k4ecgx, s5ka3dfx, s5k4ba, s5k4ea是四个CMM模组, cam8000原生代码的Video ADC,tw9912_1和tw9912_2是我定义的两个camera 视频源,为什么一个TW9912 Video ADC要定义成两个,这是因为在我的项目中TW9912存在两路输入,一个是CVBS另外一个是YPbPr,我认为他们和 TW9912组成了两个camera 视频源。
下面代码演示了如何区分这两个camera通道
- 1831 static struct s3c_platform_camera tw9912_1 = {
- 1832 .id = CAMERA_PAR_A,
- 1833 .type = CAM_TYPE_ITU,
- 1834 .fmt = ITU_656_YCBCR422_8BIT,
- 1835 .order422 = CAM_ORDER422_8BIT_CBYCRY,
- 1836 .i2c_busnum = 1,
- 1837 .info = &tw9912_1_i2c_info,
- 1838 .pixelformat = V4L2_PIX_FMT_YUYV,
- 1839 .srclk_name = "mout_mpll",
- 1840 .clk_name = "sclk_cam0",
- 1841 .clk_rate = 44000000,
- 1842 .line_length = 1440,
- 1843 .width = 720,
- 1844 .height = 576,
- 1845 .window = {
- 1846 .left = 0,
- 1847 .top = 0,
- 1848 .width = 720,
- 1849 .height = 576,
- 1850 },
- 1851
- 1852 /* Polarity */
- 1853 .inv_pclk = 0,
- 1854 .inv_vsync = 0,
- 1855 .inv_href = 0,
- 1856 .inv_hsync = 0,
- 1857
- 1858 .initialized = 0,
- 1859 /*
- 1860 * It is too late to call camera sensor poweron in fimc_camera_init
- 1861 * so we move power function to board init
- 1862 */
- 1863 .cam_power = NULL,
- 1864 };
- 1866 static struct s3c_platform_camera tw9912_2 = {
- 1867 .id = CAMERA_PAR_A,
- 1868 .type = CAM_TYPE_ITU,
- 1869 .fmt = ITU_656_YCBCR422_8BIT,
- 1870 .order422 = CAM_ORDER422_8BIT_CBYCRY,
- 1871 .i2c_busnum = 1,
- 1872 .info = &tw9912_2_i2c_info,
- 1873 .pixelformat = V4L2_PIX_FMT_YUYV,
- 1874 .srclk_name = "mout_mpll",
- 1875 .clk_name = "sclk_cam0",
- 1876 .clk_rate = 44000000,
- 1877 .line_length = 1440,
- 1878 .width = 720,
- 1879 .height = 576,
- 1880 .window = {
- 1881 .left = 0,
- 1882 .top = 0,
- 1883 .width = 720,
- 1884 .height = 576,
- 1885 },
- 1886
- 1887 /* Polarity */
- 1888 .inv_pclk = 0,
- 1889 .inv_vsync = 0,
- 1890 .inv_href = 0,
- 1891 .inv_hsync = 0,
- 1892
- 1893 .initialized = 0,
- 1894 /*
- 1895 * It is too late to call camera sensor poweron in fimc_camera_init
- 1896 * so we move power function to board init
- 1897 */
- 1898 .cam_power = NULL,
- 1899 };
他们唯一不同的地方在于.info成员,分别为tw9912_1_i2c_info 和tw9912_2_i2c_info
- 1796 static struct tw9912_platform_data tw9912_1_plat = {
- 1797 .default_width = 720,
- 1798 .default_height = 576,
- 1799 .pixelformat = V4L2_PIX_FMT_YUYV,
- 1800 .ifsel = 0,
- 1801 .ysel = 0,
- 1802 .csel = 0,
- 1803 .vsel = 0,
- 1804 .cam_reset = tw9912_reset,
- 1805 };
- 1806
- 1807 static struct tw9912_platform_data tw9912_2_plat = {
- 1808 .default_width = 720,
- 1809 .default_height = 576,
- 1810 .pixelformat = V4L2_PIX_FMT_YUYV,
- 1811 .ifsel = 0x20,
- 1812 .ysel = 0x08,
- 1813 .csel = 0x2,
- 1814 .vsel = 0,
- 1815 .cam_reset = tw9912_reset,
- 1816 };
- 1817
- 1818 static struct i2c_board_info tw9912_1_i2c_info = {
- 1819 I2C_BOARD_INFO("tw9912", (0x88 >> 1)),
- 1820 .platform_data = &tw9912_1_plat,
- 1821 };
- 1822
- 1823 static struct i2c_board_info tw9912_2_i2c_info = {
- 1824 I2C_BOARD_INFO("tw9912", (0x88 >> 1)),
- 1825 .platform_data = &tw9912_2_plat,
- 1826 };
i2c_board_info不同的地方是ifsel, ysel, csel, vsel成员,这几个成员对应着tw9912 0x02寄存器(input format)寄存器位
这是tw9912_1和tw9912_2唯一有差别的地方。
当上层调用VIDIOC_S_INPUT时,调用路径如下
fimc_s_input->fimc_configure_subdev->v4l2_i2c_new_subdev_board->v4l2_subdev_call(sd, core, s_config, info->irq, info->platform_data)->
v4l2_subdev_call(sd, core, s_config, info->irq, info->platform_data)调用sd->ops->core->s_config, 这个函数需要在tw9912驱动中实现,info->platform_data就是在mach-smkvc110.c中定义的tw9912_1_i2c_info或者tw9912_2_i2c_info,因此在s_config实现中就可以根据i2c_board_info.platform_data中的ifsel, ysel, csel, vsel来设置tw9912的输入
总结一下:
1. video ADC芯片驱动要通过s_config来实现视频源的切换
2. 为video ADC的每个输入创建一个s3c_platform_camera 结构,通过i2c_board_info.platform_data来体现输入的差异性