1 /*- 2 * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/param.h> 31 #include <sys/systm.h> 32 #include <sys/bus.h> 33 #include <sys/kernel.h> 34 #include <sys/lock.h> 35 #include <sys/module.h> 36 #include <sys/mutex.h> 37 #include <sys/rman.h> 38 #include <sys/sema.h> 39 #include <machine/bus.h> 40 41 #include <dev/ofw/ofw_bus.h> 42 #include <dev/ofw/ofw_bus_subr.h> 43 44 #include <arm/broadcom/bcm2835/bcm2835_mbox.h> 45 #include <arm/broadcom/bcm2835/bcm2835_mbox_prop.h> 46 #include <arm/broadcom/bcm2835/bcm2835_vcbus.h> 47 48 #include "mbox_if.h" 49 50 #define REG_READ 0x00 51 #define REG_POL 0x10 52 #define REG_SENDER 0x14 53 #define REG_STATUS 0x18 54 #define STATUS_FULL 0x80000000 55 #define STATUS_EMPTY 0x40000000 56 #define REG_CONFIG 0x1C 57 #define CONFIG_DATA_IRQ 0x00000001 58 #define REG_WRITE 0x20 /* This is Mailbox 1 address */ 59 60 #define MBOX_MSG(chan, data) (((data) & ~0xf) | ((chan) & 0xf)) 61 #define MBOX_CHAN(msg) ((msg) & 0xf) 62 #define MBOX_DATA(msg) ((msg) & ~0xf) 63 64 #define MBOX_LOCK(sc) do { \ 65 mtx_lock(&(sc)->lock); \ 66 } while(0) 67 68 #define MBOX_UNLOCK(sc) do { \ 69 mtx_unlock(&(sc)->lock); \ 70 } while(0) 71 72 #ifdef DEBUG 73 #define dprintf(fmt, args...) printf(fmt, ##args) 74 #else 75 #define dprintf(fmt, args...) 76 #endif 77 78 struct bcm_mbox_softc { 79 struct mtx lock; 80 struct resource * mem_res; 81 struct resource * irq_res; 82 void* intr_hl; 83 bus_space_tag_t bst; 84 bus_space_handle_t bsh; 85 int msg[BCM2835_MBOX_CHANS]; 86 struct sema sema[BCM2835_MBOX_CHANS]; 87 }; 88 89 #define mbox_read_4(sc, reg) \ 90 bus_space_read_4((sc)->bst, (sc)->bsh, reg) 91 #define mbox_write_4(sc, reg, val) \ 92 bus_space_write_4((sc)->bst, (sc)->bsh, reg, val) 93 94 static int 95 bcm_mbox_read_msg(struct bcm_mbox_softc *sc, int *ochan) 96 { 97 uint32_t data; 98 uint32_t msg; 99 int chan; 100 101 msg = mbox_read_4(sc, REG_READ); 102 dprintf("bcm_mbox_intr: raw data %08x\n", msg); 103 chan = MBOX_CHAN(msg); 104 data = MBOX_DATA(msg); 105 if (sc->msg[chan]) { 106 printf("bcm_mbox_intr: channel %d oveflow\n", chan); 107 return (1); 108 } 109 dprintf("bcm_mbox_intr: chan %d, data %08x\n", chan, data); 110 sc->msg[chan] = msg; 111 112 if (ochan != NULL) 113 *ochan = chan; 114 115 return (0); 116 } 117 118 static void 119 bcm_mbox_intr(void *arg) 120 { 121 struct bcm_mbox_softc *sc = arg; 122 int chan; 123 124 while (!(mbox_read_4(sc, REG_STATUS) & STATUS_EMPTY)) 125 if (bcm_mbox_read_msg(sc, &chan) == 0) 126 sema_post(&sc->sema[chan]); 127 } 128 129 static int 130 bcm_mbox_probe(device_t dev) 131 { 132 133 if (!ofw_bus_status_okay(dev)) 134 return (ENXIO); 135 136 if (ofw_bus_is_compatible(dev, "broadcom,bcm2835-mbox")) { 137 device_set_desc(dev, "BCM2835 VideoCore Mailbox"); 138 return(BUS_PROBE_DEFAULT); 139 } 140 141 return (ENXIO); 142 } 143 144 static int 145 bcm_mbox_attach(device_t dev) 146 { 147 struct bcm_mbox_softc *sc = device_get_softc(dev); 148 int i; 149 int rid = 0; 150 151 sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 152 if (sc->mem_res == NULL) { 153 device_printf(dev, "could not allocate memory resource\n"); 154 return (ENXIO); 155 } 156 157 sc->bst = rman_get_bustag(sc->mem_res); 158 sc->bsh = rman_get_bushandle(sc->mem_res); 159 160 rid = 0; 161 sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); 162 if (sc->irq_res == NULL) { 163 device_printf(dev, "could not allocate interrupt resource\n"); 164 return (ENXIO); 165 } 166 167 /* Setup and enable the timer */ 168 if (bus_setup_intr(dev, sc->irq_res, INTR_MPSAFE | INTR_TYPE_MISC, 169 NULL, bcm_mbox_intr, sc, &sc->intr_hl) != 0) { 170 bus_release_resource(dev, SYS_RES_IRQ, rid, sc->irq_res); 171 device_printf(dev, "Unable to setup the clock irq handler.\n"); 172 return (ENXIO); 173 } 174 175 mtx_init(&sc->lock, "vcio mbox", NULL, MTX_DEF); 176 for (i = 0; i < BCM2835_MBOX_CHANS; i++) { 177 sc->msg[i] = 0; 178 sema_init(&sc->sema[i], 0, "mbox"); 179 } 180 181 /* Read all pending messages */ 182 while ((mbox_read_4(sc, REG_STATUS) & STATUS_EMPTY) == 0) 183 (void)mbox_read_4(sc, REG_READ); 184 185 mbox_write_4(sc, REG_CONFIG, CONFIG_DATA_IRQ); 186 187 return (0); 188 } 189 190 /* 191 * Mailbox API 192 */ 193 static int 194 bcm_mbox_write(device_t dev, int chan, uint32_t data) 195 { 196 int limit = 1000; 197 struct bcm_mbox_softc *sc = device_get_softc(dev); 198 199 dprintf("bcm_mbox_write: chan %d, data %08x\n", chan, data); 200 MBOX_LOCK(sc); 201 while ((mbox_read_4(sc, REG_STATUS) & STATUS_FULL) && --limit) 202 DELAY(5); 203 if (limit == 0) { 204 printf("bcm_mbox_write: STATUS_FULL stuck"); 205 MBOX_UNLOCK(sc); 206 return (EAGAIN); 207 } 208 mbox_write_4(sc, REG_WRITE, MBOX_MSG(chan, data)); 209 MBOX_UNLOCK(sc); 210 211 return (0); 212 } 213 214 static int 215 bcm_mbox_read(device_t dev, int chan, uint32_t *data) 216 { 217 struct bcm_mbox_softc *sc = device_get_softc(dev); 218 int err, read_chan; 219 220 dprintf("bcm_mbox_read: chan %d\n", chan); 221 222 err = 0; 223 MBOX_LOCK(sc); 224 if (!cold) { 225 while (sema_trywait(&sc->sema[chan]) == 0) { 226 /* do not unlock sc while waiting for the mbox */ 227 if (sema_timedwait(&sc->sema[chan], 10*hz) == 0) 228 break; 229 printf("timeout sema for chan %d\n", chan); 230 } 231 } else { 232 do { 233 /* Wait for a message */ 234 while ((mbox_read_4(sc, REG_STATUS) & STATUS_EMPTY)) 235 ; 236 /* Read the message */ 237 if (bcm_mbox_read_msg(sc, &read_chan) != 0) { 238 err = EINVAL; 239 goto out; 240 } 241 } while (read_chan != chan); 242 } 243 /* 244 * get data from intr handler, the same channel is never coming 245 * because of holding sc lock. 246 */ 247 *data = MBOX_DATA(sc->msg[chan]); 248 sc->msg[chan] = 0; 249 out: 250 MBOX_UNLOCK(sc); 251 dprintf("bcm_mbox_read: chan %d, data %08x\n", chan, *data); 252 253 return (err); 254 } 255 256 static device_method_t bcm_mbox_methods[] = { 257 DEVMETHOD(device_probe, bcm_mbox_probe), 258 DEVMETHOD(device_attach, bcm_mbox_attach), 259 260 DEVMETHOD(mbox_read, bcm_mbox_read), 261 DEVMETHOD(mbox_write, bcm_mbox_write), 262 263 DEVMETHOD_END 264 }; 265 266 static driver_t bcm_mbox_driver = { 267 "mbox", 268 bcm_mbox_methods, 269 sizeof(struct bcm_mbox_softc), 270 }; 271 272 static devclass_t bcm_mbox_devclass; 273 274 DRIVER_MODULE(mbox, simplebus, bcm_mbox_driver, bcm_mbox_devclass, 0, 0); 275 276 static void 277 bcm2835_mbox_dma_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err) 278 { 279 bus_addr_t *addr; 280 281 if (err) 282 return; 283 addr = (bus_addr_t *)arg; 284 *addr = PHYS_TO_VCBUS(segs[0].ds_addr); 285 } 286 287 static void * 288 bcm2835_mbox_init_dma(device_t dev, size_t len, bus_dma_tag_t *tag, 289 bus_dmamap_t *map, bus_addr_t *phys) 290 { 291 void *buf; 292 int err; 293 294 err = bus_dma_tag_create(bus_get_dma_tag(dev), 16, 0, 295 BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 296 len, 1, len, 0, NULL, NULL, tag); 297 if (err != 0) { 298 device_printf(dev, "can't create DMA tag\n"); 299 return (NULL); 300 } 301 302 err = bus_dmamem_alloc(*tag, &buf, 0, map); 303 if (err != 0) { 304 bus_dma_tag_destroy(*tag); 305 device_printf(dev, "can't allocate dmamem\n"); 306 return (NULL); 307 } 308 309 err = bus_dmamap_load(*tag, *map, buf, len, bcm2835_mbox_dma_cb, 310 phys, 0); 311 if (err != 0) { 312 bus_dmamem_free(*tag, buf, *map); 313 bus_dma_tag_destroy(*tag); 314 device_printf(dev, "can't load DMA map\n"); 315 return (NULL); 316 } 317 318 return (buf); 319 } 320 321 static int 322 bcm2835_mbox_err(device_t dev, bus_addr_t msg_phys, uint32_t resp_phys, 323 struct bcm2835_mbox_hdr *msg, size_t len) 324 { 325 int idx; 326 struct bcm2835_mbox_tag_hdr *tag; 327 uint8_t *last; 328 329 if ((uint32_t)msg_phys != resp_phys) { 330 device_printf(dev, "response channel mismatch\n"); 331 return (EIO); 332 } 333 if (msg->code != BCM2835_MBOX_CODE_RESP_SUCCESS) { 334 device_printf(dev, "mbox response error\n"); 335 return (EIO); 336 } 337 338 /* Loop until the end tag. */ 339 tag = (struct bcm2835_mbox_tag_hdr *)(msg + 1); 340 last = (uint8_t *)msg + len; 341 for (idx = 0; tag->tag != 0; idx++) { 342 if ((tag->val_len & BCM2835_MBOX_TAG_VAL_LEN_RESPONSE) == 0) { 343 device_printf(dev, "tag %d response error\n", idx); 344 return (EIO); 345 } 346 /* Clear the response bit. */ 347 tag->val_len &= ~BCM2835_MBOX_TAG_VAL_LEN_RESPONSE; 348 349 /* Next tag. */ 350 tag = (struct bcm2835_mbox_tag_hdr *)((uint8_t *)tag + 351 sizeof(*tag) + tag->val_buf_size); 352 353 if ((uint8_t *)tag > last) { 354 device_printf(dev, "mbox buffer size error\n"); 355 return (EIO); 356 } 357 } 358 359 return (0); 360 } 361 362 int 363 bcm2835_mbox_set_power_state(device_t dev, uint32_t device_id, boolean_t on) 364 { 365 struct msg_set_power_state *msg; 366 bus_dma_tag_t msg_tag; 367 bus_dmamap_t msg_map; 368 bus_addr_t msg_phys; 369 uint32_t reg; 370 device_t mbox; 371 372 /* get mbox device */ 373 mbox = devclass_get_device(devclass_find("mbox"), 0); 374 if (mbox == NULL) { 375 device_printf(dev, "can't find mbox\n"); 376 return (ENXIO); 377 } 378 379 /* Allocate memory for the message */ 380 msg = bcm2835_mbox_init_dma(dev, sizeof(*msg), &msg_tag, &msg_map, 381 &msg_phys); 382 if (msg == NULL) 383 return (ENOMEM); 384 385 memset(msg, 0, sizeof(*msg)); 386 msg->hdr.buf_size = sizeof(*msg); 387 msg->hdr.code = BCM2835_MBOX_CODE_REQ; 388 msg->tag_hdr.tag = BCM2835_MBOX_TAG_SET_POWER_STATE; 389 msg->tag_hdr.val_buf_size = sizeof(msg->body); 390 msg->tag_hdr.val_len = sizeof(msg->body.req); 391 msg->body.req.device_id = device_id; 392 msg->body.req.state = (on ? BCM2835_MBOX_POWER_ON : 0) | 393 BCM2835_MBOX_POWER_WAIT; 394 msg->end_tag = 0; 395 396 bus_dmamap_sync(msg_tag, msg_map, 397 BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); 398 399 MBOX_WRITE(mbox, BCM2835_MBOX_CHAN_PROP, (uint32_t)msg_phys); 400 MBOX_READ(mbox, BCM2835_MBOX_CHAN_PROP, ®); 401 402 bus_dmamap_unload(msg_tag, msg_map); 403 bus_dmamem_free(msg_tag, msg, msg_map); 404 bus_dma_tag_destroy(msg_tag); 405 406 return (0); 407 } 408 409 int 410 bcm2835_mbox_get_clock_rate(device_t dev, uint32_t clock_id, uint32_t *hz) 411 { 412 struct msg_get_clock_rate *msg; 413 bus_dma_tag_t msg_tag; 414 bus_dmamap_t msg_map; 415 bus_addr_t msg_phys; 416 uint32_t reg; 417 device_t mbox; 418 419 /* get mbox device */ 420 mbox = devclass_get_device(devclass_find("mbox"), 0); 421 if (mbox == NULL) { 422 device_printf(dev, "can't find mbox\n"); 423 return (ENXIO); 424 } 425 426 /* Allocate memory for the message */ 427 msg = bcm2835_mbox_init_dma(dev, sizeof(*msg), &msg_tag, &msg_map, 428 &msg_phys); 429 if (msg == NULL) 430 return (ENOMEM); 431 432 memset(msg, 0, sizeof(*msg)); 433 msg->hdr.buf_size = sizeof(*msg); 434 msg->hdr.code = BCM2835_MBOX_CODE_REQ; 435 msg->tag_hdr.tag = BCM2835_MBOX_TAG_GET_CLOCK_RATE; 436 msg->tag_hdr.val_buf_size = sizeof(msg->body); 437 msg->tag_hdr.val_len = sizeof(msg->body.req); 438 msg->body.req.clock_id = clock_id; 439 msg->end_tag = 0; 440 441 bus_dmamap_sync(msg_tag, msg_map, BUS_DMASYNC_PREWRITE); 442 MBOX_WRITE(mbox, BCM2835_MBOX_CHAN_PROP, (uint32_t)msg_phys); 443 bus_dmamap_sync(msg_tag, msg_map, BUS_DMASYNC_POSTWRITE); 444 445 bus_dmamap_sync(msg_tag, msg_map, BUS_DMASYNC_PREREAD); 446 MBOX_READ(mbox, BCM2835_MBOX_CHAN_PROP, ®); 447 bus_dmamap_sync(msg_tag, msg_map, BUS_DMASYNC_POSTREAD); 448 449 *hz = msg->body.resp.rate_hz; 450 451 bus_dmamap_unload(msg_tag, msg_map); 452 bus_dmamem_free(msg_tag, msg, msg_map); 453 bus_dma_tag_destroy(msg_tag); 454 455 return (0); 456 } 457 458 int 459 bcm2835_mbox_fb_get_w_h(device_t dev, struct bcm2835_fb_config *fb) 460 { 461 device_t mbox; 462 int err; 463 bus_dma_tag_t msg_tag; 464 bus_dmamap_t msg_map; 465 bus_addr_t msg_phys; 466 struct msg_fb_get_w_h *msg; 467 uint32_t reg; 468 469 /* get mbox device */ 470 mbox = devclass_get_device(devclass_find("mbox"), 0); 471 if (mbox == NULL) { 472 device_printf(dev, "can't find mbox\n"); 473 return (ENXIO); 474 } 475 476 /* Allocate memory for the message */ 477 msg = bcm2835_mbox_init_dma(dev, sizeof(*msg), &msg_tag, &msg_map, 478 &msg_phys); 479 if (msg == NULL) 480 return (ENOMEM); 481 482 memset(msg, 0, sizeof(*msg)); 483 msg->hdr.buf_size = sizeof(*msg); 484 msg->hdr.code = BCM2835_MBOX_CODE_REQ; 485 BCM2835_MBOX_INIT_TAG(&msg->physical_w_h, GET_PHYSICAL_W_H); 486 msg->physical_w_h.tag_hdr.val_len = 0; 487 BCM2835_MBOX_INIT_TAG(&msg->virtual_w_h, GET_VIRTUAL_W_H); 488 msg->virtual_w_h.tag_hdr.val_len = 0; 489 BCM2835_MBOX_INIT_TAG(&msg->offset, GET_VIRTUAL_OFFSET); 490 msg->offset.tag_hdr.val_len = 0; 491 msg->end_tag = 0; 492 493 bus_dmamap_sync(msg_tag, msg_map, BUS_DMASYNC_PREWRITE); 494 MBOX_WRITE(mbox, BCM2835_MBOX_CHAN_PROP, (uint32_t)msg_phys); 495 bus_dmamap_sync(msg_tag, msg_map, BUS_DMASYNC_POSTWRITE); 496 497 bus_dmamap_sync(msg_tag, msg_map, BUS_DMASYNC_PREREAD); 498 MBOX_READ(mbox, BCM2835_MBOX_CHAN_PROP, ®); 499 bus_dmamap_sync(msg_tag, msg_map, BUS_DMASYNC_POSTREAD); 500 501 err = bcm2835_mbox_err(dev, msg_phys, reg, &msg->hdr, sizeof(*msg)); 502 if (err == 0) { 503 fb->xres = msg->physical_w_h.body.resp.width; 504 fb->yres = msg->physical_w_h.body.resp.height; 505 fb->vxres = msg->virtual_w_h.body.resp.width; 506 fb->vyres = msg->virtual_w_h.body.resp.height; 507 fb->xoffset = msg->offset.body.resp.x; 508 fb->yoffset = msg->offset.body.resp.y; 509 } 510 511 bus_dmamap_unload(msg_tag, msg_map); 512 bus_dmamem_free(msg_tag, msg, msg_map); 513 bus_dma_tag_destroy(msg_tag); 514 515 return (err); 516 } 517 518 int 519 bcm2835_mbox_fb_init(device_t dev, struct bcm2835_fb_config *fb) 520 { 521 device_t mbox; 522 int err; 523 bus_dma_tag_t msg_tag; 524 bus_dmamap_t msg_map; 525 bus_addr_t msg_phys; 526 struct msg_fb_setup *msg; 527 uint32_t reg; 528 529 /* get mbox device */ 530 mbox = devclass_get_device(devclass_find("mbox"), 0); 531 if (mbox == NULL) { 532 device_printf(dev, "can't find mbox\n"); 533 return (ENXIO); 534 } 535 536 /* Allocate memory for the message */ 537 msg = bcm2835_mbox_init_dma(dev, sizeof(*msg), &msg_tag, &msg_map, 538 &msg_phys); 539 if (msg == NULL) 540 return (ENOMEM); 541 542 memset(msg, 0, sizeof(*msg)); 543 msg->hdr.buf_size = sizeof(*msg); 544 msg->hdr.code = BCM2835_MBOX_CODE_REQ; 545 BCM2835_MBOX_INIT_TAG(&msg->physical_w_h, SET_PHYSICAL_W_H); 546 msg->physical_w_h.body.req.width = fb->xres; 547 msg->physical_w_h.body.req.height = fb->yres; 548 BCM2835_MBOX_INIT_TAG(&msg->virtual_w_h, SET_VIRTUAL_W_H); 549 msg->virtual_w_h.body.req.width = fb->vxres; 550 msg->virtual_w_h.body.req.height = fb->vyres; 551 BCM2835_MBOX_INIT_TAG(&msg->offset, GET_VIRTUAL_OFFSET); 552 msg->offset.body.req.x = fb->xoffset; 553 msg->offset.body.req.y = fb->yoffset; 554 BCM2835_MBOX_INIT_TAG(&msg->depth, SET_DEPTH); 555 msg->depth.body.req.bpp = fb->bpp; 556 BCM2835_MBOX_INIT_TAG(&msg->alpha, SET_ALPHA_MODE); 557 msg->alpha.body.req.alpha = BCM2835_MBOX_ALPHA_MODE_IGNORED; 558 BCM2835_MBOX_INIT_TAG(&msg->buffer, ALLOCATE_BUFFER); 559 msg->buffer.body.req.alignment = PAGE_SIZE; 560 BCM2835_MBOX_INIT_TAG(&msg->pitch, GET_PITCH); 561 msg->end_tag = 0; 562 563 bus_dmamap_sync(msg_tag, msg_map, BUS_DMASYNC_PREWRITE); 564 MBOX_WRITE(mbox, BCM2835_MBOX_CHAN_PROP, (uint32_t)msg_phys); 565 bus_dmamap_sync(msg_tag, msg_map, BUS_DMASYNC_POSTWRITE); 566 567 bus_dmamap_sync(msg_tag, msg_map, BUS_DMASYNC_PREREAD); 568 MBOX_READ(mbox, BCM2835_MBOX_CHAN_PROP, ®); 569 bus_dmamap_sync(msg_tag, msg_map, BUS_DMASYNC_POSTREAD); 570 571 err = bcm2835_mbox_err(dev, msg_phys, reg, &msg->hdr, sizeof(*msg)); 572 if (err == 0) { 573 fb->xres = msg->physical_w_h.body.resp.width; 574 fb->yres = msg->physical_w_h.body.resp.height; 575 fb->vxres = msg->virtual_w_h.body.resp.width; 576 fb->vyres = msg->virtual_w_h.body.resp.height; 577 fb->xoffset = msg->offset.body.resp.x; 578 fb->yoffset = msg->offset.body.resp.y; 579 fb->pitch = msg->pitch.body.resp.pitch; 580 fb->base = msg->buffer.body.resp.fb_address; 581 fb->size = msg->buffer.body.resp.fb_size; 582 } 583 584 bus_dmamap_unload(msg_tag, msg_map); 585 bus_dmamem_free(msg_tag, msg, msg_map); 586 bus_dma_tag_destroy(msg_tag); 587 588 return (err); 589 } 590