isif.c 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144
  1. /*
  2. * Copyright (C) 2008-2009 Texas Instruments Inc
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, write to the Free Software
  16. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  17. *
  18. * Image Sensor Interface (ISIF) driver
  19. *
  20. * This driver is for configuring the ISIF IP available on DM365 or any other
  21. * TI SoCs. This is used for capturing yuv or bayer video or image data
  22. * from a decoder or sensor. This IP is similar to the CCDC IP on DM355
  23. * and DM6446, but with enhanced or additional ip blocks. The driver
  24. * configures the ISIF upon commands from the vpfe bridge driver through
  25. * ccdc_hw_device interface.
  26. *
  27. * TODO: 1) Raw bayer parameter settings and bayer capture
  28. * 2) Add support for control ioctl
  29. */
  30. #include <linux/delay.h>
  31. #include <linux/platform_device.h>
  32. #include <linux/uaccess.h>
  33. #include <linux/io.h>
  34. #include <linux/videodev2.h>
  35. #include <linux/err.h>
  36. #include <linux/module.h>
  37. #include <mach/mux.h>
  38. #include <media/davinci/isif.h>
  39. #include <media/davinci/vpss.h>
  40. #include "isif_regs.h"
  41. #include "ccdc_hw_device.h"
  42. /* Defaults for module configuration parameters */
  43. static struct isif_config_params_raw isif_config_defaults = {
  44. .linearize = {
  45. .en = 0,
  46. .corr_shft = ISIF_NO_SHIFT,
  47. .scale_fact = {1, 0},
  48. },
  49. .df_csc = {
  50. .df_or_csc = 0,
  51. .csc = {
  52. .en = 0,
  53. },
  54. },
  55. .dfc = {
  56. .en = 0,
  57. },
  58. .bclamp = {
  59. .en = 0,
  60. },
  61. .gain_offset = {
  62. .gain = {
  63. .r_ye = {1, 0},
  64. .gr_cy = {1, 0},
  65. .gb_g = {1, 0},
  66. .b_mg = {1, 0},
  67. },
  68. },
  69. .culling = {
  70. .hcpat_odd = 0xff,
  71. .hcpat_even = 0xff,
  72. .vcpat = 0xff,
  73. },
  74. .compress = {
  75. .alg = ISIF_ALAW,
  76. },
  77. };
  78. /* ISIF operation configuration */
  79. static struct isif_oper_config {
  80. struct device *dev;
  81. enum vpfe_hw_if_type if_type;
  82. struct isif_ycbcr_config ycbcr;
  83. struct isif_params_raw bayer;
  84. enum isif_data_pack data_pack;
  85. /* ISIF base address */
  86. void __iomem *base_addr;
  87. /* ISIF Linear Table 0 */
  88. void __iomem *linear_tbl0_addr;
  89. /* ISIF Linear Table 1 */
  90. void __iomem *linear_tbl1_addr;
  91. } isif_cfg = {
  92. .ycbcr = {
  93. .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT,
  94. .frm_fmt = CCDC_FRMFMT_INTERLACED,
  95. .win = ISIF_WIN_NTSC,
  96. .fid_pol = VPFE_PINPOL_POSITIVE,
  97. .vd_pol = VPFE_PINPOL_POSITIVE,
  98. .hd_pol = VPFE_PINPOL_POSITIVE,
  99. .pix_order = CCDC_PIXORDER_CBYCRY,
  100. .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED,
  101. },
  102. .bayer = {
  103. .pix_fmt = CCDC_PIXFMT_RAW,
  104. .frm_fmt = CCDC_FRMFMT_PROGRESSIVE,
  105. .win = ISIF_WIN_VGA,
  106. .fid_pol = VPFE_PINPOL_POSITIVE,
  107. .vd_pol = VPFE_PINPOL_POSITIVE,
  108. .hd_pol = VPFE_PINPOL_POSITIVE,
  109. .gain = {
  110. .r_ye = {1, 0},
  111. .gr_cy = {1, 0},
  112. .gb_g = {1, 0},
  113. .b_mg = {1, 0},
  114. },
  115. .cfa_pat = ISIF_CFA_PAT_MOSAIC,
  116. .data_msb = ISIF_BIT_MSB_11,
  117. .config_params = {
  118. .data_shift = ISIF_NO_SHIFT,
  119. .col_pat_field0 = {
  120. .olop = ISIF_GREEN_BLUE,
  121. .olep = ISIF_BLUE,
  122. .elop = ISIF_RED,
  123. .elep = ISIF_GREEN_RED,
  124. },
  125. .col_pat_field1 = {
  126. .olop = ISIF_GREEN_BLUE,
  127. .olep = ISIF_BLUE,
  128. .elop = ISIF_RED,
  129. .elep = ISIF_GREEN_RED,
  130. },
  131. .test_pat_gen = 0,
  132. },
  133. },
  134. .data_pack = ISIF_DATA_PACK8,
  135. };
  136. /* Raw Bayer formats */
  137. static const u32 isif_raw_bayer_pix_formats[] = {
  138. V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16};
  139. /* Raw YUV formats */
  140. static const u32 isif_raw_yuv_pix_formats[] = {
  141. V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV};
  142. /* register access routines */
  143. static inline u32 regr(u32 offset)
  144. {
  145. return __raw_readl(isif_cfg.base_addr + offset);
  146. }
  147. static inline void regw(u32 val, u32 offset)
  148. {
  149. __raw_writel(val, isif_cfg.base_addr + offset);
  150. }
  151. /* reg_modify() - read, modify and write register */
  152. static inline u32 reg_modify(u32 mask, u32 val, u32 offset)
  153. {
  154. u32 new_val = (regr(offset) & ~mask) | (val & mask);
  155. regw(new_val, offset);
  156. return new_val;
  157. }
  158. static inline void regw_lin_tbl(u32 val, u32 offset, int i)
  159. {
  160. if (!i)
  161. __raw_writel(val, isif_cfg.linear_tbl0_addr + offset);
  162. else
  163. __raw_writel(val, isif_cfg.linear_tbl1_addr + offset);
  164. }
  165. static void isif_disable_all_modules(void)
  166. {
  167. /* disable BC */
  168. regw(0, CLAMPCFG);
  169. /* disable vdfc */
  170. regw(0, DFCCTL);
  171. /* disable CSC */
  172. regw(0, CSCCTL);
  173. /* disable linearization */
  174. regw(0, LINCFG0);
  175. /* disable other modules here as they are supported */
  176. }
  177. static void isif_enable(int en)
  178. {
  179. if (!en) {
  180. /* Before disable isif, disable all ISIF modules */
  181. isif_disable_all_modules();
  182. /*
  183. * wait for next VD. Assume lowest scan rate is 12 Hz. So
  184. * 100 msec delay is good enough
  185. */
  186. msleep(100);
  187. }
  188. reg_modify(ISIF_SYNCEN_VDHDEN_MASK, en, SYNCEN);
  189. }
  190. static void isif_enable_output_to_sdram(int en)
  191. {
  192. reg_modify(ISIF_SYNCEN_WEN_MASK, en << ISIF_SYNCEN_WEN_SHIFT, SYNCEN);
  193. }
  194. static void isif_config_culling(struct isif_cul *cul)
  195. {
  196. u32 val;
  197. /* Horizontal pattern */
  198. val = (cul->hcpat_even << CULL_PAT_EVEN_LINE_SHIFT) | cul->hcpat_odd;
  199. regw(val, CULH);
  200. /* vertical pattern */
  201. regw(cul->vcpat, CULV);
  202. /* LPF */
  203. reg_modify(ISIF_LPF_MASK << ISIF_LPF_SHIFT,
  204. cul->en_lpf << ISIF_LPF_SHIFT, MODESET);
  205. }
  206. static void isif_config_gain_offset(void)
  207. {
  208. struct isif_gain_offsets_adj *gain_off_p =
  209. &isif_cfg.bayer.config_params.gain_offset;
  210. u32 val;
  211. val = (!!gain_off_p->gain_sdram_en << GAIN_SDRAM_EN_SHIFT) |
  212. (!!gain_off_p->gain_ipipe_en << GAIN_IPIPE_EN_SHIFT) |
  213. (!!gain_off_p->gain_h3a_en << GAIN_H3A_EN_SHIFT) |
  214. (!!gain_off_p->offset_sdram_en << OFST_SDRAM_EN_SHIFT) |
  215. (!!gain_off_p->offset_ipipe_en << OFST_IPIPE_EN_SHIFT) |
  216. (!!gain_off_p->offset_h3a_en << OFST_H3A_EN_SHIFT);
  217. reg_modify(GAIN_OFFSET_EN_MASK, val, CGAMMAWD);
  218. val = (gain_off_p->gain.r_ye.integer << GAIN_INTEGER_SHIFT) |
  219. gain_off_p->gain.r_ye.decimal;
  220. regw(val, CRGAIN);
  221. val = (gain_off_p->gain.gr_cy.integer << GAIN_INTEGER_SHIFT) |
  222. gain_off_p->gain.gr_cy.decimal;
  223. regw(val, CGRGAIN);
  224. val = (gain_off_p->gain.gb_g.integer << GAIN_INTEGER_SHIFT) |
  225. gain_off_p->gain.gb_g.decimal;
  226. regw(val, CGBGAIN);
  227. val = (gain_off_p->gain.b_mg.integer << GAIN_INTEGER_SHIFT) |
  228. gain_off_p->gain.b_mg.decimal;
  229. regw(val, CBGAIN);
  230. regw(gain_off_p->offset, COFSTA);
  231. }
  232. static void isif_restore_defaults(void)
  233. {
  234. enum vpss_ccdc_source_sel source = VPSS_CCDCIN;
  235. dev_dbg(isif_cfg.dev, "\nstarting isif_restore_defaults...");
  236. isif_cfg.bayer.config_params = isif_config_defaults;
  237. /* Enable clock to ISIF, IPIPEIF and BL */
  238. vpss_enable_clock(VPSS_CCDC_CLOCK, 1);
  239. vpss_enable_clock(VPSS_IPIPEIF_CLOCK, 1);
  240. vpss_enable_clock(VPSS_BL_CLOCK, 1);
  241. /* Set default offset and gain */
  242. isif_config_gain_offset();
  243. vpss_select_ccdc_source(source);
  244. dev_dbg(isif_cfg.dev, "\nEnd of isif_restore_defaults...");
  245. }
  246. static int isif_open(struct device *device)
  247. {
  248. isif_restore_defaults();
  249. return 0;
  250. }
  251. /* This function will configure the window size to be capture in ISIF reg */
  252. static void isif_setwin(struct v4l2_rect *image_win,
  253. enum ccdc_frmfmt frm_fmt, int ppc)
  254. {
  255. int horz_start, horz_nr_pixels;
  256. int vert_start, vert_nr_lines;
  257. int mid_img = 0;
  258. dev_dbg(isif_cfg.dev, "\nStarting isif_setwin...");
  259. /*
  260. * ppc - per pixel count. indicates how many pixels per cell
  261. * output to SDRAM. example, for ycbcr, it is one y and one c, so 2.
  262. * raw capture this is 1
  263. */
  264. horz_start = image_win->left << (ppc - 1);
  265. horz_nr_pixels = ((image_win->width) << (ppc - 1)) - 1;
  266. /* Writing the horizontal info into the registers */
  267. regw(horz_start & START_PX_HOR_MASK, SPH);
  268. regw(horz_nr_pixels & NUM_PX_HOR_MASK, LNH);
  269. vert_start = image_win->top;
  270. if (frm_fmt == CCDC_FRMFMT_INTERLACED) {
  271. vert_nr_lines = (image_win->height >> 1) - 1;
  272. vert_start >>= 1;
  273. /* To account for VD since line 0 doesn't have any data */
  274. vert_start += 1;
  275. } else {
  276. /* To account for VD since line 0 doesn't have any data */
  277. vert_start += 1;
  278. vert_nr_lines = image_win->height - 1;
  279. /* configure VDINT0 and VDINT1 */
  280. mid_img = vert_start + (image_win->height / 2);
  281. regw(mid_img, VDINT1);
  282. }
  283. regw(0, VDINT0);
  284. regw(vert_start & START_VER_ONE_MASK, SLV0);
  285. regw(vert_start & START_VER_TWO_MASK, SLV1);
  286. regw(vert_nr_lines & NUM_LINES_VER, LNV);
  287. }
  288. static void isif_config_bclamp(struct isif_black_clamp *bc)
  289. {
  290. u32 val;
  291. /*
  292. * DC Offset is always added to image data irrespective of bc enable
  293. * status
  294. */
  295. regw(bc->dc_offset, CLDCOFST);
  296. if (bc->en) {
  297. val = bc->bc_mode_color << ISIF_BC_MODE_COLOR_SHIFT;
  298. /* Enable BC and horizontal clamp caculation paramaters */
  299. val = val | 1 | (bc->horz.mode << ISIF_HORZ_BC_MODE_SHIFT);
  300. regw(val, CLAMPCFG);
  301. if (bc->horz.mode != ISIF_HORZ_BC_DISABLE) {
  302. /*
  303. * Window count for calculation
  304. * Base window selection
  305. * pixel limit
  306. * Horizontal size of window
  307. * vertical size of the window
  308. * Horizontal start position of the window
  309. * Vertical start position of the window
  310. */
  311. val = bc->horz.win_count_calc |
  312. ((!!bc->horz.base_win_sel_calc) <<
  313. ISIF_HORZ_BC_WIN_SEL_SHIFT) |
  314. ((!!bc->horz.clamp_pix_limit) <<
  315. ISIF_HORZ_BC_PIX_LIMIT_SHIFT) |
  316. (bc->horz.win_h_sz_calc <<
  317. ISIF_HORZ_BC_WIN_H_SIZE_SHIFT) |
  318. (bc->horz.win_v_sz_calc <<
  319. ISIF_HORZ_BC_WIN_V_SIZE_SHIFT);
  320. regw(val, CLHWIN0);
  321. regw(bc->horz.win_start_h_calc, CLHWIN1);
  322. regw(bc->horz.win_start_v_calc, CLHWIN2);
  323. }
  324. /* vertical clamp caculation paramaters */
  325. /* Reset clamp value sel for previous line */
  326. val |=
  327. (bc->vert.reset_val_sel << ISIF_VERT_BC_RST_VAL_SEL_SHIFT) |
  328. (bc->vert.line_ave_coef << ISIF_VERT_BC_LINE_AVE_COEF_SHIFT);
  329. regw(val, CLVWIN0);
  330. /* Optical Black horizontal start position */
  331. regw(bc->vert.ob_start_h, CLVWIN1);
  332. /* Optical Black vertical start position */
  333. regw(bc->vert.ob_start_v, CLVWIN2);
  334. /* Optical Black vertical size for calculation */
  335. regw(bc->vert.ob_v_sz_calc, CLVWIN3);
  336. /* Vertical start position for BC subtraction */
  337. regw(bc->vert_start_sub, CLSV);
  338. }
  339. }
  340. static void isif_config_linearization(struct isif_linearize *linearize)
  341. {
  342. u32 val, i;
  343. if (!linearize->en) {
  344. regw(0, LINCFG0);
  345. return;
  346. }
  347. /* shift value for correction & enable linearization (set lsb) */
  348. val = (linearize->corr_shft << ISIF_LIN_CORRSFT_SHIFT) | 1;
  349. regw(val, LINCFG0);
  350. /* Scale factor */
  351. val = ((!!linearize->scale_fact.integer) <<
  352. ISIF_LIN_SCALE_FACT_INTEG_SHIFT) |
  353. linearize->scale_fact.decimal;
  354. regw(val, LINCFG1);
  355. for (i = 0; i < ISIF_LINEAR_TAB_SIZE; i++) {
  356. if (i % 2)
  357. regw_lin_tbl(linearize->table[i], ((i >> 1) << 2), 1);
  358. else
  359. regw_lin_tbl(linearize->table[i], ((i >> 1) << 2), 0);
  360. }
  361. }
  362. static int isif_config_dfc(struct isif_dfc *vdfc)
  363. {
  364. /* initialize retries to loop for max ~ 250 usec */
  365. u32 val, count, retries = loops_per_jiffy / (4000/HZ);
  366. int i;
  367. if (!vdfc->en)
  368. return 0;
  369. /* Correction mode */
  370. val = (vdfc->corr_mode << ISIF_VDFC_CORR_MOD_SHIFT);
  371. /* Correct whole line or partial */
  372. if (vdfc->corr_whole_line)
  373. val |= 1 << ISIF_VDFC_CORR_WHOLE_LN_SHIFT;
  374. /* level shift value */
  375. val |= vdfc->def_level_shift << ISIF_VDFC_LEVEL_SHFT_SHIFT;
  376. regw(val, DFCCTL);
  377. /* Defect saturation level */
  378. regw(vdfc->def_sat_level, VDFSATLV);
  379. regw(vdfc->table[0].pos_vert, DFCMEM0);
  380. regw(vdfc->table[0].pos_horz, DFCMEM1);
  381. if (vdfc->corr_mode == ISIF_VDFC_NORMAL ||
  382. vdfc->corr_mode == ISIF_VDFC_HORZ_INTERPOL_IF_SAT) {
  383. regw(vdfc->table[0].level_at_pos, DFCMEM2);
  384. regw(vdfc->table[0].level_up_pixels, DFCMEM3);
  385. regw(vdfc->table[0].level_low_pixels, DFCMEM4);
  386. }
  387. /* set DFCMARST and set DFCMWR */
  388. val = regr(DFCMEMCTL) | (1 << ISIF_DFCMEMCTL_DFCMARST_SHIFT) | 1;
  389. regw(val, DFCMEMCTL);
  390. count = retries;
  391. while (count && (regr(DFCMEMCTL) & 0x1))
  392. count--;
  393. if (!count) {
  394. dev_dbg(isif_cfg.dev, "defect table write timeout !!!\n");
  395. return -1;
  396. }
  397. for (i = 1; i < vdfc->num_vdefects; i++) {
  398. regw(vdfc->table[i].pos_vert, DFCMEM0);
  399. regw(vdfc->table[i].pos_horz, DFCMEM1);
  400. if (vdfc->corr_mode == ISIF_VDFC_NORMAL ||
  401. vdfc->corr_mode == ISIF_VDFC_HORZ_INTERPOL_IF_SAT) {
  402. regw(vdfc->table[i].level_at_pos, DFCMEM2);
  403. regw(vdfc->table[i].level_up_pixels, DFCMEM3);
  404. regw(vdfc->table[i].level_low_pixels, DFCMEM4);
  405. }
  406. val = regr(DFCMEMCTL);
  407. /* clear DFCMARST and set DFCMWR */
  408. val &= ~BIT(ISIF_DFCMEMCTL_DFCMARST_SHIFT);
  409. val |= 1;
  410. regw(val, DFCMEMCTL);
  411. count = retries;
  412. while (count && (regr(DFCMEMCTL) & 0x1))
  413. count--;
  414. if (!count) {
  415. dev_err(isif_cfg.dev,
  416. "defect table write timeout !!!\n");
  417. return -1;
  418. }
  419. }
  420. if (vdfc->num_vdefects < ISIF_VDFC_TABLE_SIZE) {
  421. /* Extra cycle needed */
  422. regw(0, DFCMEM0);
  423. regw(0x1FFF, DFCMEM1);
  424. regw(1, DFCMEMCTL);
  425. }
  426. /* enable VDFC */
  427. reg_modify((1 << ISIF_VDFC_EN_SHIFT), (1 << ISIF_VDFC_EN_SHIFT),
  428. DFCCTL);
  429. return 0;
  430. }
  431. static void isif_config_csc(struct isif_df_csc *df_csc)
  432. {
  433. u32 val1 = 0, val2 = 0, i;
  434. if (!df_csc->csc.en) {
  435. regw(0, CSCCTL);
  436. return;
  437. }
  438. for (i = 0; i < ISIF_CSC_NUM_COEFF; i++) {
  439. if ((i % 2) == 0) {
  440. /* CSCM - LSB */
  441. val1 = (df_csc->csc.coeff[i].integer <<
  442. ISIF_CSC_COEF_INTEG_SHIFT) |
  443. df_csc->csc.coeff[i].decimal;
  444. } else {
  445. /* CSCM - MSB */
  446. val2 = (df_csc->csc.coeff[i].integer <<
  447. ISIF_CSC_COEF_INTEG_SHIFT) |
  448. df_csc->csc.coeff[i].decimal;
  449. val2 <<= ISIF_CSCM_MSB_SHIFT;
  450. val2 |= val1;
  451. regw(val2, (CSCM0 + ((i - 1) << 1)));
  452. }
  453. }
  454. /* program the active area */
  455. regw(df_csc->start_pix, FMTSPH);
  456. /*
  457. * one extra pixel as required for CSC. Actually number of
  458. * pixel - 1 should be configured in this register. So we
  459. * need to subtract 1 before writing to FMTSPH, but we will
  460. * not do this since csc requires one extra pixel
  461. */
  462. regw(df_csc->num_pixels, FMTLNH);
  463. regw(df_csc->start_line, FMTSLV);
  464. /*
  465. * one extra line as required for CSC. See reason documented for
  466. * num_pixels
  467. */
  468. regw(df_csc->num_lines, FMTLNV);
  469. /* Enable CSC */
  470. regw(1, CSCCTL);
  471. }
  472. static int isif_config_raw(void)
  473. {
  474. struct isif_params_raw *params = &isif_cfg.bayer;
  475. struct isif_config_params_raw *module_params =
  476. &isif_cfg.bayer.config_params;
  477. struct vpss_pg_frame_size frame_size;
  478. struct vpss_sync_pol sync;
  479. u32 val;
  480. dev_dbg(isif_cfg.dev, "\nStarting isif_config_raw..\n");
  481. /*
  482. * Configure CCDCFG register:-
  483. * Set CCD Not to swap input since input is RAW data
  484. * Set FID detection function to Latch at V-Sync
  485. * Set WENLOG - isif valid area
  486. * Set TRGSEL
  487. * Set EXTRG
  488. * Packed to 8 or 16 bits
  489. */
  490. val = ISIF_YCINSWP_RAW | ISIF_CCDCFG_FIDMD_LATCH_VSYNC |
  491. ISIF_CCDCFG_WENLOG_AND | ISIF_CCDCFG_TRGSEL_WEN |
  492. ISIF_CCDCFG_EXTRG_DISABLE | isif_cfg.data_pack;
  493. dev_dbg(isif_cfg.dev, "Writing 0x%x to ...CCDCFG \n", val);
  494. regw(val, CCDCFG);
  495. /*
  496. * Configure the vertical sync polarity(MODESET.VDPOL)
  497. * Configure the horizontal sync polarity (MODESET.HDPOL)
  498. * Configure frame id polarity (MODESET.FLDPOL)
  499. * Configure data polarity
  500. * Configure External WEN Selection
  501. * Configure frame format(progressive or interlace)
  502. * Configure pixel format (Input mode)
  503. * Configure the data shift
  504. */
  505. val = ISIF_VDHDOUT_INPUT | (params->vd_pol << ISIF_VD_POL_SHIFT) |
  506. (params->hd_pol << ISIF_HD_POL_SHIFT) |
  507. (params->fid_pol << ISIF_FID_POL_SHIFT) |
  508. (ISIF_DATAPOL_NORMAL << ISIF_DATAPOL_SHIFT) |
  509. (ISIF_EXWEN_DISABLE << ISIF_EXWEN_SHIFT) |
  510. (params->frm_fmt << ISIF_FRM_FMT_SHIFT) |
  511. (params->pix_fmt << ISIF_INPUT_SHIFT) |
  512. (params->config_params.data_shift << ISIF_DATASFT_SHIFT);
  513. regw(val, MODESET);
  514. dev_dbg(isif_cfg.dev, "Writing 0x%x to MODESET...\n", val);
  515. /*
  516. * Configure GAMMAWD register
  517. * CFA pattern setting
  518. */
  519. val = params->cfa_pat << ISIF_GAMMAWD_CFA_SHIFT;
  520. /* Gamma msb */
  521. if (module_params->compress.alg == ISIF_ALAW)
  522. val |= ISIF_ALAW_ENABLE;
  523. val |= (params->data_msb << ISIF_ALAW_GAMMA_WD_SHIFT);
  524. regw(val, CGAMMAWD);
  525. /* Configure DPCM compression settings */
  526. if (module_params->compress.alg == ISIF_DPCM) {
  527. val = BIT(ISIF_DPCM_EN_SHIFT) |
  528. (module_params->compress.pred <<
  529. ISIF_DPCM_PREDICTOR_SHIFT);
  530. }
  531. regw(val, MISC);
  532. /* Configure Gain & Offset */
  533. isif_config_gain_offset();
  534. /* Configure Color pattern */
  535. val = (params->config_params.col_pat_field0.olop) |
  536. (params->config_params.col_pat_field0.olep << 2) |
  537. (params->config_params.col_pat_field0.elop << 4) |
  538. (params->config_params.col_pat_field0.elep << 6) |
  539. (params->config_params.col_pat_field1.olop << 8) |
  540. (params->config_params.col_pat_field1.olep << 10) |
  541. (params->config_params.col_pat_field1.elop << 12) |
  542. (params->config_params.col_pat_field1.elep << 14);
  543. regw(val, CCOLP);
  544. dev_dbg(isif_cfg.dev, "Writing %x to CCOLP ...\n", val);
  545. /* Configure HSIZE register */
  546. val = (!!params->horz_flip_en) << ISIF_HSIZE_FLIP_SHIFT;
  547. /* calculate line offset in 32 bytes based on pack value */
  548. if (isif_cfg.data_pack == ISIF_PACK_8BIT)
  549. val |= ((params->win.width + 31) >> 5);
  550. else if (isif_cfg.data_pack == ISIF_PACK_12BIT)
  551. val |= (((params->win.width +
  552. (params->win.width >> 2)) + 31) >> 5);
  553. else
  554. val |= (((params->win.width * 2) + 31) >> 5);
  555. regw(val, HSIZE);
  556. /* Configure SDOFST register */
  557. if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) {
  558. if (params->image_invert_en) {
  559. /* For interlace inverse mode */
  560. regw(0x4B6D, SDOFST);
  561. dev_dbg(isif_cfg.dev, "Writing 0x4B6D to SDOFST...\n");
  562. } else {
  563. /* For interlace non inverse mode */
  564. regw(0x0B6D, SDOFST);
  565. dev_dbg(isif_cfg.dev, "Writing 0x0B6D to SDOFST...\n");
  566. }
  567. } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) {
  568. if (params->image_invert_en) {
  569. /* For progressive inverse mode */
  570. regw(0x4000, SDOFST);
  571. dev_dbg(isif_cfg.dev, "Writing 0x4000 to SDOFST...\n");
  572. } else {
  573. /* For progressive non inverse mode */
  574. regw(0x0000, SDOFST);
  575. dev_dbg(isif_cfg.dev, "Writing 0x0000 to SDOFST...\n");
  576. }
  577. }
  578. /* Configure video window */
  579. isif_setwin(&params->win, params->frm_fmt, 1);
  580. /* Configure Black Clamp */
  581. isif_config_bclamp(&module_params->bclamp);
  582. /* Configure Vertical Defection Pixel Correction */
  583. if (isif_config_dfc(&module_params->dfc) < 0)
  584. return -EFAULT;
  585. if (!module_params->df_csc.df_or_csc)
  586. /* Configure Color Space Conversion */
  587. isif_config_csc(&module_params->df_csc);
  588. isif_config_linearization(&module_params->linearize);
  589. /* Configure Culling */
  590. isif_config_culling(&module_params->culling);
  591. /* Configure horizontal and vertical offsets(DFC,LSC,Gain) */
  592. regw(module_params->horz_offset, DATAHOFST);
  593. regw(module_params->vert_offset, DATAVOFST);
  594. /* Setup test pattern if enabled */
  595. if (params->config_params.test_pat_gen) {
  596. /* Use the HD/VD pol settings from user */
  597. sync.ccdpg_hdpol = params->hd_pol;
  598. sync.ccdpg_vdpol = params->vd_pol;
  599. dm365_vpss_set_sync_pol(sync);
  600. frame_size.hlpfr = isif_cfg.bayer.win.width;
  601. frame_size.pplen = isif_cfg.bayer.win.height;
  602. dm365_vpss_set_pg_frame_size(frame_size);
  603. vpss_select_ccdc_source(VPSS_PGLPBK);
  604. }
  605. dev_dbg(isif_cfg.dev, "\nEnd of isif_config_ycbcr...\n");
  606. return 0;
  607. }
  608. static int isif_set_buftype(enum ccdc_buftype buf_type)
  609. {
  610. if (isif_cfg.if_type == VPFE_RAW_BAYER)
  611. isif_cfg.bayer.buf_type = buf_type;
  612. else
  613. isif_cfg.ycbcr.buf_type = buf_type;
  614. return 0;
  615. }
  616. static enum ccdc_buftype isif_get_buftype(void)
  617. {
  618. if (isif_cfg.if_type == VPFE_RAW_BAYER)
  619. return isif_cfg.bayer.buf_type;
  620. return isif_cfg.ycbcr.buf_type;
  621. }
  622. static int isif_enum_pix(u32 *pix, int i)
  623. {
  624. int ret = -EINVAL;
  625. if (isif_cfg.if_type == VPFE_RAW_BAYER) {
  626. if (i < ARRAY_SIZE(isif_raw_bayer_pix_formats)) {
  627. *pix = isif_raw_bayer_pix_formats[i];
  628. ret = 0;
  629. }
  630. } else {
  631. if (i < ARRAY_SIZE(isif_raw_yuv_pix_formats)) {
  632. *pix = isif_raw_yuv_pix_formats[i];
  633. ret = 0;
  634. }
  635. }
  636. return ret;
  637. }
  638. static int isif_set_pixel_format(unsigned int pixfmt)
  639. {
  640. if (isif_cfg.if_type == VPFE_RAW_BAYER) {
  641. if (pixfmt == V4L2_PIX_FMT_SBGGR8) {
  642. if ((isif_cfg.bayer.config_params.compress.alg !=
  643. ISIF_ALAW) &&
  644. (isif_cfg.bayer.config_params.compress.alg !=
  645. ISIF_DPCM)) {
  646. dev_dbg(isif_cfg.dev,
  647. "Either configure A-Law or DPCM\n");
  648. return -EINVAL;
  649. }
  650. isif_cfg.data_pack = ISIF_PACK_8BIT;
  651. } else if (pixfmt == V4L2_PIX_FMT_SBGGR16) {
  652. isif_cfg.bayer.config_params.compress.alg =
  653. ISIF_NO_COMPRESSION;
  654. isif_cfg.data_pack = ISIF_PACK_16BIT;
  655. } else
  656. return -EINVAL;
  657. isif_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW;
  658. } else {
  659. if (pixfmt == V4L2_PIX_FMT_YUYV)
  660. isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR;
  661. else if (pixfmt == V4L2_PIX_FMT_UYVY)
  662. isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;
  663. else
  664. return -EINVAL;
  665. isif_cfg.data_pack = ISIF_PACK_8BIT;
  666. }
  667. return 0;
  668. }
  669. static u32 isif_get_pixel_format(void)
  670. {
  671. u32 pixfmt;
  672. if (isif_cfg.if_type == VPFE_RAW_BAYER)
  673. if (isif_cfg.bayer.config_params.compress.alg == ISIF_ALAW ||
  674. isif_cfg.bayer.config_params.compress.alg == ISIF_DPCM)
  675. pixfmt = V4L2_PIX_FMT_SBGGR8;
  676. else
  677. pixfmt = V4L2_PIX_FMT_SBGGR16;
  678. else {
  679. if (isif_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR)
  680. pixfmt = V4L2_PIX_FMT_YUYV;
  681. else
  682. pixfmt = V4L2_PIX_FMT_UYVY;
  683. }
  684. return pixfmt;
  685. }
  686. static int isif_set_image_window(struct v4l2_rect *win)
  687. {
  688. if (isif_cfg.if_type == VPFE_RAW_BAYER) {
  689. isif_cfg.bayer.win.top = win->top;
  690. isif_cfg.bayer.win.left = win->left;
  691. isif_cfg.bayer.win.width = win->width;
  692. isif_cfg.bayer.win.height = win->height;
  693. } else {
  694. isif_cfg.ycbcr.win.top = win->top;
  695. isif_cfg.ycbcr.win.left = win->left;
  696. isif_cfg.ycbcr.win.width = win->width;
  697. isif_cfg.ycbcr.win.height = win->height;
  698. }
  699. return 0;
  700. }
  701. static void isif_get_image_window(struct v4l2_rect *win)
  702. {
  703. if (isif_cfg.if_type == VPFE_RAW_BAYER)
  704. *win = isif_cfg.bayer.win;
  705. else
  706. *win = isif_cfg.ycbcr.win;
  707. }
  708. static unsigned int isif_get_line_length(void)
  709. {
  710. unsigned int len;
  711. if (isif_cfg.if_type == VPFE_RAW_BAYER) {
  712. if (isif_cfg.data_pack == ISIF_PACK_8BIT)
  713. len = ((isif_cfg.bayer.win.width));
  714. else if (isif_cfg.data_pack == ISIF_PACK_12BIT)
  715. len = (((isif_cfg.bayer.win.width * 2) +
  716. (isif_cfg.bayer.win.width >> 2)));
  717. else
  718. len = (((isif_cfg.bayer.win.width * 2)));
  719. } else
  720. len = (((isif_cfg.ycbcr.win.width * 2)));
  721. return ALIGN(len, 32);
  722. }
  723. static int isif_set_frame_format(enum ccdc_frmfmt frm_fmt)
  724. {
  725. if (isif_cfg.if_type == VPFE_RAW_BAYER)
  726. isif_cfg.bayer.frm_fmt = frm_fmt;
  727. else
  728. isif_cfg.ycbcr.frm_fmt = frm_fmt;
  729. return 0;
  730. }
  731. static enum ccdc_frmfmt isif_get_frame_format(void)
  732. {
  733. if (isif_cfg.if_type == VPFE_RAW_BAYER)
  734. return isif_cfg.bayer.frm_fmt;
  735. return isif_cfg.ycbcr.frm_fmt;
  736. }
  737. static int isif_getfid(void)
  738. {
  739. return (regr(MODESET) >> 15) & 0x1;
  740. }
  741. /* misc operations */
  742. static void isif_setfbaddr(unsigned long addr)
  743. {
  744. regw((addr >> 21) & 0x07ff, CADU);
  745. regw((addr >> 5) & 0x0ffff, CADL);
  746. }
  747. static int isif_set_hw_if_params(struct vpfe_hw_if_param *params)
  748. {
  749. isif_cfg.if_type = params->if_type;
  750. switch (params->if_type) {
  751. case VPFE_BT656:
  752. case VPFE_BT656_10BIT:
  753. case VPFE_YCBCR_SYNC_8:
  754. isif_cfg.ycbcr.pix_fmt = CCDC_PIXFMT_YCBCR_8BIT;
  755. isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;
  756. break;
  757. case VPFE_BT1120:
  758. case VPFE_YCBCR_SYNC_16:
  759. isif_cfg.ycbcr.pix_fmt = CCDC_PIXFMT_YCBCR_16BIT;
  760. isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;
  761. break;
  762. case VPFE_RAW_BAYER:
  763. isif_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW;
  764. break;
  765. default:
  766. dev_dbg(isif_cfg.dev, "Invalid interface type\n");
  767. return -EINVAL;
  768. }
  769. return 0;
  770. }
  771. /* This function will configure ISIF for YCbCr parameters. */
  772. static int isif_config_ycbcr(void)
  773. {
  774. struct isif_ycbcr_config *params = &isif_cfg.ycbcr;
  775. struct vpss_pg_frame_size frame_size;
  776. u32 modeset = 0, ccdcfg = 0;
  777. struct vpss_sync_pol sync;
  778. dev_dbg(isif_cfg.dev, "\nStarting isif_config_ycbcr...");
  779. /* configure pixel format or input mode */
  780. modeset = modeset | (params->pix_fmt << ISIF_INPUT_SHIFT) |
  781. (params->frm_fmt << ISIF_FRM_FMT_SHIFT) |
  782. (params->fid_pol << ISIF_FID_POL_SHIFT) |
  783. (params->hd_pol << ISIF_HD_POL_SHIFT) |
  784. (params->vd_pol << ISIF_VD_POL_SHIFT);
  785. /* pack the data to 8-bit ISIFCFG */
  786. switch (isif_cfg.if_type) {
  787. case VPFE_BT656:
  788. if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) {
  789. dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n");
  790. return -EINVAL;
  791. }
  792. modeset |= (VPFE_PINPOL_NEGATIVE << ISIF_VD_POL_SHIFT);
  793. regw(3, REC656IF);
  794. ccdcfg = ccdcfg | ISIF_DATA_PACK8 | ISIF_YCINSWP_YCBCR;
  795. break;
  796. case VPFE_BT656_10BIT:
  797. if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) {
  798. dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n");
  799. return -EINVAL;
  800. }
  801. /* setup BT.656, embedded sync */
  802. regw(3, REC656IF);
  803. /* enable 10 bit mode in ccdcfg */
  804. ccdcfg = ccdcfg | ISIF_DATA_PACK8 | ISIF_YCINSWP_YCBCR |
  805. ISIF_BW656_ENABLE;
  806. break;
  807. case VPFE_BT1120:
  808. if (params->pix_fmt != CCDC_PIXFMT_YCBCR_16BIT) {
  809. dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n");
  810. return -EINVAL;
  811. }
  812. regw(3, REC656IF);
  813. break;
  814. case VPFE_YCBCR_SYNC_8:
  815. ccdcfg |= ISIF_DATA_PACK8;
  816. ccdcfg |= ISIF_YCINSWP_YCBCR;
  817. if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) {
  818. dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n");
  819. return -EINVAL;
  820. }
  821. break;
  822. case VPFE_YCBCR_SYNC_16:
  823. if (params->pix_fmt != CCDC_PIXFMT_YCBCR_16BIT) {
  824. dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n");
  825. return -EINVAL;
  826. }
  827. break;
  828. default:
  829. /* should never come here */
  830. dev_dbg(isif_cfg.dev, "Invalid interface type\n");
  831. return -EINVAL;
  832. }
  833. regw(modeset, MODESET);
  834. /* Set up pix order */
  835. ccdcfg |= params->pix_order << ISIF_PIX_ORDER_SHIFT;
  836. regw(ccdcfg, CCDCFG);
  837. /* configure video window */
  838. if ((isif_cfg.if_type == VPFE_BT1120) ||
  839. (isif_cfg.if_type == VPFE_YCBCR_SYNC_16))
  840. isif_setwin(&params->win, params->frm_fmt, 1);
  841. else
  842. isif_setwin(&params->win, params->frm_fmt, 2);
  843. /*
  844. * configure the horizontal line offset
  845. * this is done by rounding up width to a multiple of 16 pixels
  846. * and multiply by two to account for y:cb:cr 4:2:2 data
  847. */
  848. regw(((((params->win.width * 2) + 31) & 0xffffffe0) >> 5), HSIZE);
  849. /* configure the memory line offset */
  850. if ((params->frm_fmt == CCDC_FRMFMT_INTERLACED) &&
  851. (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED))
  852. /* two fields are interleaved in memory */
  853. regw(0x00000249, SDOFST);
  854. /* Setup test pattern if enabled */
  855. if (isif_cfg.bayer.config_params.test_pat_gen) {
  856. sync.ccdpg_hdpol = params->hd_pol;
  857. sync.ccdpg_vdpol = params->vd_pol;
  858. dm365_vpss_set_sync_pol(sync);
  859. dm365_vpss_set_pg_frame_size(frame_size);
  860. }
  861. return 0;
  862. }
  863. static int isif_configure(void)
  864. {
  865. if (isif_cfg.if_type == VPFE_RAW_BAYER)
  866. return isif_config_raw();
  867. return isif_config_ycbcr();
  868. }
  869. static int isif_close(struct device *device)
  870. {
  871. /* copy defaults to module params */
  872. isif_cfg.bayer.config_params = isif_config_defaults;
  873. return 0;
  874. }
  875. static struct ccdc_hw_device isif_hw_dev = {
  876. .name = "ISIF",
  877. .owner = THIS_MODULE,
  878. .hw_ops = {
  879. .open = isif_open,
  880. .close = isif_close,
  881. .enable = isif_enable,
  882. .enable_out_to_sdram = isif_enable_output_to_sdram,
  883. .set_hw_if_params = isif_set_hw_if_params,
  884. .configure = isif_configure,
  885. .set_buftype = isif_set_buftype,
  886. .get_buftype = isif_get_buftype,
  887. .enum_pix = isif_enum_pix,
  888. .set_pixel_format = isif_set_pixel_format,
  889. .get_pixel_format = isif_get_pixel_format,
  890. .set_frame_format = isif_set_frame_format,
  891. .get_frame_format = isif_get_frame_format,
  892. .set_image_window = isif_set_image_window,
  893. .get_image_window = isif_get_image_window,
  894. .get_line_length = isif_get_line_length,
  895. .setfbaddr = isif_setfbaddr,
  896. .getfid = isif_getfid,
  897. },
  898. };
  899. static int isif_probe(struct platform_device *pdev)
  900. {
  901. void (*setup_pinmux)(void);
  902. struct resource *res;
  903. void *__iomem addr;
  904. int status = 0, i;
  905. /* Platform data holds setup_pinmux function ptr */
  906. if (!pdev->dev.platform_data)
  907. return -ENODEV;
  908. /*
  909. * first try to register with vpfe. If not correct platform, then we
  910. * don't have to iomap
  911. */
  912. status = vpfe_register_ccdc_device(&isif_hw_dev);
  913. if (status < 0)
  914. return status;
  915. setup_pinmux = pdev->dev.platform_data;
  916. /*
  917. * setup Mux configuration for ccdc which may be different for
  918. * different SoCs using this CCDC
  919. */
  920. setup_pinmux();
  921. i = 0;
  922. /* Get the ISIF base address, linearization table0 and table1 addr. */
  923. while (i < 3) {
  924. res = platform_get_resource(pdev, IORESOURCE_MEM, i);
  925. if (!res) {
  926. status = -ENODEV;
  927. goto fail_nobase_res;
  928. }
  929. res = request_mem_region(res->start, resource_size(res),
  930. res->name);
  931. if (!res) {
  932. status = -EBUSY;
  933. goto fail_nobase_res;
  934. }
  935. addr = ioremap_nocache(res->start, resource_size(res));
  936. if (!addr) {
  937. status = -ENOMEM;
  938. goto fail_base_iomap;
  939. }
  940. switch (i) {
  941. case 0:
  942. /* ISIF base address */
  943. isif_cfg.base_addr = addr;
  944. break;
  945. case 1:
  946. /* ISIF linear tbl0 address */
  947. isif_cfg.linear_tbl0_addr = addr;
  948. break;
  949. default:
  950. /* ISIF linear tbl0 address */
  951. isif_cfg.linear_tbl1_addr = addr;
  952. break;
  953. }
  954. i++;
  955. }
  956. isif_cfg.dev = &pdev->dev;
  957. printk(KERN_NOTICE "%s is registered with vpfe.\n",
  958. isif_hw_dev.name);
  959. return 0;
  960. fail_base_iomap:
  961. release_mem_region(res->start, resource_size(res));
  962. i--;
  963. fail_nobase_res:
  964. if (isif_cfg.base_addr)
  965. iounmap(isif_cfg.base_addr);
  966. if (isif_cfg.linear_tbl0_addr)
  967. iounmap(isif_cfg.linear_tbl0_addr);
  968. while (i >= 0) {
  969. res = platform_get_resource(pdev, IORESOURCE_MEM, i);
  970. release_mem_region(res->start, resource_size(res));
  971. i--;
  972. }
  973. vpfe_unregister_ccdc_device(&isif_hw_dev);
  974. return status;
  975. }
  976. static int isif_remove(struct platform_device *pdev)
  977. {
  978. struct resource *res;
  979. int i = 0;
  980. iounmap(isif_cfg.base_addr);
  981. iounmap(isif_cfg.linear_tbl0_addr);
  982. iounmap(isif_cfg.linear_tbl1_addr);
  983. while (i < 3) {
  984. res = platform_get_resource(pdev, IORESOURCE_MEM, i);
  985. if (res)
  986. release_mem_region(res->start, resource_size(res));
  987. i++;
  988. }
  989. vpfe_unregister_ccdc_device(&isif_hw_dev);
  990. return 0;
  991. }
  992. static struct platform_driver isif_driver = {
  993. .driver = {
  994. .name = "isif",
  995. },
  996. .remove = isif_remove,
  997. .probe = isif_probe,
  998. };
  999. module_platform_driver(isif_driver);
  1000. MODULE_LICENSE("GPL");