1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * dcam_frame.c
29 *
30 * dcam1394 driver. Support for video frame access.
31 */
32
33 #include <sys/int_limits.h>
34 #include <sys/types.h>
35 #include <sys/kmem.h>
36 #include <sys/cmn_err.h>
37 #include <sys/1394/targets/dcam1394/dcam.h>
38 #include <sys/1394/targets/dcam1394/dcam_frame.h>
39 #include <sys/dcam/dcam1394_io.h>
40
41 #include <sys/1394/targets/dcam1394/dcam_reg.h>
42
43 static void dcam_free_resources(dcam_state_t *);
44
45 typedef struct dcam_mode_info_s {
46 int bytes_per_pkt;
47 int pkts_per_frame;
48 } dcam_mode_info_t;
49
50 /*
51 * packets per frame
52 *
53 * 30fps
54 * mode_0 1/2h, 60q, 240b
55 * mode_1 1h, 160q, 640
56 * mode_2 2h, 480q, 1920
57 * mode_3 2h, 640q, 2560
58 * mode_4 2h, 960q, 3840
59 * mode_5 2h, 320q, 1280
60 *
61 * 15fps
62 * mode_0 1/4h, 30q, 120
63 * mode_1 1/2h, 80q, 320
64 * mode_2 1h, 240q, 960
65 * mode_3 1h, 320q, 1280
66 * mode_4 1h, 480q, 1920
67 * mode_5 1h, 160q, 640
68 *
69 * 7.5fps
70 * mode_0 1/8h, 15q, 60
71 * mode_1 1/4h, 40q, 160
72 * mode_2 1/2h, 120q, 480
73 * mode_3 1/2h, 160q, 640
74 * mode_4 1/2h, 240q, 960
75 * mode_5 1/2h, 80q, 320
76 *
77 * 3.75fps
78 * mode_0 x
79 * mode_1 1/8h, 20q, 80
80 * mode_2 1/4h, 60q, 240
81 * mode_3 1/4h, 80q, 320
82 * mode_4 1/4h, 120q, 480
83 * mode_5 1/4h, 40q, 160
84 *
85 * 60fps
86 * mode_5 4H, 640q, 2560
87 *
88 */
89
90 /* indexed by vid mode, frame rate */
91 static int g_bytes_per_packet[6][5] = {
92
93 /* fps: 3.75 7.5 15 30 60 */
94 /* vid mode 0 */ -1, 60, 120, 240, -1,
95 /* vid mode 1 */ 80, 160, 320, 640, -1,
96 /* vid mode 2 */ 240, 480, 960, 1920, -1,
97 /* vid mode 3 */ 320, 640, 1280, 2560, -1,
98 /* vid mode 4 */ 480, 960, 1920, 3840, -1,
99 /* vid mode 5 */ 160, 320, 640, 1280, 2560
100 };
101
102 /* indexed by vid mode */
103 static int g_bytes_per_frame[6] = {
104 57600,
105 153600,
106 460800,
107 614400,
108 921600,
109 307200
110 };
111
112
113 static
114 void dcam_rsrc_fail(t1394_isoch_single_handle_t t1394_single_hdl,
115 opaque_t single_evt_arg, t1394_isoch_rsrc_error_t fail_args);
116
117 /*
118 * dcam1394_ioctl_frame_rcv_start
119 */
120 int
dcam1394_ioctl_frame_rcv_start(dcam_state_t * softc_p)121 dcam1394_ioctl_frame_rcv_start(dcam_state_t *softc_p)
122 {
123 if (!(softc_p->flags & DCAM1394_FLAG_FRAME_RCV_INIT)) {
124
125 if (dcam_frame_rcv_init(softc_p, softc_p->cur_vid_mode,
126 softc_p->cur_frame_rate, softc_p->cur_ring_buff_capacity)) {
127
128 dcam_free_resources(softc_p);
129 return (1);
130 }
131
132 softc_p->flags |= DCAM1394_FLAG_FRAME_RCV_INIT;
133 }
134
135 if (dcam_frame_rcv_start(softc_p)) {
136 return (1);
137 }
138
139 return (0);
140 }
141
142
143 /*
144 * dcam_frame_rcv_init
145 */
146 int
dcam_frame_rcv_init(dcam_state_t * softc_p,int vid_mode,int frame_rate,int ring_buff_capacity)147 dcam_frame_rcv_init(dcam_state_t *softc_p, int vid_mode, int frame_rate,
148 int ring_buff_capacity)
149 {
150 int16_t bytes_per_pkt; /* # pkt bytes + overhead */
151 int bytes_per_frame;
152 size_t frame;
153 int cookie;
154 int failure;
155 id1394_isoch_dmainfo_t isoch_args; /* for alloc isoch call */
156 ixl1394_command_t *last_ixlp; /* last ixl in chain, */
157 /* used for appending ixls */
158 ixl1394_command_t *new_ixl_cmdp; /* new ixl command */
159 ixl1394_set_syncwait_t *new_ixl_sswp; /* new ixl set syncwait */
160 ixl1394_xfer_pkt_t *new_ixl_xfpp; /* new ixl xfer packet */
161 ixl1394_xfer_buf_t *new_ixl_xfbp; /* new ixl xfer buffer */
162 ixl1394_callback_t *new_ixl_cbp; /* new ixl callback */
163 ixl1394_jump_t *new_ixl_jmpp; /* new ixl jump */
164 int32_t result; /* errno from alloc_isoch_dma */
165 buff_info_t *buff_info_p;
166 dcam1394_reg_io_t reg_io;
167 uint_t data;
168 size_t num_bytes, num_bytes_left;
169 size_t num_xfer_cmds, xfer_cmd;
170 size_t max_ixl_buff_size;
171 uint64_t ixl_buff_kaddr;
172 caddr_t ixl_buff_vaddr;
173
174 bytes_per_pkt = g_bytes_per_packet[vid_mode][frame_rate];
175 if (bytes_per_pkt == -1) {
176 return (1);
177 }
178
179 bytes_per_frame = g_bytes_per_frame[vid_mode];
180
181 if ((softc_p->ring_buff_p = ring_buff_create(softc_p,
182 (size_t)ring_buff_capacity, (size_t)bytes_per_frame)) == NULL) {
183 return (1);
184 }
185
186 softc_p->ring_buff_p->read_ptr_pos[0] = 0;
187
188 /* allocate isoch channel */
189 softc_p->sii.si_channel_mask = 0xFFFF000000000000;
190 softc_p->sii.si_bandwidth = bytes_per_pkt;
191 softc_p->sii.rsrc_fail_target = dcam_rsrc_fail;
192 softc_p->sii.single_evt_arg = softc_p;
193 softc_p->sii.si_speed = softc_p->targetinfo.current_max_speed;
194
195 if (t1394_alloc_isoch_single(softc_p->sl_handle,
196 &softc_p->sii, 0, &softc_p->sii_output_args, &softc_p->sii_hdl,
197 &failure) != DDI_SUCCESS) {
198 return (1);
199 }
200
201 /*
202 * At this point, all buffer memory has been allocated and
203 * mapped, and is tracked on a linear linked list. Now need to
204 * build the IXL. Done on a frame-by-frame basis. Could
205 * theoretically have been done at the same time as the mem alloc
206 * above, but hey, no need to be so fancy here.
207 *
208 * ixl buff size is bound by SHRT_MAX and needs to
209 * be a multiple of packet size
210 */
211 max_ixl_buff_size = (SHRT_MAX / bytes_per_pkt) * bytes_per_pkt;
212
213 /* for each frame build frame's ixl list */
214 for (frame = 0; frame < softc_p->ring_buff_p->num_buffs; frame++) {
215
216 buff_info_p = &(softc_p->ring_buff_p->buff_info_array_p[frame]);
217
218 /*
219 * if this is the 1st frame, put a IXL label at the top so a
220 * loop can be created later
221 */
222 if (frame == 0) {
223 new_ixl_cmdp = kmem_zalloc(
224 sizeof (ixl1394_label_t), KM_SLEEP);
225 softc_p->ixlp = new_ixl_cmdp;
226
227 new_ixl_cmdp->ixl_opcode = IXL1394_OP_LABEL;
228
229 last_ixlp = softc_p->ixlp;
230 }
231
232 /* add wait-for-sync IXL command */
233 new_ixl_sswp = kmem_zalloc(
234 sizeof (ixl1394_set_syncwait_t), KM_SLEEP);
235
236 new_ixl_sswp->ixl_opcode = IXL1394_OP_SET_SYNCWAIT;
237
238 last_ixlp->next_ixlp = (ixl1394_command_t *)new_ixl_sswp;
239 last_ixlp = (ixl1394_command_t *)new_ixl_sswp;
240
241 /* add in each dma cookie */
242 for (cookie = 0; cookie < buff_info_p->dma_cookie_count;
243 cookie++) {
244
245 num_xfer_cmds = min(bytes_per_frame,
246 buff_info_p->dma_cookie.dmac_size) /
247 max_ixl_buff_size;
248
249 if (min(bytes_per_frame,
250 buff_info_p->dma_cookie.dmac_size) %
251 max_ixl_buff_size) {
252 num_xfer_cmds++;
253 }
254
255 num_bytes_left = min(bytes_per_frame,
256 buff_info_p->dma_cookie.dmac_size);
257
258 ixl_buff_kaddr =
259 buff_info_p->dma_cookie.dmac_laddress;
260
261 ixl_buff_vaddr = buff_info_p->kaddr_p;
262
263 for (xfer_cmd = 0; xfer_cmd < (num_xfer_cmds + 1);
264 xfer_cmd++) {
265 num_bytes = min(num_bytes_left,
266 max_ixl_buff_size);
267
268 if (xfer_cmd == 0) {
269 new_ixl_xfpp =
270 kmem_zalloc(
271 sizeof (ixl1394_xfer_pkt_t),
272 KM_SLEEP);
273
274 new_ixl_xfpp->ixl_opcode =
275 IXL1394_OP_RECV_PKT_ST;
276
277 new_ixl_xfpp->ixl_buf._dmac_ll =
278 ixl_buff_kaddr;
279 new_ixl_xfpp->size =
280 (uint16_t)bytes_per_pkt;
281 new_ixl_xfpp->mem_bufp =
282 ixl_buff_vaddr;
283
284 last_ixlp->next_ixlp =
285 (ixl1394_command_t *)new_ixl_xfpp;
286 last_ixlp =
287 (ixl1394_command_t *)new_ixl_xfpp;
288
289 num_bytes_left -= bytes_per_pkt;
290 ixl_buff_kaddr += bytes_per_pkt;
291 ixl_buff_vaddr += bytes_per_pkt;
292
293 continue;
294 }
295
296 /* allocate & init an IXL transfer command. */
297 new_ixl_xfbp =
298 kmem_zalloc(sizeof (ixl1394_xfer_buf_t),
299 KM_SLEEP);
300
301 new_ixl_xfbp->ixl_opcode = IXL1394_OP_RECV_BUF;
302
303 new_ixl_xfbp->ixl_buf._dmac_ll =
304 ixl_buff_kaddr;
305 new_ixl_xfbp->size = (uint16_t)num_bytes;
306 new_ixl_xfbp->pkt_size = bytes_per_pkt;
307 new_ixl_xfbp->mem_bufp = ixl_buff_vaddr;
308
309 last_ixlp->next_ixlp =
310 (ixl1394_command_t *)new_ixl_xfbp;
311 last_ixlp =
312 (ixl1394_command_t *)new_ixl_xfbp;
313
314 num_bytes_left -= num_bytes;
315 ixl_buff_kaddr += num_bytes;
316 ixl_buff_vaddr += num_bytes;
317 }
318
319 if (cookie > 0) {
320 ddi_dma_nextcookie(buff_info_p->dma_handle,
321 &(buff_info_p->dma_cookie));
322 }
323
324 }
325
326 /*
327 * at this point, have finished a frame. put in a callback
328 */
329 new_ixl_cbp = kmem_zalloc(
330 sizeof (ixl1394_callback_t), KM_SLEEP);
331
332 new_ixl_cbp->ixl_opcode = IXL1394_OP_CALLBACK;
333
334 new_ixl_cbp->callback = &dcam_frame_is_done;
335 new_ixl_cbp->callback_arg = NULL;
336
337 last_ixlp->next_ixlp = (ixl1394_command_t *)new_ixl_cbp;
338 last_ixlp = (ixl1394_command_t *)new_ixl_cbp;
339 }
340
341 /*
342 * for the final touch, put an IXL jump at the end to jump to the
343 * label at the top
344 */
345 new_ixl_jmpp = kmem_zalloc(sizeof (ixl1394_jump_t), KM_SLEEP);
346
347 new_ixl_jmpp->ixl_opcode = IXL1394_OP_JUMP;
348
349 new_ixl_jmpp->label = softc_p->ixlp;
350
351 last_ixlp->next_ixlp = (ixl1394_command_t *)new_ixl_jmpp;
352
353 /* don't need this, but it's neater */
354 last_ixlp = (ixl1394_command_t *)new_ixl_jmpp;
355
356 /* call fwim routine to alloc an isoch resource */
357 isoch_args.ixlp = softc_p->ixlp;
358 isoch_args.channel_num = softc_p->sii_output_args.channel_num;
359
360 /* other misc args. note speed doesn't matter for isoch receive */
361 isoch_args.idma_options = ID1394_LISTEN_PKT_MODE;
362 isoch_args.default_tag = 0;
363 isoch_args.default_sync = 1;
364 isoch_args.global_callback_arg = softc_p;
365
366 /* set the ISO channel number */
367 data = (softc_p->sii_output_args.channel_num & 0xF) << 28;
368
369 /* set the ISO speed */
370 data |= (softc_p->targetinfo.current_max_speed << 24);
371
372 reg_io.offs = DCAM1394_REG_OFFS_CUR_ISO_CHANNEL;
373 reg_io.val = data;
374
375 if (dcam_reg_write(softc_p, ®_io)) {
376 return (1);
377 }
378
379 result = 1234;
380
381 if (t1394_alloc_isoch_dma(softc_p->sl_handle, &isoch_args, 0,
382 &softc_p->isoch_handle, &result) != DDI_SUCCESS) {
383 return (1);
384 }
385
386 return (0);
387 }
388
389
390 /*
391 * dcam_frame_rcv_fini
392 */
393 int
dcam_frame_rcv_fini(dcam_state_t * softc_p)394 dcam_frame_rcv_fini(dcam_state_t *softc_p)
395 {
396 t1394_free_isoch_dma(softc_p->sl_handle, 0, &softc_p->isoch_handle);
397
398 softc_p->isoch_handle = NULL;
399
400 t1394_free_isoch_single(softc_p->sl_handle, &softc_p->sii_hdl, 0);
401
402 return (0);
403 }
404
405
406 /*
407 * dcam_frame_rcv_start
408 */
409 int
dcam_frame_rcv_start(dcam_state_t * softc_p)410 dcam_frame_rcv_start(dcam_state_t *softc_p)
411 {
412 id1394_isoch_dma_ctrlinfo_t idma_ctrlinfo; /* currently not used */
413 int32_t result;
414 dcam1394_reg_io_t reg_io;
415
416 if ((t1394_start_isoch_dma(softc_p->sl_handle, softc_p->isoch_handle,
417 &idma_ctrlinfo, 0, &result)) != DDI_SUCCESS) {
418 return (1);
419 }
420
421 reg_io.offs = DCAM1394_REG_OFFS_ISO_EN;
422 reg_io.val = 0x80000000;
423
424 if (dcam_reg_write(softc_p, ®_io)) {
425 return (1);
426 }
427
428 softc_p->flags |= DCAM1394_FLAG_FRAME_RCVING;
429
430 return (0);
431 }
432
433
434 /*
435 * dcam_frame_rcv_stop
436 */
437 int
dcam_frame_rcv_stop(dcam_state_t * softc_p)438 dcam_frame_rcv_stop(dcam_state_t *softc_p)
439 {
440 dcam1394_reg_io_t reg_io;
441
442 /* if resources have already been cleared, nothing to do */
443 if (!(softc_p->flags & DCAM1394_FLAG_FRAME_RCV_INIT)) {
444 return (0);
445 }
446
447 reg_io.offs = DCAM1394_REG_OFFS_ISO_EN;
448 reg_io.val = 0;
449
450 (void) dcam_reg_write(softc_p, ®_io);
451
452 t1394_stop_isoch_dma(softc_p->sl_handle, softc_p->isoch_handle, 0);
453 t1394_free_isoch_dma(softc_p->sl_handle, 0, &softc_p->isoch_handle);
454 t1394_free_isoch_single(softc_p->sl_handle, &softc_p->sii_hdl, 0);
455
456 dcam_free_resources(softc_p);
457
458 return (0);
459 }
460
461
462 void
dcam_free_resources(dcam_state_t * softc_p)463 dcam_free_resources(dcam_state_t *softc_p)
464 {
465 ixl1394_command_t *ptr;
466 ixl1394_command_t *tmp;
467
468 /*
469 * The following fixes a memory leak. See bug #4423667.
470 * The original code only released memory for the first frame.
471 */
472
473 /* free ixl opcode resources */
474 ptr = softc_p->ixlp;
475
476 while (ptr != NULL) {
477 tmp = ptr;
478 ptr = ptr->next_ixlp;
479
480 switch (tmp->ixl_opcode) {
481 case IXL1394_OP_LABEL:
482 kmem_free(tmp, sizeof (ixl1394_label_t));
483 break;
484
485 case IXL1394_OP_SET_SYNCWAIT:
486 kmem_free(tmp, sizeof (ixl1394_set_syncwait_t));
487 break;
488
489 case IXL1394_OP_RECV_PKT_ST:
490 kmem_free(tmp, sizeof (ixl1394_xfer_pkt_t));
491 break;
492
493 case IXL1394_OP_RECV_BUF:
494 kmem_free(tmp, sizeof (ixl1394_xfer_buf_t));
495 break;
496
497 case IXL1394_OP_CALLBACK:
498 kmem_free(tmp, sizeof (ixl1394_callback_t));
499 break;
500
501 case IXL1394_OP_JUMP:
502 kmem_free(tmp, sizeof (ixl1394_jump_t));
503 break;
504 }
505 }
506
507 /*
508 * free ring buff and indicate that the resources have been cleared
509 */
510 ring_buff_free(softc_p, softc_p->ring_buff_p);
511
512 softc_p->flags &= ~DCAM1394_FLAG_FRAME_RCV_INIT;
513 softc_p->ixlp = NULL;
514 }
515
516
517 /*
518 * dcam_frame_is_done
519 *
520 * This routine is called after DMA engine has stored a single received
521 * frame in ring buffer position pointed to by write pointer; this
522 * routine marks the frame's vid mode, timestamp, and sequence number
523 *
524 * Store received frame in ring buffer position pointed to by write pointer.
525 * Increment write pointer. If write pointer is pointing to the same
526 * position as read pointer, increment read pointer.
527 *
528 * If device driver is processing a user process's read() request
529 * invalidate the read() request processing operation.
530 *
531 */
532
533 /* ARGSUSED */
534 void
dcam_frame_is_done(void * ssp,ixl1394_callback_t * ixlp)535 dcam_frame_is_done(void *ssp, ixl1394_callback_t *ixlp)
536 {
537 dcam_state_t *softc_p;
538 int num_read_ptrs;
539 int read_ptr_id;
540 int vid_mode;
541 size_t write_ptr_pos;
542 ring_buff_t *ring_buff_p;
543 unsigned int seq_num;
544
545 /*
546 * Store received frame in ring buffer position pointed to by
547 * write pointer (this routine is called after DMA engine has
548 * stored a single received frame in ring buffer position pointed
549 * to by write pointer; this routine marks the frame's vid mode,
550 * timestamp, and sequence number)
551 */
552
553 if ((softc_p = (dcam_state_t *)ssp) == NULL) {
554 return;
555 }
556
557 if ((ring_buff_p = softc_p->ring_buff_p) == NULL) {
558 return;
559 }
560
561 mutex_enter(&softc_p->dcam_frame_is_done_mutex);
562
563 write_ptr_pos = ring_buff_write_ptr_pos_get(ring_buff_p);
564
565 /* mark vid mode */
566 vid_mode =
567 softc_p->
568 param_attr[DCAM1394_PARAM_VID_MODE][DCAM1394_SUBPARAM_NONE];
569 ring_buff_p->buff_info_array_p[write_ptr_pos].vid_mode = vid_mode;
570
571
572 /* update sequence counter overflow in param_status */
573 if (softc_p->seq_count == 0xffffffff)
574 softc_p->param_status |=
575 DCAM1394_STATUS_FRAME_SEQ_NUM_COUNT_OVERFLOW;
576
577
578 /* mark frame's sequence number */
579 ring_buff_p->buff_info_array_p[write_ptr_pos].seq_num =
580 softc_p->seq_count++;
581
582 seq_num = ring_buff_p->buff_info_array_p[write_ptr_pos].seq_num;
583
584
585 /* mark frame's timestamp */
586 ring_buff_p->buff_info_array_p[write_ptr_pos].timestamp = gethrtime();
587
588
589 /* increment write pointer */
590 ring_buff_write_ptr_incr(ring_buff_p);
591
592 num_read_ptrs = 1;
593
594 for (read_ptr_id = 0; read_ptr_id < num_read_ptrs; read_ptr_id++) {
595
596 /*
597 * if write pointer is pointing to the same position as
598 * read pointer
599 */
600
601 if ((ring_buff_write_ptr_pos_get(ring_buff_p) ==
602 ring_buff_read_ptr_pos_get(ring_buff_p, read_ptr_id)) &&
603 (seq_num != 0)) {
604
605 /* increment read pointer */
606 ring_buff_read_ptr_incr(ring_buff_p, read_ptr_id);
607
608 /*
609 * if device driver is processing a user
610 * process's read() request
611 */
612 if (softc_p->reader_flags[read_ptr_id] &
613 DCAM1394_FLAG_READ_REQ_PROC) {
614
615 /*
616 * invalidate the read() request processing
617 * operation
618 */
619 softc_p->reader_flags[read_ptr_id] |=
620 DCAM1394_FLAG_READ_REQ_INVALID;
621 }
622
623 /* inform user app that we have lost one frame */
624 softc_p->param_status |=
625 DCAM1394_STATUS_RING_BUFF_LOST_FRAME;
626 }
627 }
628
629 /* inform user app that we have received one frame */
630 softc_p->param_status |= DCAM1394_STATUS_FRAME_RCV_DONE;
631
632 mutex_exit(&softc_p->dcam_frame_is_done_mutex);
633 }
634
635
636 /* ARGSUSED */
637 static void
dcam_rsrc_fail(t1394_isoch_single_handle_t t1394_single_hdl,opaque_t single_evt_arg,t1394_isoch_rsrc_error_t fail_args)638 dcam_rsrc_fail(t1394_isoch_single_handle_t t1394_single_hdl,
639 opaque_t single_evt_arg, t1394_isoch_rsrc_error_t fail_args)
640 {
641 cmn_err(CE_NOTE, "dcam_rsrc_fail(): unable to re-alloc resources\n");
642 }
643