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