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