2 * Copyright (c) 2014, ETH Zurich.
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.
13 #include <barrelfish/barrelfish.h>
14 #include <barrelfish/waitset.h>
15 #include <barrelfish/inthandler.h>
16 #include <driverkit/driverkit.h>
20 #include <arch/arm/omap44xx/device_registers.h>
21 #include <maps/omap44xx_map.h>
24 #include "omap_sdma.h"
27 * \brief Interrupt callback which will be called when a channel interrupt
30 * \param channel Channel which triggered the interrupt
31 * \param err State of the channel, SYS_ERR_OK if transfer completed
33 static void sdma_irq_handler(struct sdma_driver_state* st, omap_sdma_channel_t channel, errval_t err)
35 st->channel_state[channel].err = err;
36 THCSchedule(st->channel_state[channel].request);
40 * \brief Execute a transfer on the SDMA engine. Blocks until the transfer is
41 * completed or an error occurred.
43 * \param conf Pointer to valid & initialized channel configuration
45 static errval_t run_omap_sdma_transfer(struct sdma_driver_state* st, struct omap_sdma_channel_conf *conf)
48 omap_sdma_channel_t channel;
50 err = omap_sdma_allocate_channel(st, &channel);
51 if (err_is_fail(err)) return err;
53 // configure and enable allocated channel
54 omap_sdma_set_channel_conf(st, channel, conf);
55 omap_sdma_enable_channel(st, channel, true);
57 // this task will be rescheduled by the IRQ handler
58 THCSuspend(&st->channel_state[channel].request);
60 // read status flag set by IRQ handler
61 err = st->channel_state[channel].err;
63 omap_sdma_free_channel(st, channel);
69 * \brief Converts the pixel size of the Flounder interface description to the
70 * element size needed for the hardware.
73 static omap44xx_sdma_data_type_t extract_data_type(omap_sdma_data_type_t pixel_size)
75 omap44xx_sdma_data_type_t data_type;
78 case omap_sdma_DATA_TYPE_8BIT:
79 data_type = omap44xx_sdma_DATA_TYPE_8BIT;
81 case omap_sdma_DATA_TYPE_16BIT:
82 data_type = omap44xx_sdma_DATA_TYPE_16BIT;
84 case omap_sdma_DATA_TYPE_32BIT:
86 data_type = omap44xx_sdma_DATA_TYPE_32BIT;
94 * \brief Initializes a configuration struct for the given parameters. It is
95 * the callers responsibility ensure that the start address and count values
98 static 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)
107 omap44xx_sdma_data_type_t data_type = extract_data_type(count.pixel_size);
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;
114 int32_t dst_element_index = (dst_x_modify - 1) * es + 1;
115 int32_t dst_frame_index = (dst_y_modify - 1) * es + 1;
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,
122 // normal copy/transparent copy/constant fill
123 .color_mode = color_mode,
126 // wait for last write to complete
127 .write_mode = omap44xx_sdma_WRITE_MODE_LAST_NON_POSTED,
129 // channel linking is not used
130 .enable_link = false,
133 // always use double indexing mode, packed & burst transfer
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,
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,
152 // conversion of omap_count_2d
154 .element_number = count.x_count,
155 .frame_number = count.y_count,
156 .data_type = data_type,
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.
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
168 static void init_count_1d(uint8_t bits, omap_sdma_count_2d_t *retcount)
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;
177 retcount->pixel_size = omap_sdma_DATA_TYPE_8BIT;
178 retcount->x_count = 1 << x_bits;
179 retcount->y_count = 1 << y_bits;
183 * \brief Performs a 32bit integer multiplication and checks for overflow.
185 inline 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;
193 * \brief Calculates the start address for a given frame capability in a
194 * two-dimensional transfer.
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
201 * This function also does some sanity checks, to ensure that the hardware will
202 * not access any values outside the frame boundaries.
204 static errval_t frame_address_2d(struct capref cap, omap_sdma_addr_2d_t *addr,
205 omap_sdma_count_2d_t *count, lpaddr_t *retaddr)
212 struct frame_identity id;
214 err = invoke_frame_identify(cap, &id);
215 if (err_is_fail(err)) return err_push(err, OMAP_SDMA_ERR_CAP_LOOKUP);
217 lpaddr_t frame_start = id.base;
218 int32_t frame_size = id.bytes;
220 // image size cannot exceed hardware limits
221 if (count->x_count > OMAP44XX_SDMA_MAX_EN ||
222 count->y_count > OMAP44XX_SDMA_MAX_FN
224 return OMAP_SDMA_ERR_HARDWARE_LIMIT_SIZE;
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;
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;
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;
243 // check for integer overflow
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)
251 return OMAP_SDMA_ERR_HARDWARE_LIMIT_ADDR;
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;
259 int32_t lowest_access, highest_access;
261 if (x_mod >= 0 && y_mod >= 0) {
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) {
268 // last access is smallest, first access is largest
269 lowest_access = last_access;
270 highest_access = first_access;
272 // non-monotonic access
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;
279 lowest_access = first_access;
280 highest_access = last_access;
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;
288 lowest_access = last_access;
289 highest_access = first_access;
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;
299 *retaddr = frame_start + first_access;
305 * \brief Stub to perform simple frame-to-frame memory copy
306 * \see Flounder definition in if/omap_sdma.if
308 errval_t mem_copy(struct sdma_driver_state* st, struct capref dst_cap, struct capref src_cap)
311 omap_sdma_count_2d_t count;
312 struct frame_identity src_id, dst_id;
315 err = invoke_frame_identify(src_cap, &src_id);
316 if (err_is_fail(err)) return err_push(err, OMAP_SDMA_ERR_CAP_LOOKUP);
318 err = invoke_frame_identify(dst_cap, &dst_id);
319 if (err_is_fail(err)) return err_push(err, OMAP_SDMA_ERR_CAP_LOOKUP);
321 // infer element/frame number for smaller frame
322 init_count_1d(MIN(log2ceil(dst_id.bytes), log2ceil(dst_id.bytes)), &count);
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);
334 * \brief Stub to fill a memory frame with a constant value
335 * \see Flounder definition in if/omap_sdma.if
337 errval_t mem_fill(struct sdma_driver_state* st, struct capref dst_cap, uint8_t color)
340 omap_sdma_count_2d_t count;
341 struct frame_identity dst_id;
343 // get frame size and infer element/frame number
344 err = invoke_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);
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);
358 * \brief Stub to perform a two-dimensional memory copy
359 * \see Flounder definition in if/omap_sdma.if
361 errval_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)
365 lpaddr_t src_start, dst_start;
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;
371 err = frame_address_2d(src.cap, &src, &count, &src_start);
372 if (err_is_fail(err)) return err;
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;
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);
385 err = run_omap_sdma_transfer(st, &conf);
390 * \brief Stub to fill parts of a frame using two-dimensional indeces
391 * \see Flounder definition in if/omap_sdma.if
393 errval_t mem_fill_2d(struct sdma_driver_state* st, omap_sdma_addr_2d_t dst, omap_sdma_count_2d_t count, uint32_t color)
398 err = frame_address_2d(dst.cap, &dst, &count, &dst_start);
399 if (err_is_fail(err)) return err;
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);
406 err = run_omap_sdma_transfer(st, &conf);
411 static errval_t init(struct bfdriver_instance* bfi, const char* name, uint64_t flags,
412 struct capref* caps, size_t caps_len, char** args, size_t args_len, iref_t* dev) {
414 bfi->dstate = malloc(sizeof(struct sdma_driver_state));
415 if (bfi->dstate == NULL) {
416 return LIB_ERR_MALLOC_FAIL;
418 assert(bfi->dstate != NULL);
420 // 1. Initialize the device:
423 err = map_device_cap(caps[0], &dev_base);
424 if (err_is_fail(err)) {
425 USER_PANIC_ERR(err, "unable to map SDMA registers");
427 omap_sdma_init(bfi->dstate, (mackerel_addr_t)dev_base, sdma_irq_handler);
429 // 2. Export service to talk to the device:
430 sdma_init_service(bfi->dstate, dev);
435 static errval_t attach(struct bfdriver_instance* bfi) {
439 static errval_t detach(struct bfdriver_instance* bfi) {
443 static errval_t set_sleep_level(struct bfdriver_instance* bfi, uint32_t level) {
444 struct sdma_driver_state* uds = bfi->dstate;
449 static errval_t destroy(struct bfdriver_instance* bfi) {
450 struct sdma_driver_state* uds = bfi->dstate;
453 // XXX: Tear-down the service
459 DEFINE_MODULE(sdma, init, attach, detach, set_sleep_level, destroy);