Commit SDMA driver changes.
[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 /**
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  */
33 static 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  */
45 static 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  */
73 static 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  */
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)
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  */
168 static 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  */
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;
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  */
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)
206 {
207     assert(addr);
208     assert(count);
209     assert(retaddr);
210
211     errval_t err;
212     struct frame_identity id;
213
214     err = invoke_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  */
308 errval_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 = invoke_frame_identify(src_cap, &src_id);
316     if (err_is_fail(err)) return err_push(err, OMAP_SDMA_ERR_CAP_LOOKUP);
317
318     err = invoke_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  */
337 errval_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 = 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);
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  */
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)
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  */
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)
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
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) {
413
414     bfi->dstate = malloc(sizeof(struct sdma_driver_state));
415     if (bfi->dstate == NULL) {
416         return LIB_ERR_MALLOC_FAIL;
417     }
418     assert(bfi->dstate != NULL);
419
420     // 1. Initialize the device:
421     errval_t err;
422     lvaddr_t dev_base;
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");
426     }
427     omap_sdma_init(bfi->dstate, (mackerel_addr_t)dev_base, sdma_irq_handler);
428
429     // 2. Export service to talk to the device:
430     start_service(bfi->dstate);
431
432     // 3. Set iref of your exported service (this is reported back to Kaluga)
433     *dev = 0x00;
434
435     return SYS_ERR_OK;
436 }
437
438 static errval_t attach(struct bfdriver_instance* bfi) {
439     return SYS_ERR_OK;
440 }
441
442 static errval_t detach(struct bfdriver_instance* bfi) {
443     return SYS_ERR_OK;
444 }
445
446 static errval_t set_sleep_level(struct bfdriver_instance* bfi, uint32_t level) {
447     struct sdma_driver_state* uds = bfi->dstate;
448     uds->level = level;
449     return SYS_ERR_OK;
450 }
451
452 static errval_t destroy(struct bfdriver_instance* bfi) {
453     struct sdma_driver_state* uds = bfi->dstate;
454     free(uds);
455     bfi->dstate = NULL;
456     // XXX: Tear-down the service
457     bfi->device = 0x0;
458     return SYS_ERR_OK;
459 }
460
461
462 DEFINE_MODULE(sdma, init, attach, detach, set_sleep_level, destroy);