Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:23:44

0001 /*  video.c
0002  *
0003  *  Milkymist video input driver for RTEMS
0004  *
0005  *  The license and distribution terms for this file may be
0006  *  found in the file LICENSE in this distribution or at
0007  *  http://www.rtems.org/license/LICENSE.
0008  *
0009  *  COPYRIGHT (c) 2010, 2011 Sebastien Bourdeauducq
0010  */
0011 
0012 #define RTEMS_STATUS_CHECKS_USE_PRINTK
0013 
0014 #include <stdlib.h>
0015 #include <stdio.h>
0016 #include <errno.h>
0017 #include <sys/types.h>
0018 #include <rtems.h>
0019 #include <bsp.h>
0020 #include <bsp/irq-generic.h>
0021 #include <rtems/libio.h>
0022 #include <rtems/status-checks.h>
0023 #include <rtems/bspIo.h>
0024 #include "../include/system_conf.h"
0025 #include <bsp/milkymist_video.h>
0026 
0027 #define DEVICE_NAME "/dev/video"
0028 #define N_BUFFERS 3
0029 #define FRAME_W 720
0030 #define FRAME_H 288
0031 
0032 static bool buffers_locked[N_BUFFERS];
0033 static void *buffers[N_BUFFERS];
0034 static int last_buffer;
0035 static int current_buffer;
0036 
0037 static rtems_isr frame_handler(rtems_vector_number n)
0038 {
0039   int remaining_attempts;
0040 
0041   lm32_interrupt_ack(1 << MM_IRQ_VIDEOIN);
0042   
0043   last_buffer = current_buffer;
0044   
0045   /* get a new buffer */
0046   remaining_attempts = N_BUFFERS;
0047   do {
0048     current_buffer++;
0049     if(current_buffer == N_BUFFERS)
0050       current_buffer = 0;
0051     remaining_attempts--;
0052   } while(buffers_locked[current_buffer] && (remaining_attempts > 0));
0053 
0054   MM_WRITE(MM_BT656_BASE, (unsigned int)buffers[current_buffer]);
0055   
0056   if(buffers_locked[current_buffer])
0057     printk("Failed to find unlocked buffer\n");
0058 }
0059 
0060 static void i2c_delay(void)
0061 {
0062   unsigned int i;
0063 
0064   for(i=0;i<1000;i++) __asm__("nop");
0065 }
0066 
0067 /* I2C bit-banging functions from http://en.wikipedia.org/wiki/I2c */
0068 static unsigned int i2c_read_bit(void)
0069 {
0070   unsigned int bit;
0071 
0072   /* Let the slave drive data */
0073   MM_WRITE(MM_BT656_I2C, 0);
0074   i2c_delay();
0075   MM_WRITE(MM_BT656_I2C, BT656_I2C_SDC);
0076   i2c_delay();
0077   bit = MM_READ(MM_BT656_I2C) & BT656_I2C_SDAIN;
0078   i2c_delay();
0079   MM_WRITE(MM_BT656_I2C, 0);
0080   return bit;
0081 }
0082 
0083 static void i2c_write_bit(unsigned int bit)
0084 {
0085   if(bit) {
0086     MM_WRITE(MM_BT656_I2C, BT656_I2C_SDAOE|BT656_I2C_SDAOUT);
0087   } else {
0088     MM_WRITE(MM_BT656_I2C, BT656_I2C_SDAOE);
0089   }
0090   i2c_delay();
0091   MM_WRITE(MM_BT656_I2C, MM_READ(MM_BT656_I2C) | BT656_I2C_SDC);
0092   i2c_delay();
0093   MM_WRITE(MM_BT656_I2C, MM_READ(MM_BT656_I2C) & ~BT656_I2C_SDC);
0094 }
0095 
0096 static int i2c_started;
0097 
0098 static void i2c_start_cond(void)
0099 {
0100   if(i2c_started) {
0101     /* set SDA to 1 */
0102     MM_WRITE(MM_BT656_I2C, BT656_I2C_SDAOE|BT656_I2C_SDAOUT);
0103     i2c_delay();
0104     MM_WRITE(MM_BT656_I2C, MM_READ(MM_BT656_I2C) | BT656_I2C_SDC);
0105   }
0106   /* SCL is high, set SDA from 1 to 0 */
0107   MM_WRITE(MM_BT656_I2C, BT656_I2C_SDAOE|BT656_I2C_SDC);
0108   i2c_delay();
0109   MM_WRITE(MM_BT656_I2C, BT656_I2C_SDAOE);
0110   i2c_started = 1;
0111 }
0112 
0113 static void i2c_stop_cond(void)
0114 {
0115   /* set SDA to 0 */
0116   MM_WRITE(MM_BT656_I2C, BT656_I2C_SDAOE);
0117   i2c_delay();
0118   /* Clock stretching */
0119   MM_WRITE(MM_BT656_I2C, BT656_I2C_SDAOE|BT656_I2C_SDC);
0120   /* SCL is high, set SDA from 0 to 1 */
0121   MM_WRITE(MM_BT656_I2C, BT656_I2C_SDC);
0122   i2c_delay();
0123   i2c_started = 0;
0124 }
0125 
0126 static unsigned int i2c_write(unsigned char byte)
0127 {
0128   unsigned int bit;
0129   unsigned int ack;
0130 
0131   for(bit = 0; bit < 8; bit++) {
0132     i2c_write_bit(byte & 0x80);
0133     byte <<= 1;
0134   }
0135   ack = !i2c_read_bit();
0136   return ack;
0137 }
0138 
0139 static unsigned char i2c_read(int ack)
0140 {
0141   unsigned char byte = 0;
0142   unsigned int bit;
0143 
0144   for(bit = 0; bit < 8; bit++) {
0145     byte <<= 1;
0146     byte |= i2c_read_bit();
0147   }
0148   i2c_write_bit(!ack);
0149   return byte;
0150 }
0151 
0152 static unsigned char read_reg(unsigned char addr)
0153 {
0154   unsigned char r;
0155 
0156   i2c_start_cond();
0157   i2c_write(0x40);
0158   i2c_write(addr);
0159   i2c_start_cond();
0160   i2c_write(0x41);
0161   r = i2c_read(0);
0162   i2c_stop_cond();
0163 
0164   return r;
0165 }
0166 
0167 static void write_reg(unsigned char addr, unsigned char val)
0168 {
0169   i2c_start_cond();
0170   i2c_write(0x40);
0171   i2c_write(addr);
0172   i2c_write(val);
0173   i2c_stop_cond();
0174 }
0175 
0176 static const char vreg_addr[] = {
0177   0x1d, 0xc3, 0xc4
0178 };
0179 
0180 static const char vreg_dat[] = {
0181   0x40, 0x05, 0x80
0182 };
0183 
0184 rtems_device_driver video_initialize(
0185   rtems_device_major_number major,
0186   rtems_device_minor_number minor,
0187   void *arg
0188 )
0189 {
0190   rtems_status_code sc;
0191   rtems_isr_entry dummy;
0192   int i;
0193   
0194   MM_WRITE(MM_BT656_I2C, BT656_I2C_SDC);
0195 
0196   sc = rtems_io_register_name(DEVICE_NAME, major, 0);
0197   RTEMS_CHECK_SC(sc, "create video input device");
0198 
0199   rtems_interrupt_catch(frame_handler, MM_IRQ_VIDEOIN, &dummy);
0200   bsp_interrupt_vector_enable(MM_IRQ_VIDEOIN);
0201   
0202   for(i=0;i<sizeof(vreg_addr);i++)
0203     write_reg(vreg_addr[i], vreg_dat[i]);
0204 
0205   return RTEMS_SUCCESSFUL;
0206 }
0207 
0208 rtems_device_driver video_open(
0209   rtems_device_major_number major,
0210   rtems_device_minor_number minor,
0211   void *arg
0212 )
0213 {
0214   int i;
0215   int status;
0216 
0217   for(i=0;i<N_BUFFERS;i++) {
0218     status = posix_memalign(&buffers[i], 32, 2*FRAME_W*FRAME_H);
0219     if(status != 0) {
0220       i--;
0221       while(i > 0) {
0222         free(buffers[i]);
0223         i--;
0224       }
0225       return RTEMS_UNSATISFIED;
0226     }
0227   }
0228   
0229   last_buffer = -1;
0230   current_buffer = 0;
0231   
0232   MM_WRITE(MM_BT656_BASE, (unsigned int)buffers[current_buffer]);
0233   MM_WRITE(MM_BT656_FILTERSTATUS, BT656_FILTER_FIELD1);
0234   
0235   return RTEMS_SUCCESSFUL;
0236 }
0237 
0238 rtems_device_driver video_close(
0239   rtems_device_major_number major,
0240   rtems_device_minor_number minor,
0241   void *arg
0242 )
0243 {
0244   int i;
0245   
0246   MM_WRITE(MM_BT656_FILTERSTATUS, 0);
0247   while(MM_READ(MM_BT656_FILTERSTATUS) & BT656_FILTER_INFRAME);
0248   for(i=0;i<N_BUFFERS;i++)
0249     free(buffers[i]);
0250   return RTEMS_SUCCESSFUL;
0251 }
0252 
0253 static void invalidate_caches(void)
0254 {
0255   volatile char *flushbase = (char *)FMLBRG_FLUSH_BASE;
0256   int i, offset;
0257 
0258   offset = 0;
0259   for (i=0;i<FMLBRG_LINE_COUNT;i++) {
0260     flushbase[offset] = 0;
0261     offset += FMLBRG_LINE_LENGTH;
0262   }
0263   __asm__ volatile( /* Invalidate Level-1 data cache */
0264     "wcsr DCC, r0\n"
0265     "nop\n"
0266   );
0267 }
0268 
0269 static void set_format(int format)
0270 {
0271   switch(format) {
0272     case VIDEO_FORMAT_CVBS6:
0273       write_reg(0x00, 0x00);
0274       write_reg(0xc3, 0x05);
0275       write_reg(0xc4, 0x80);
0276       break;
0277     case VIDEO_FORMAT_CVBS5:
0278       write_reg(0x00, 0x00);
0279       write_reg(0xc3, 0x0d);
0280       write_reg(0xc4, 0x80);
0281       break;
0282     case VIDEO_FORMAT_CVBS4:
0283       write_reg(0x00, 0x00);
0284       write_reg(0xc3, 0x04);
0285       write_reg(0xc4, 0x80);
0286       break;
0287     case VIDEO_FORMAT_SVIDEO:
0288       write_reg(0x00, 0x06);
0289       write_reg(0xc3, 0xd5);
0290       write_reg(0xc4, 0x80);
0291       break;
0292     case VIDEO_FORMAT_COMPONENT:
0293       write_reg(0x00, 0x09);
0294       write_reg(0xc3, 0x45);
0295       write_reg(0xc4, 0x8d);
0296       break;
0297   }
0298 }
0299 
0300 rtems_device_driver video_control(
0301   rtems_device_major_number major,
0302   rtems_device_minor_number minor,
0303   void *arg
0304 )
0305 {
0306   rtems_libio_ioctl_args_t *args = arg;
0307   unsigned int *a = (unsigned int *)args->buffer;
0308   rtems_status_code sc;
0309 
0310   switch (args->command) {
0311     case VIDEO_BUFFER_LOCK:
0312       if (last_buffer == -1) {
0313         *a = 0;
0314       } else {
0315         bsp_interrupt_vector_disable(MM_IRQ_VIDEOIN);
0316         if(*a) invalidate_caches();
0317         *a = (unsigned int)buffers[last_buffer];
0318         buffers_locked[last_buffer] = true;
0319         bsp_interrupt_vector_enable(MM_IRQ_VIDEOIN);
0320       }
0321       sc = RTEMS_SUCCESSFUL;
0322       break;
0323     case VIDEO_BUFFER_UNLOCK: {
0324       int i;
0325       for(i=0;i<N_BUFFERS;i++) {
0326         if ((unsigned int)buffers[i] == (unsigned int)a) {
0327           buffers_locked[i] = false;
0328           break;
0329         }
0330       }
0331       sc = RTEMS_SUCCESSFUL;
0332       break;
0333     }
0334     
0335     case VIDEO_SET_BRIGHTNESS:
0336       write_reg(0x0a, (unsigned int)a);
0337       sc = RTEMS_SUCCESSFUL;
0338       break;
0339     case VIDEO_GET_BRIGHTNESS:
0340       *a = read_reg(0x0a);
0341       sc = RTEMS_SUCCESSFUL;
0342       break;
0343     case VIDEO_SET_CONTRAST:
0344       write_reg(0x08, (unsigned int)a);
0345       sc = RTEMS_SUCCESSFUL;
0346       break;
0347     case VIDEO_GET_CONTRAST:
0348       *a = read_reg(0x08);
0349       sc = RTEMS_SUCCESSFUL;
0350       break;
0351     case VIDEO_SET_HUE:
0352       write_reg(0x0b, (unsigned int)a);
0353       sc = RTEMS_SUCCESSFUL;
0354       break;
0355     case VIDEO_GET_HUE:
0356       *a = read_reg(0x0b);
0357       sc = RTEMS_SUCCESSFUL;
0358       break;
0359     
0360     case VIDEO_GET_SIGNAL:
0361       *a = read_reg(0x10);
0362       sc = RTEMS_SUCCESSFUL;
0363       break;
0364     
0365     case VIDEO_SET_REGISTER:
0366       write_reg(((unsigned int)a & 0xffff0000) >> 16,
0367         (unsigned int)a & 0x0000ffff);
0368       sc = RTEMS_SUCCESSFUL;
0369       break;
0370     case VIDEO_GET_REGISTER:
0371       *a = read_reg(*a);
0372       sc = RTEMS_SUCCESSFUL;
0373       break;
0374     
0375     case VIDEO_SET_FORMAT:
0376       set_format((int)a);
0377       sc = RTEMS_SUCCESSFUL;
0378       break;
0379     
0380     default:
0381       sc = RTEMS_UNSATISFIED;
0382       break;
0383   }
0384 
0385   if (sc == RTEMS_SUCCESSFUL)
0386     args->ioctl_return = 0;
0387   else
0388     args->ioctl_return = -1;
0389 
0390   return sc;
0391 }