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