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 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 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 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 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 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 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 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 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