1/* 2 * Copyright (c) 2014, ETH Zurich. 3 * All rights reserved. 4 * 5 * This file is distributed under the terms in the attached LICENSE file. 6 * If you do not find this file, copies can be found by writing to: 7 * ETH Zurich D-INFK, CAB F.78, Universitaetstr 6, CH-8092 Zurich. 8 */ 9 10#include <stdio.h> 11#include <stdlib.h> 12 13#include <barrelfish/barrelfish.h> 14#include <barrelfish/waitset.h> 15#include <barrelfish/inthandler.h> 16#include <driverkit/driverkit.h> 17 18#include <thc/thc.h> 19 20#include <arch/arm/omap44xx/device_registers.h> 21#include <maps/omap44xx_map.h> 22 23#include "sdma.h" 24#include "omap_sdma.h" 25 26/** 27 * \brief Interrupt callback which will be called when a channel interrupt 28 * occurs. 29 * 30 * \param channel Channel which triggered the interrupt 31 * \param err State of the channel, SYS_ERR_OK if transfer completed 32 */ 33static void sdma_irq_handler(struct sdma_driver_state* st, omap_sdma_channel_t channel, errval_t err) 34{ 35 st->channel_state[channel].err = err; 36 THCSchedule(st->channel_state[channel].request); 37} 38 39/** 40 * \brief Execute a transfer on the SDMA engine. Blocks until the transfer is 41 * completed or an error occurred. 42 * 43 * \param conf Pointer to valid & initialized channel configuration 44 */ 45static errval_t run_omap_sdma_transfer(struct sdma_driver_state* st, struct omap_sdma_channel_conf *conf) 46{ 47 errval_t err; 48 omap_sdma_channel_t channel; 49 50 err = omap_sdma_allocate_channel(st, &channel); 51 if (err_is_fail(err)) return err; 52 53 // configure and enable allocated channel 54 omap_sdma_set_channel_conf(st, channel, conf); 55 omap_sdma_enable_channel(st, channel, true); 56 57 // this task will be rescheduled by the IRQ handler 58 THCSuspend(&st->channel_state[channel].request); 59 60 // read status flag set by IRQ handler 61 err = st->channel_state[channel].err; 62 63 omap_sdma_free_channel(st, channel); 64 65 return err; 66} 67 68/** 69 * \brief Converts the pixel size of the Flounder interface description to the 70 * element size needed for the hardware. 71 * 72 */ 73static omap44xx_sdma_data_type_t extract_data_type(omap_sdma_data_type_t pixel_size) 74{ 75 omap44xx_sdma_data_type_t data_type; 76 77 switch(pixel_size) { 78 case omap_sdma_DATA_TYPE_8BIT: 79 data_type = omap44xx_sdma_DATA_TYPE_8BIT; 80 break; 81 case omap_sdma_DATA_TYPE_16BIT: 82 data_type = omap44xx_sdma_DATA_TYPE_16BIT; 83 break; 84 case omap_sdma_DATA_TYPE_32BIT: 85 default: 86 data_type = omap44xx_sdma_DATA_TYPE_32BIT; 87 break; 88 } 89 90 return data_type; 91} 92 93/** 94 * \brief Initializes a configuration struct for the given parameters. It is 95 * the callers responsibility ensure that the start address and count values 96 * are valid. 97 */ 98static void init_channel_conf(struct omap_sdma_channel_conf *conf, 99 lpaddr_t dst_start, lpaddr_t src_start, 100 int32_t dst_x_modify, int32_t dst_y_modify, 101 int32_t src_x_modify, int32_t src_y_modify, 102 omap_sdma_count_2d_t count, 103 omap44xx_sdma_color_mode_t color_mode, uint32_t color) 104{ 105 assert(conf); 106 107 omap44xx_sdma_data_type_t data_type = extract_data_type(count.pixel_size); 108 109 // OMAP4460 TRM: SDMA 16.4.5 110 int32_t es = 1 << (data_type); 111 int32_t src_element_index = (src_x_modify - 1) * es + 1; 112 int32_t src_frame_index = (src_y_modify - 1) * es + 1; 113 114 int32_t dst_element_index = (dst_x_modify - 1) * es + 1; 115 int32_t dst_frame_index = (dst_y_modify - 1) * es + 1; 116 117 *conf = (struct omap_sdma_channel_conf) { 118 // low priority for software-synchronized transfers 119 .read_priority = omap44xx_sdma_PORT_PRIORITY_LOW, 120 .write_priority = omap44xx_sdma_PORT_PRIORITY_LOW, 121 122 // normal copy/transparent copy/constant fill 123 .color_mode = color_mode, 124 .color = color, 125 126 // wait for last write to complete 127 .write_mode = omap44xx_sdma_WRITE_MODE_LAST_NON_POSTED, 128 129 // channel linking is not used 130 .enable_link = false, 131 .next_channel = 0, 132 133 // always use double indexing mode, packed & burst transfer 134 .src_conf = { 135 .start_address = src_start, 136 .addr_mode = omap44xx_sdma_ADDR_MODE_DOUBLE_IDX, 137 .element_index = src_element_index, 138 .frame_index = src_frame_index, 139 .packed_transfer = omap44xx_sdma_SRC_PACKED_ENABLE, 140 .burst_mode = omap44xx_sdma_BURST_EN_64BYTE, 141 }, 142 143 .dst_conf = { 144 .start_address = dst_start, 145 .addr_mode = omap44xx_sdma_ADDR_MODE_DOUBLE_IDX, 146 .element_index = dst_element_index, 147 .frame_index = dst_frame_index, 148 .packed_transfer = omap44xx_sdma_DST_PACKED_ENABLE, 149 .burst_mode = omap44xx_sdma_BURST_EN_64BYTE, 150 }, 151 152 // conversion of omap_count_2d 153 .transfer_size = { 154 .element_number = count.x_count, 155 .frame_number = count.y_count, 156 .data_type = data_type, 157 }, 158 }; 159} 160 161/** 162 * \brief Splits the physical frame size into two factors, as the SDMA engine 163 * needs the memory region to be specified in EN * FN. 164 * 165 * \param bits Size of the frame as a power of two. 166 * \param retcount Pointer to count struct which will be filled with frame size 167 */ 168static void init_count_1d(uint8_t bits, omap_sdma_count_2d_t *retcount) 169{ 170 assert(retcount); 171 172 // split frame size: 2^b = 2^(b/2) * 2^(b - b/2) 173 uint8_t x_bits = MIN(bits, OMAP44XX_SDMA_MAX_EN_BITS); 174 uint8_t y_bits = bits - x_bits; 175 176 // fill count struct 177 retcount->pixel_size = omap_sdma_DATA_TYPE_8BIT; 178 retcount->x_count = 1 << x_bits; 179 retcount->y_count = 1 << y_bits; 180} 181 182/** 183 * \brief Performs a 32bit integer multiplication and checks for overflow. 184 */ 185inline static bool i32_mull_overflow(int32_t y, int32_t x, int32_t* prod) { 186 int64_t i64prod=(int64_t)x*y; 187 if (i64prod > INT32_MAX || i64prod < INT32_MIN) return true; 188 *prod = i64prod & 0xffffffff; 189 return false; 190} 191 192/** 193 * \brief Calculates the start address for a given frame capability in a 194 * two-dimensional transfer. 195 * 196 * \param cap frame capability in which the transfer should happen 197 * \param addr addr_2d struct containing start offset and modifiers 198 * \param count count_2d struct specifing size of the transfer 199 * \param retaddr filled with the physical start address of the transfer 200 * 201 * This function also does some sanity checks, to ensure that the hardware will 202 * not access any values outside the frame boundaries. 203 */ 204static errval_t frame_address_2d(struct capref cap, omap_sdma_addr_2d_t *addr, 205 omap_sdma_count_2d_t *count, lpaddr_t *retaddr) 206{ 207 assert(addr); 208 assert(count); 209 assert(retaddr); 210 211 errval_t err; 212 struct frame_identity id; 213 214 err = frame_identify(cap, &id); 215 if (err_is_fail(err)) return err_push(err, OMAP_SDMA_ERR_CAP_LOOKUP); 216 217 lpaddr_t frame_start = id.base; 218 int32_t frame_size = id.bytes; 219 220 // image size cannot exceed hardware limits 221 if (count->x_count > OMAP44XX_SDMA_MAX_EN || 222 count->y_count > OMAP44XX_SDMA_MAX_FN 223 ) { 224 return OMAP_SDMA_ERR_HARDWARE_LIMIT_SIZE; 225 } 226 227 // pixel size in bytes 228 int32_t pixel_size = 1 << extract_data_type(count->pixel_size); 229 // image size in pixels 230 int32_t x_cnt = count->x_count; 231 int32_t y_cnt = count->y_count; 232 233 // {x,y} modifiers and their accumulated value 234 // (all value in bytes, not pixels!) 235 int32_t x_mod, y_mod, 236 x_mod_sum, y_mod_sum; 237 238 // x_mod = addr->x_modify * pixel_size 239 // y_mod = addr->y_modify * pixel_size 240 // x_mod_sum = (x_cnt-1) * x_mod; 241 // y_mod_sum = (y_cnt-1) * y_mod; 242 243 // check for integer overflow 244 if ( 245 (addr->x_modify > INT16_MAX || addr->x_modify < INT16_MIN) || 246 i32_mull_overflow(addr->x_modify, pixel_size, &x_mod) || 247 i32_mull_overflow(addr->y_modify, pixel_size, &y_mod) || 248 i32_mull_overflow(x_cnt-1, x_mod, &x_mod_sum) || 249 i32_mull_overflow(y_cnt-1, y_mod, &y_mod_sum) 250 ) { 251 return OMAP_SDMA_ERR_HARDWARE_LIMIT_ADDR; 252 } 253 254 // first access performed by the device (start offset) 255 int32_t first_access = (addr->y_start * y_cnt + addr->x_start) * pixel_size; 256 // last access performed by the device 257 int32_t last_access = first_access + (y_cnt * x_mod_sum) + y_mod_sum; 258 259 int32_t lowest_access, highest_access; 260 261 if (x_mod >= 0 && y_mod >= 0) { 262 // monotonic access 263 // first access is smallest, last access is largest 264 lowest_access = first_access; 265 highest_access = last_access; 266 } else if (x_mod < 0 && y_mod < 0) { 267 // monotonic access 268 // last access is smallest, first access is largest 269 lowest_access = last_access; 270 highest_access = first_access; 271 } else { 272 // non-monotonic access 273 if (x_mod > 0) { 274 // x_mod > 0, y_mod < 0 275 if (x_mod_sum + y_mod < 0) { 276 lowest_access = last_access - x_mod_sum; 277 highest_access = first_access + x_mod_sum; 278 } else { 279 lowest_access = first_access; 280 highest_access = last_access; 281 } 282 } else { 283 // x_mod < 0, y_mod > 0 284 if (x_mod_sum + y_mod > 0) { 285 lowest_access = first_access + x_mod_sum; 286 highest_access = last_access - x_mod_sum; 287 } else { 288 lowest_access = last_access; 289 highest_access = first_access; 290 } 291 } 292 } 293 294 // all accesses have to be within frame boundaries 295 if (lowest_access < 0 || highest_access >= frame_size) { 296 return OMAP_SDMA_ERR_OUT_OF_BOUNDS; 297 } 298 299 *retaddr = frame_start + first_access; 300 301 return SYS_ERR_OK; 302} 303 304/** 305 * \brief Stub to perform simple frame-to-frame memory copy 306 * \see Flounder definition in if/omap_sdma.if 307 */ 308errval_t mem_copy(struct sdma_driver_state* st, struct capref dst_cap, struct capref src_cap) 309{ 310 errval_t err; 311 omap_sdma_count_2d_t count; 312 struct frame_identity src_id, dst_id; 313 314 // get frame sizes 315 err = frame_identify(src_cap, &src_id); 316 if (err_is_fail(err)) return err_push(err, OMAP_SDMA_ERR_CAP_LOOKUP); 317 318 err = frame_identify(dst_cap, &dst_id); 319 if (err_is_fail(err)) return err_push(err, OMAP_SDMA_ERR_CAP_LOOKUP); 320 321 // infer element/frame number for smaller frame 322 init_count_1d(MIN(log2ceil(dst_id.bytes), log2ceil(dst_id.bytes)), &count); 323 324 // configure and initiate transfer 325 struct omap_sdma_channel_conf conf; 326 init_channel_conf(&conf, dst_id.base, src_id.base, 1, 1, 1, 1, count, 327 omap44xx_sdma_DISABLE_COLOR_MODE, 0); 328 err = run_omap_sdma_transfer(st, &conf); 329 330 return err; 331} 332 333/** 334 * \brief Stub to fill a memory frame with a constant value 335 * \see Flounder definition in if/omap_sdma.if 336 */ 337errval_t mem_fill(struct sdma_driver_state* st, struct capref dst_cap, uint8_t color) 338{ 339 errval_t err; 340 omap_sdma_count_2d_t count; 341 struct frame_identity dst_id; 342 343 // get frame size and infer element/frame number 344 err = frame_identify(dst_cap, &dst_id); 345 if (err_is_fail(err)) return err_push(err, OMAP_SDMA_ERR_CAP_LOOKUP); 346 init_count_1d(log2ceil(dst_id.bytes), &count); 347 348 // configure and initiate transfer 349 struct omap_sdma_channel_conf conf; 350 init_channel_conf(&conf, dst_id.base, 0, 1, 1, 1, 1, count, 351 omap44xx_sdma_CONSTANT_FILL, color); 352 err = run_omap_sdma_transfer(st, &conf); 353 354 return err; 355} 356 357/** 358 * \brief Stub to perform a two-dimensional memory copy 359 * \see Flounder definition in if/omap_sdma.if 360 */ 361errval_t mem_copy_2d(struct sdma_driver_state* st, omap_sdma_addr_2d_t dst, omap_sdma_addr_2d_t src, 362 omap_sdma_count_2d_t count, bool transparent, uint32_t color) 363{ 364 errval_t err; 365 lpaddr_t src_start, dst_start; 366 367 // check boundaries and calculate start address for source/dest frames 368 err = frame_address_2d(dst.cap, &dst, &count, &dst_start); 369 if (err_is_fail(err)) return err; 370 371 err = frame_address_2d(src.cap, &src, &count, &src_start); 372 if (err_is_fail(err)) return err; 373 374 // use transparent copy mode if requested 375 omap44xx_sdma_color_mode_t color_mode = (transparent) ? 376 omap44xx_sdma_TRANSPARENT_COPY : 377 omap44xx_sdma_DISABLE_COLOR_MODE; 378 379 struct omap_sdma_channel_conf conf; 380 init_channel_conf(&conf, dst_start, src_start, 381 dst.x_modify, dst.y_modify, 382 src.x_modify, src.y_modify, 383 count, color_mode, color); 384 385 err = run_omap_sdma_transfer(st, &conf); 386 return err; 387} 388 389/** 390 * \brief Stub to fill parts of a frame using two-dimensional indeces 391 * \see Flounder definition in if/omap_sdma.if 392 */ 393errval_t mem_fill_2d(struct sdma_driver_state* st, omap_sdma_addr_2d_t dst, omap_sdma_count_2d_t count, uint32_t color) 394{ 395 errval_t err; 396 lpaddr_t dst_start; 397 398 err = frame_address_2d(dst.cap, &dst, &count, &dst_start); 399 if (err_is_fail(err)) return err; 400 401 struct omap_sdma_channel_conf conf; 402 init_channel_conf(&conf, dst_start, 0, 403 dst.x_modify, dst.y_modify, 0, 0, 404 count, omap44xx_sdma_CONSTANT_FILL, color); 405 406 err = run_omap_sdma_transfer(st, &conf); 407 return err; 408} 409 410 411static errval_t init(struct bfdriver_instance *bfi, uint64_t flags, iref_t *dev) { 412 413 bfi->dstate = malloc(sizeof(struct sdma_driver_state)); 414 if (bfi->dstate == NULL) { 415 return LIB_ERR_MALLOC_FAIL; 416 } 417 assert(bfi->dstate != NULL); 418 419 // 1. Initialize the device: 420 errval_t err; 421 lvaddr_t dev_base; 422 err = map_device_cap(bfi->caps[0], &dev_base); 423 if (err_is_fail(err)) { 424 USER_PANIC_ERR(err, "unable to map SDMA registers"); 425 } 426 omap_sdma_init(bfi->dstate, (mackerel_addr_t)dev_base, sdma_irq_handler); 427 428 // 2. Export service to talk to the device: 429 sdma_init_service(bfi->dstate, dev); 430 431 return SYS_ERR_OK; 432} 433 434static errval_t attach(struct bfdriver_instance* bfi) { 435 return SYS_ERR_OK; 436} 437 438static errval_t detach(struct bfdriver_instance* bfi) { 439 return SYS_ERR_OK; 440} 441 442static errval_t set_sleep_level(struct bfdriver_instance* bfi, uint32_t level) { 443 struct sdma_driver_state* uds = bfi->dstate; 444 uds->level = level; 445 return SYS_ERR_OK; 446} 447 448static errval_t destroy(struct bfdriver_instance* bfi) { 449 struct sdma_driver_state* uds = bfi->dstate; 450 free(uds); 451 bfi->dstate = NULL; 452 // XXX: Tear-down the service 453 bfi->device = 0x0; 454 return SYS_ERR_OK; 455} 456 457static errval_t get_ep(struct bfdriver_instance* bfi, bool lmp, struct capref* ret_cap) 458{ 459 USER_PANIC("NIY \n"); 460 return SYS_ERR_OK; 461} 462 463DEFINE_MODULE(sdma, init, attach, detach, set_sleep_level, destroy, get_ep); 464