source: rtems/c/src/lib/libbsp/lm32/shared/milkymist_video/video.c @ e5a8020

4.11
Last change on this file since e5a8020 was 267357a, checked in by Gedare Bloom <gedare@…>, on Nov 27, 2011 at 5:33:00 PM

2011-11-27 Sebastien Bourdeauducq <seb@…>

PR 1972/bsps

  • milkymist_video/milkymist_video.h, milkymist_video/video.c: Adds an ioctl to implement selection of composite on any connector, S-Video and Component sources on the Milkymist One.
  • Property mode set to 100644
File size: 8.3 KB
Line 
1/*  video.c
2 *
3 *  Milkymist video input driver for RTEMS
4 *
5 *  The license and distribution terms for this file may be
6 *  found in the file LICENSE in this distribution or at
7 *  http://www.rtems.com/license/LICENSE.
8 *
9 *  $Id$
10 *
11 *  COPYRIGHT (c) 2010, 2011 Sebastien Bourdeauducq
12 */
13
14#define RTEMS_STATUS_CHECKS_USE_PRINTK
15
16#include <stdlib.h>
17#include <stdio.h>
18#include <errno.h>
19#include <sys/types.h>
20#include <rtems.h>
21#include <bsp.h>
22#include <bsp/irq-generic.h>
23#include <rtems/libio.h>
24#include <rtems/status-checks.h>
25#include "../include/system_conf.h"
26#include "milkymist_video.h"
27
28#define DEVICE_NAME "/dev/video"
29#define N_BUFFERS 3
30#define FRAME_W 720
31#define FRAME_H 288
32
33static bool buffers_locked[N_BUFFERS];
34static void *buffers[N_BUFFERS];
35static int last_buffer;
36static int current_buffer;
37
38static rtems_isr frame_handler(rtems_vector_number n)
39{
40  int remaining_attempts;
41
42  lm32_interrupt_ack(1 << MM_IRQ_VIDEOIN);
43 
44  last_buffer = current_buffer;
45 
46  /* get a new buffer */
47  remaining_attempts = N_BUFFERS;
48  do {
49    current_buffer++;
50    if(current_buffer == N_BUFFERS)
51      current_buffer = 0;
52    remaining_attempts--;
53  } while(buffers_locked[current_buffer] && (remaining_attempts > 0));
54
55  MM_WRITE(MM_BT656_BASE, (unsigned int)buffers[current_buffer]);
56 
57  if(buffers_locked[current_buffer])
58    printk("Failed to find unlocked buffer\n");
59}
60
61static void i2c_delay(void)
62{
63  unsigned int i;
64
65  for(i=0;i<1000;i++) __asm__("nop");
66}
67
68/* I2C bit-banging functions from http://en.wikipedia.org/wiki/I2c */
69static unsigned int i2c_read_bit(void)
70{
71  unsigned int bit;
72
73  /* Let the slave drive data */
74  MM_WRITE(MM_BT656_I2C, 0);
75  i2c_delay();
76  MM_WRITE(MM_BT656_I2C, BT656_I2C_SDC);
77  i2c_delay();
78  bit = MM_READ(MM_BT656_I2C) & BT656_I2C_SDAIN;
79  i2c_delay();
80  MM_WRITE(MM_BT656_I2C, 0);
81  return bit;
82}
83
84static void i2c_write_bit(unsigned int bit)
85{
86  if(bit) {
87    MM_WRITE(MM_BT656_I2C, BT656_I2C_SDAOE|BT656_I2C_SDAOUT);
88  } else {
89    MM_WRITE(MM_BT656_I2C, BT656_I2C_SDAOE);
90  }
91  i2c_delay();
92  MM_WRITE(MM_BT656_I2C, MM_READ(MM_BT656_I2C) | BT656_I2C_SDC);
93  i2c_delay();
94  MM_WRITE(MM_BT656_I2C, MM_READ(MM_BT656_I2C) & ~BT656_I2C_SDC);
95}
96
97static int i2c_started;
98
99static void i2c_start_cond(void)
100{
101  if(i2c_started) {
102    /* set SDA to 1 */
103    MM_WRITE(MM_BT656_I2C, BT656_I2C_SDAOE|BT656_I2C_SDAOUT);
104    i2c_delay();
105    MM_WRITE(MM_BT656_I2C, MM_READ(MM_BT656_I2C) | BT656_I2C_SDC);
106  }
107  /* SCL is high, set SDA from 1 to 0 */
108  MM_WRITE(MM_BT656_I2C, BT656_I2C_SDAOE|BT656_I2C_SDC);
109  i2c_delay();
110  MM_WRITE(MM_BT656_I2C, BT656_I2C_SDAOE);
111  i2c_started = 1;
112}
113
114static void i2c_stop_cond(void)
115{
116  /* set SDA to 0 */
117  MM_WRITE(MM_BT656_I2C, BT656_I2C_SDAOE);
118  i2c_delay();
119  /* Clock stretching */
120  MM_WRITE(MM_BT656_I2C, BT656_I2C_SDAOE|BT656_I2C_SDC);
121  /* SCL is high, set SDA from 0 to 1 */
122  MM_WRITE(MM_BT656_I2C, BT656_I2C_SDC);
123  i2c_delay();
124  i2c_started = 0;
125}
126
127static unsigned int i2c_write(unsigned char byte)
128{
129  unsigned int bit;
130  unsigned int ack;
131
132  for(bit = 0; bit < 8; bit++) {
133    i2c_write_bit(byte & 0x80);
134    byte <<= 1;
135  }
136  ack = !i2c_read_bit();
137  return ack;
138}
139
140static unsigned char i2c_read(int ack)
141{
142  unsigned char byte = 0;
143  unsigned int bit;
144
145  for(bit = 0; bit < 8; bit++) {
146    byte <<= 1;
147    byte |= i2c_read_bit();
148  }
149  i2c_write_bit(!ack);
150  return byte;
151}
152
153static unsigned char read_reg(unsigned char addr)
154{
155  unsigned char r;
156
157  i2c_start_cond();
158  i2c_write(0x40);
159  i2c_write(addr);
160  i2c_start_cond();
161  i2c_write(0x41);
162  r = i2c_read(0);
163  i2c_stop_cond();
164
165  return r;
166}
167
168static void write_reg(unsigned char addr, unsigned char val)
169{
170  i2c_start_cond();
171  i2c_write(0x40);
172  i2c_write(addr);
173  i2c_write(val);
174  i2c_stop_cond();
175}
176
177static const char vreg_addr[] = {
178  0x1d, 0xc3, 0xc4
179};
180
181static const char vreg_dat[] = {
182  0x40, 0x05, 0x80
183};
184
185rtems_device_driver video_initialize(
186  rtems_device_major_number major,
187  rtems_device_minor_number minor,
188  void *arg
189)
190{
191  rtems_status_code sc;
192  rtems_isr_entry dummy;
193  int i;
194 
195  MM_WRITE(MM_BT656_I2C, BT656_I2C_SDC);
196
197  sc = rtems_io_register_name(DEVICE_NAME, major, 0);
198  RTEMS_CHECK_SC(sc, "create video input device");
199
200  rtems_interrupt_catch(frame_handler, MM_IRQ_VIDEOIN, &dummy);
201  bsp_interrupt_vector_enable(MM_IRQ_VIDEOIN);
202 
203  for(i=0;i<sizeof(vreg_addr);i++)
204    write_reg(vreg_addr[i], vreg_dat[i]);
205
206  return RTEMS_SUCCESSFUL;
207}
208
209rtems_device_driver video_open(
210  rtems_device_major_number major,
211  rtems_device_minor_number minor,
212  void *arg
213)
214{
215  int i;
216  int status;
217
218  for(i=0;i<N_BUFFERS;i++) {
219    status = posix_memalign(&buffers[i], 32, 2*FRAME_W*FRAME_H);
220    if(status != 0) {
221      i--;
222      while(i > 0) {
223        free(buffers[i]);
224        i--;
225      }
226      return RTEMS_UNSATISFIED;
227    }
228  }
229 
230  last_buffer = -1;
231  current_buffer = 0;
232 
233  MM_WRITE(MM_BT656_BASE, (unsigned int)buffers[current_buffer]);
234  MM_WRITE(MM_BT656_FILTERSTATUS, BT656_FILTER_FIELD1);
235 
236  return RTEMS_SUCCESSFUL;
237}
238
239rtems_device_driver video_close(
240  rtems_device_major_number major,
241  rtems_device_minor_number minor,
242  void *arg
243)
244{
245  int i;
246 
247  MM_WRITE(MM_BT656_FILTERSTATUS, 0);
248  while(MM_READ(MM_BT656_FILTERSTATUS) & BT656_FILTER_INFRAME);
249  for(i=0;i<N_BUFFERS;i++)
250    free(buffers[i]);
251  return RTEMS_SUCCESSFUL;
252}
253
254static void invalidate_caches(void)
255{
256  volatile char *flushbase = (char *)FMLBRG_FLUSH_BASE;
257  int i, offset;
258
259  offset = 0;
260  for (i=0;i<FMLBRG_LINE_COUNT;i++) {
261    flushbase[offset] = 0;
262    offset += FMLBRG_LINE_LENGTH;
263  }
264  __asm__ volatile( /* Invalidate Level-1 data cache */
265    "wcsr DCC, r0\n"
266    "nop\n"
267  );
268}
269
270static void set_format(int format)
271{
272  switch(format) {
273    case VIDEO_FORMAT_CVBS6:
274      write_reg(0x00, 0x00);
275      write_reg(0xc3, 0x05);
276      write_reg(0xc4, 0x80);
277      break;
278    case VIDEO_FORMAT_CVBS5:
279      write_reg(0x00, 0x00);
280      write_reg(0xc3, 0x0d);
281      write_reg(0xc4, 0x80);
282      break;
283    case VIDEO_FORMAT_CVBS4:
284      write_reg(0x00, 0x00);
285      write_reg(0xc3, 0x04);
286      write_reg(0xc4, 0x80);
287      break;
288    case VIDEO_FORMAT_SVIDEO:
289      write_reg(0x00, 0x06);
290      write_reg(0xc3, 0xd5);
291      write_reg(0xc4, 0x80);
292      break;
293    case VIDEO_FORMAT_COMPONENT:
294      write_reg(0x00, 0x09);
295      write_reg(0xc3, 0x45);
296      write_reg(0xc4, 0x8d);
297      break;
298  }
299}
300
301rtems_device_driver video_control(
302  rtems_device_major_number major,
303  rtems_device_minor_number minor,
304  void *arg
305)
306{
307  rtems_libio_ioctl_args_t *args = arg;
308  unsigned int *a = (unsigned int *)args->buffer;
309  rtems_status_code sc;
310
311  switch (args->command) {
312    case VIDEO_BUFFER_LOCK:
313      if (last_buffer == -1) {
314        *a = 0;
315      } else {
316        bsp_interrupt_vector_disable(MM_IRQ_VIDEOIN);
317        if(*a) invalidate_caches();
318        *a = (unsigned int)buffers[last_buffer];
319        buffers_locked[last_buffer] = true;
320        bsp_interrupt_vector_enable(MM_IRQ_VIDEOIN);
321      }
322      sc = RTEMS_SUCCESSFUL;
323      break;
324    case VIDEO_BUFFER_UNLOCK: {
325      int i;
326      for(i=0;i<N_BUFFERS;i++) {
327        if ((unsigned int)buffers[i] == (unsigned int)a) {
328          buffers_locked[i] = false;
329          break;
330        }
331      }
332      sc = RTEMS_SUCCESSFUL;
333      break;
334    }
335   
336    case VIDEO_SET_BRIGHTNESS:
337      write_reg(0x0a, (unsigned int)a);
338      sc = RTEMS_SUCCESSFUL;
339      break;
340    case VIDEO_GET_BRIGHTNESS:
341      *a = read_reg(0x0a);
342      sc = RTEMS_SUCCESSFUL;
343      break;
344    case VIDEO_SET_CONTRAST:
345      write_reg(0x08, (unsigned int)a);
346      sc = RTEMS_SUCCESSFUL;
347      break;
348    case VIDEO_GET_CONTRAST:
349      *a = read_reg(0x08);
350      sc = RTEMS_SUCCESSFUL;
351      break;
352    case VIDEO_SET_HUE:
353      write_reg(0x0b, (unsigned int)a);
354      sc = RTEMS_SUCCESSFUL;
355      break;
356    case VIDEO_GET_HUE:
357      *a = read_reg(0x0b);
358      sc = RTEMS_SUCCESSFUL;
359      break;
360   
361    case VIDEO_GET_SIGNAL:
362      *a = read_reg(0x10);
363      sc = RTEMS_SUCCESSFUL;
364      break;
365   
366    case VIDEO_SET_REGISTER:
367      write_reg(((unsigned int)a & 0xffff0000) >> 16,
368        (unsigned int)a & 0x0000ffff);
369      sc = RTEMS_SUCCESSFUL;
370      break;
371    case VIDEO_GET_REGISTER:
372      *a = read_reg(*a);
373      sc = RTEMS_SUCCESSFUL;
374      break;
375   
376    case VIDEO_SET_FORMAT:
377      set_format((int)a);
378      sc = RTEMS_SUCCESSFUL;
379      break;
380   
381    default:
382      sc = RTEMS_UNSATISFIED;
383      break;
384  }
385
386  if (sc == RTEMS_SUCCESSFUL)
387    args->ioctl_return = 0;
388  else
389    args->ioctl_return = -1;
390
391  return sc;
392}
Note: See TracBrowser for help on using the repository browser.