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 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 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 caddr_t ixl_buff_kaddr, ixl_buff_vaddr; 175 176 bytes_per_pkt = g_bytes_per_packet[vid_mode][frame_rate]; 177 if (bytes_per_pkt == -1) { 178 return (1); 179 } 180 181 bytes_per_frame = g_bytes_per_frame[vid_mode]; 182 183 if ((softc_p->ring_buff_p = ring_buff_create(softc_p, 184 (size_t)ring_buff_capacity, (size_t)bytes_per_frame)) == NULL) { 185 return (1); 186 } 187 188 softc_p->ring_buff_p->read_ptr_pos[0] = 0; 189 190 /* allocate isoch channel */ 191 softc_p->sii.si_channel_mask = 0xFFFF000000000000; 192 softc_p->sii.si_bandwidth = bytes_per_pkt; 193 softc_p->sii.rsrc_fail_target = dcam_rsrc_fail; 194 softc_p->sii.single_evt_arg = softc_p; 195 softc_p->sii.si_speed = softc_p->targetinfo.current_max_speed; 196 197 if (t1394_alloc_isoch_single(softc_p->sl_handle, 198 &softc_p->sii, 0, &softc_p->sii_output_args, &softc_p->sii_hdl, 199 &failure) != DDI_SUCCESS) { 200 return (1); 201 } 202 203 /* 204 * At this point, all buffer memory has been allocated and 205 * mapped, and is tracked on a linear linked list. Now need to 206 * build the IXL. Done on a frame-by-frame basis. Could 207 * theoretically have been done at the same time as the mem alloc 208 * above, but hey, no need to be so fancy here. 209 * 210 * ixl buff size is bound by SHRT_MAX and needs to 211 * be a multiple of packet size 212 */ 213 max_ixl_buff_size = (SHRT_MAX / bytes_per_pkt) * bytes_per_pkt; 214 215 /* for each frame build frame's ixl list */ 216 for (frame = 0; frame < softc_p->ring_buff_p->num_buffs; frame++) { 217 218 buff_info_p = &(softc_p->ring_buff_p->buff_info_array_p[frame]); 219 220 /* 221 * if this is the 1st frame, put a IXL label at the top so a 222 * loop can be created later 223 */ 224 if (frame == 0) { 225 new_ixl_cmdp = kmem_zalloc( 226 sizeof (ixl1394_label_t), KM_SLEEP); 227 softc_p->ixlp = new_ixl_cmdp; 228 229 new_ixl_cmdp->ixl_opcode = IXL1394_OP_LABEL; 230 231 last_ixlp = softc_p->ixlp; 232 } 233 234 /* add wait-for-sync IXL command */ 235 new_ixl_sswp = kmem_zalloc( 236 sizeof (ixl1394_set_syncwait_t), KM_SLEEP); 237 238 new_ixl_sswp->ixl_opcode = IXL1394_OP_SET_SYNCWAIT; 239 240 last_ixlp->next_ixlp = (ixl1394_command_t *)new_ixl_sswp; 241 last_ixlp = (ixl1394_command_t *)new_ixl_sswp; 242 243 /* add in each dma cookie */ 244 for (cookie = 0; cookie < buff_info_p->dma_cookie_count; 245 cookie++) { 246 247 num_xfer_cmds = min(bytes_per_frame, 248 buff_info_p->dma_cookie.dmac_size) / 249 max_ixl_buff_size; 250 251 if (min(bytes_per_frame, 252 buff_info_p->dma_cookie.dmac_size) % 253 max_ixl_buff_size) { 254 num_xfer_cmds++; 255 } 256 257 num_bytes_left = min(bytes_per_frame, 258 buff_info_p->dma_cookie.dmac_size); 259 260 ixl_buff_kaddr = (caddr_t) 261 buff_info_p->dma_cookie.dmac_laddress; 262 263 ixl_buff_vaddr = buff_info_p->kaddr_p; 264 265 for (xfer_cmd = 0; xfer_cmd < (num_xfer_cmds + 1); 266 xfer_cmd++) { 267 num_bytes = min(num_bytes_left, 268 max_ixl_buff_size); 269 270 if (xfer_cmd == 0) { 271 new_ixl_xfpp = 272 kmem_zalloc( 273 sizeof (ixl1394_xfer_pkt_t), 274 KM_SLEEP); 275 276 new_ixl_xfpp->ixl_opcode = 277 IXL1394_OP_RECV_PKT_ST; 278 279 new_ixl_xfpp->ixl_buf._dmac_ll = 280 (uint64_t)ixl_buff_kaddr; 281 new_ixl_xfpp->size = 282 (uint16_t)bytes_per_pkt; 283 new_ixl_xfpp->mem_bufp = 284 ixl_buff_vaddr; 285 286 last_ixlp->next_ixlp = 287 (ixl1394_command_t *)new_ixl_xfpp; 288 last_ixlp = 289 (ixl1394_command_t *)new_ixl_xfpp; 290 291 num_bytes_left -= bytes_per_pkt; 292 ixl_buff_kaddr += bytes_per_pkt; 293 ixl_buff_vaddr += bytes_per_pkt; 294 295 continue; 296 } 297 298 /* allocate & init an IXL transfer command. */ 299 new_ixl_xfbp = 300 kmem_zalloc(sizeof (ixl1394_xfer_buf_t), 301 KM_SLEEP); 302 303 new_ixl_xfbp->ixl_opcode = IXL1394_OP_RECV_BUF; 304 305 new_ixl_xfbp->ixl_buf._dmac_ll = 306 (uint64_t)ixl_buff_kaddr; 307 new_ixl_xfbp->size = (uint16_t)num_bytes; 308 new_ixl_xfbp->pkt_size = bytes_per_pkt; 309 new_ixl_xfbp->mem_bufp = ixl_buff_vaddr; 310 311 last_ixlp->next_ixlp = 312 (ixl1394_command_t *)new_ixl_xfbp; 313 last_ixlp = 314 (ixl1394_command_t *)new_ixl_xfbp; 315 316 num_bytes_left -= num_bytes; 317 ixl_buff_kaddr += num_bytes; 318 ixl_buff_vaddr += num_bytes; 319 } 320 321 if (cookie > 0) { 322 ddi_dma_nextcookie(buff_info_p->dma_handle, 323 &(buff_info_p->dma_cookie)); 324 } 325 326 } 327 328 /* 329 * at this point, have finished a frame. put in a callback 330 */ 331 new_ixl_cbp = kmem_zalloc( 332 sizeof (ixl1394_callback_t), KM_SLEEP); 333 334 new_ixl_cbp->ixl_opcode = IXL1394_OP_CALLBACK; 335 336 new_ixl_cbp->callback = &dcam_frame_is_done; 337 new_ixl_cbp->callback_arg = NULL; 338 339 last_ixlp->next_ixlp = (ixl1394_command_t *)new_ixl_cbp; 340 last_ixlp = (ixl1394_command_t *)new_ixl_cbp; 341 } 342 343 /* 344 * for the final touch, put an IXL jump at the end to jump to the 345 * label at the top 346 */ 347 new_ixl_jmpp = kmem_zalloc(sizeof (ixl1394_jump_t), KM_SLEEP); 348 349 new_ixl_jmpp->ixl_opcode = IXL1394_OP_JUMP; 350 351 new_ixl_jmpp->label = softc_p->ixlp; 352 353 last_ixlp->next_ixlp = (ixl1394_command_t *)new_ixl_jmpp; 354 355 /* don't need this, but it's neater */ 356 last_ixlp = (ixl1394_command_t *)new_ixl_jmpp; 357 358 /* call fwim routine to alloc an isoch resource */ 359 isoch_args.ixlp = softc_p->ixlp; 360 isoch_args.channel_num = softc_p->sii_output_args.channel_num; 361 362 /* other misc args. note speed doesn't matter for isoch receive */ 363 isoch_args.idma_options = ID1394_LISTEN_PKT_MODE; 364 isoch_args.default_tag = 0; 365 isoch_args.default_sync = 1; 366 isoch_args.global_callback_arg = softc_p; 367 368 /* set the ISO channel number */ 369 data = (softc_p->sii_output_args.channel_num & 0xF) << 28; 370 371 /* set the ISO speed */ 372 data |= (softc_p->targetinfo.current_max_speed << 24); 373 374 reg_io.offs = DCAM1394_REG_OFFS_CUR_ISO_CHANNEL; 375 reg_io.val = data; 376 377 if (dcam_reg_write(softc_p, ®_io)) { 378 return (1); 379 } 380 381 result = 1234; 382 383 if (t1394_alloc_isoch_dma(softc_p->sl_handle, &isoch_args, 0, 384 &softc_p->isoch_handle, &result) != DDI_SUCCESS) { 385 return (1); 386 } 387 388 return (0); 389 } 390 391 392 /* 393 * dcam_frame_rcv_fini 394 */ 395 int 396 dcam_frame_rcv_fini(dcam_state_t *softc_p) 397 { 398 t1394_free_isoch_dma(softc_p->sl_handle, 0, &softc_p->isoch_handle); 399 400 softc_p->isoch_handle = NULL; 401 402 t1394_free_isoch_single(softc_p->sl_handle, &softc_p->sii_hdl, 0); 403 404 return (0); 405 } 406 407 408 /* 409 * dcam_frame_rcv_start 410 */ 411 int 412 dcam_frame_rcv_start(dcam_state_t *softc_p) 413 { 414 id1394_isoch_dma_ctrlinfo_t idma_ctrlinfo; /* currently not used */ 415 int32_t result; 416 dcam1394_reg_io_t reg_io; 417 418 if ((t1394_start_isoch_dma(softc_p->sl_handle, softc_p->isoch_handle, 419 &idma_ctrlinfo, 0, &result)) != DDI_SUCCESS) { 420 return (1); 421 } 422 423 reg_io.offs = DCAM1394_REG_OFFS_ISO_EN; 424 reg_io.val = 0x80000000; 425 426 if (dcam_reg_write(softc_p, ®_io)) { 427 return (1); 428 } 429 430 softc_p->flags |= DCAM1394_FLAG_FRAME_RCVING; 431 432 return (0); 433 } 434 435 436 /* 437 * dcam_frame_rcv_stop 438 */ 439 int 440 dcam_frame_rcv_stop(dcam_state_t *softc_p) 441 { 442 dcam1394_reg_io_t reg_io; 443 444 /* if resources have already been cleared, nothing to do */ 445 if (!(softc_p->flags & DCAM1394_FLAG_FRAME_RCV_INIT)) { 446 return (0); 447 } 448 449 reg_io.offs = DCAM1394_REG_OFFS_ISO_EN; 450 reg_io.val = 0; 451 452 (void) dcam_reg_write(softc_p, ®_io); 453 454 t1394_stop_isoch_dma(softc_p->sl_handle, softc_p->isoch_handle, 0); 455 t1394_free_isoch_dma(softc_p->sl_handle, 0, &softc_p->isoch_handle); 456 t1394_free_isoch_single(softc_p->sl_handle, &softc_p->sii_hdl, 0); 457 458 dcam_free_resources(softc_p); 459 460 return (0); 461 } 462 463 464 void 465 dcam_free_resources(dcam_state_t *softc_p) 466 { 467 ixl1394_command_t *ptr; 468 ixl1394_command_t *tmp; 469 470 /* 471 * The following fixes a memory leak. See bug #4423667. 472 * The original code only released memory for the first frame. 473 */ 474 475 /* free ixl opcode resources */ 476 ptr = softc_p->ixlp; 477 478 while (ptr != NULL) { 479 tmp = ptr; 480 ptr = ptr->next_ixlp; 481 482 switch (tmp->ixl_opcode) { 483 case IXL1394_OP_LABEL: 484 kmem_free(tmp, sizeof (ixl1394_label_t)); 485 break; 486 487 case IXL1394_OP_SET_SYNCWAIT: 488 kmem_free(tmp, sizeof (ixl1394_set_syncwait_t)); 489 break; 490 491 case IXL1394_OP_RECV_PKT_ST: 492 kmem_free(tmp, sizeof (ixl1394_xfer_pkt_t)); 493 break; 494 495 case IXL1394_OP_RECV_BUF: 496 kmem_free(tmp, sizeof (ixl1394_xfer_buf_t)); 497 break; 498 499 case IXL1394_OP_CALLBACK: 500 kmem_free(tmp, sizeof (ixl1394_callback_t)); 501 break; 502 503 case IXL1394_OP_JUMP: 504 kmem_free(tmp, sizeof (ixl1394_jump_t)); 505 break; 506 } 507 } 508 509 /* 510 * free ring buff and indicate that the resources have been cleared 511 */ 512 ring_buff_free(softc_p, softc_p->ring_buff_p); 513 514 softc_p->flags &= ~DCAM1394_FLAG_FRAME_RCV_INIT; 515 softc_p->ixlp = NULL; 516 } 517 518 519 /* 520 * dcam_frame_is_done 521 * 522 * This routine is called after DMA engine has stored a single received 523 * frame in ring buffer position pointed to by write pointer; this 524 * routine marks the frame's vid mode, timestamp, and sequence number 525 * 526 * Store received frame in ring buffer position pointed to by write pointer. 527 * Increment write pointer. If write pointer is pointing to the same 528 * position as read pointer, increment read pointer. 529 * 530 * If device driver is processing a user process's read() request 531 * invalidate the read() request processing operation. 532 * 533 */ 534 535 /* ARGSUSED */ 536 void 537 dcam_frame_is_done(void *ssp, ixl1394_callback_t *ixlp) 538 { 539 dcam_state_t *softc_p; 540 int num_read_ptrs; 541 int read_ptr_id; 542 int vid_mode; 543 size_t write_ptr_pos; 544 ring_buff_t *ring_buff_p; 545 unsigned int seq_num; 546 547 /* 548 * Store received frame in ring buffer position pointed to by 549 * write pointer (this routine is called after DMA engine has 550 * stored a single received frame in ring buffer position pointed 551 * to by write pointer; this routine marks the frame's vid mode, 552 * timestamp, and sequence number) 553 */ 554 555 if ((softc_p = (dcam_state_t *)ssp) == NULL) { 556 return; 557 } 558 559 if ((ring_buff_p = softc_p->ring_buff_p) == NULL) { 560 return; 561 } 562 563 mutex_enter(&softc_p->dcam_frame_is_done_mutex); 564 565 write_ptr_pos = ring_buff_write_ptr_pos_get(ring_buff_p); 566 567 /* mark vid mode */ 568 vid_mode = 569 softc_p-> 570 param_attr[DCAM1394_PARAM_VID_MODE][DCAM1394_SUBPARAM_NONE]; 571 ring_buff_p->buff_info_array_p[write_ptr_pos].vid_mode = vid_mode; 572 573 574 /* update sequence counter overflow in param_status */ 575 if (softc_p->seq_count == 0xffffffff) 576 softc_p->param_status |= 577 DCAM1394_STATUS_FRAME_SEQ_NUM_COUNT_OVERFLOW; 578 579 580 /* mark frame's sequence number */ 581 ring_buff_p->buff_info_array_p[write_ptr_pos].seq_num = 582 softc_p->seq_count++; 583 584 seq_num = ring_buff_p->buff_info_array_p[write_ptr_pos].seq_num; 585 586 587 /* mark frame's timestamp */ 588 ring_buff_p->buff_info_array_p[write_ptr_pos].timestamp = gethrtime(); 589 590 591 /* increment write pointer */ 592 ring_buff_write_ptr_incr(ring_buff_p); 593 594 num_read_ptrs = 1; 595 596 for (read_ptr_id = 0; read_ptr_id < num_read_ptrs; read_ptr_id++) { 597 598 /* 599 * if write pointer is pointing to the same position as 600 * read pointer 601 */ 602 603 if ((ring_buff_write_ptr_pos_get(ring_buff_p) == 604 ring_buff_read_ptr_pos_get(ring_buff_p, read_ptr_id)) && 605 (seq_num != 0)) { 606 607 /* increment read pointer */ 608 ring_buff_read_ptr_incr(ring_buff_p, read_ptr_id); 609 610 /* 611 * if device driver is processing a user 612 * process's read() request 613 */ 614 if (softc_p->reader_flags[read_ptr_id] & 615 DCAM1394_FLAG_READ_REQ_PROC) { 616 617 /* 618 * invalidate the read() request processing 619 * operation 620 */ 621 softc_p->reader_flags[read_ptr_id] |= 622 DCAM1394_FLAG_READ_REQ_INVALID; 623 } 624 625 /* inform user app that we have lost one frame */ 626 softc_p->param_status |= 627 DCAM1394_STATUS_RING_BUFF_LOST_FRAME; 628 } 629 } 630 631 /* inform user app that we have received one frame */ 632 softc_p->param_status |= DCAM1394_STATUS_FRAME_RCV_DONE; 633 634 mutex_exit(&softc_p->dcam_frame_is_done_mutex); 635 } 636 637 638 /* ARGSUSED */ 639 static void 640 dcam_rsrc_fail(t1394_isoch_single_handle_t t1394_single_hdl, 641 opaque_t single_evt_arg, t1394_isoch_rsrc_error_t fail_args) 642 { 643 cmn_err(CE_NOTE, "dcam_rsrc_fail(): unable to re-alloc resources\n"); 644 } 645