upload tizen1.0 source
[kernel/linux-2.6.36.git] / drivers / media / video / samsung / fimg2d / fimg2d_ctx.c
1 /* linux/drivers/media/video/samsung/fimg2d/fimg2d_ctx.c
2  *
3  * Copyright (c) 2010 Samsung Electronics Co., Ltd.
4  *      http://www.samsung.com/
5  *
6  * Samsung Graphics 2D driver
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software Foundation.
11 */
12
13 #include <linux/slab.h>
14 #include <linux/sched.h>
15 #include <asm/uaccess.h>
16 #include <plat/fimg2d.h>
17
18 #include "fimg2d.h"
19
20 /**
21  * fimg2d_match_closed - [GENERIC] compare if closed
22  * @ctx: context info
23  * @data: closed value to be compared
24 */
25 inline int fimg2d_match_closed(struct fimg2d_context *ctx, void *data)
26 {
27         return (atomic_read(&ctx->closed) == (int)data);
28 }
29
30 /**
31  * fimg2d_enqueue - [GENERIC] add an item to queue
32  * @info: controller info
33  * @node: list_head of struct to be added
34  * @q: list_head of destination queue
35 */
36 inline void fimg2d_enqueue(struct fimg2d_control *info,
37                         struct list_head *node, struct list_head *q)
38 {
39         list_add_tail(node, q);
40 }
41
42 /**
43  * fimg2d_dequeue - [GENERIC] remove an item from queue
44  * @info: controller info
45  * @node: list_head of struct to be removed
46 */
47 inline void fimg2d_dequeue(struct fimg2d_control *info, struct list_head *node)
48 {
49         list_del(node);
50 }
51
52 /**
53  * fimg2d_queue_is_empty - [GENERIC] check if queue is empty
54  * @q: queue to be checked
55 */
56 inline int fimg2d_queue_is_empty(struct list_head *q)
57 {
58         return list_empty(q);
59 }
60
61 /**
62  * fimg2d_get_first_region - [GENERIC] return first entry of region queue
63  * @ctx: context info
64 */
65 inline struct fimg2d_region *fimg2d_get_first_region(struct fimg2d_context *ctx)
66 {
67         if (list_empty(&ctx->reg_q))
68                 return NULL;
69         else
70                 return list_first_entry(&ctx->reg_q, struct fimg2d_region, node);
71 }
72
73 /**
74  * fimg2d_find_context - [GENERIC] find a context
75  * @info: controller info
76  * @data: id or status to be used for comparing
77  * @match: function pointer to compare
78 */
79 struct fimg2d_context *
80 fimg2d_find_context(struct fimg2d_control *info, void *data,
81                         int (*match)(struct fimg2d_context *ctx, void *data))
82 {
83         struct fimg2d_context *ctx;
84         int found;
85
86         found = 0;
87         list_for_each_entry(ctx, &info->ctx_q, node) {
88                 if (match(ctx, data)) {
89                         found = 1;
90                         break;
91                 }
92         }
93
94         return (found ? ctx : NULL);
95 }
96
97 /**
98  * fimg2d_set_context - [GENERIC] configure a context
99  * @info: controller info
100  * @ctx: context info
101  * @c: context provided by user
102  *
103  * This function performs below:
104  *  1) copy user data to kernel space
105  *  2) decode rendering type
106  *  3) initialize list_head and context queue
107 */
108 int fimg2d_set_context(struct fimg2d_control *info, struct fimg2d_context *ctx,
109                         struct fimg2d_user_context __user *c)
110 {
111         fimg2d_debug("context: %p\n", ctx);
112
113         /* clear old set */
114         atomic_set(&ctx->closed, 0);
115
116         ctx->rop = 0;
117         memset(&ctx->src, 0, sizeof(ctx->src));
118         memset(&ctx->dst, 0, sizeof(ctx->dst));
119
120         /* copy src, dst and param info: not needed if NULL */
121         if (c->src && copy_from_user(&ctx->src, c->src, sizeof(ctx->src)))
122                 return -EFAULT;
123
124         if (c->dst && copy_from_user(&ctx->dst, c->dst, sizeof(ctx->dst)))
125                 return -EFAULT;
126
127         if (c->param && copy_from_user(&ctx->param, c->param, sizeof(ctx->param)))
128                 return -EFAULT;
129
130         /*
131          * decode for:
132          *  1) get ROP value
133          *  2) configure misc values such as 3rd opr and alpha if needed
134         */
135         ctx->rop = info->decode(ctx);
136         if (ctx->rop == 0)
137                 return -EFAULT;
138
139         /* initialize master queue node and region queue */
140         INIT_LIST_HEAD(&ctx->reg_q);
141
142         /* clear node count */
143         atomic_set(&ctx->nreg, 0);
144
145         return 0;
146 }
147
148 int fimg2d_update_context(struct fimg2d_control *info, struct fimg2d_context *ctx,
149                 struct fimg2d_param *p)
150 {
151         fimg2d_debug("context: %p\n", ctx);
152
153         memcpy(&ctx->param, p, sizeof(ctx->param));
154
155         ctx->rop = info->decode(ctx);
156         if (ctx->rop == 0)
157                 return -EFAULT;
158
159         return 0;
160 }
161
162 /**
163  * fimg2d_set_region - [INTERNAL] configure a region
164  * @reg: region to be configured
165  * @r: region provided by user
166 */
167 static int fimg2d_set_region(struct fimg2d_region *reg,
168                                 struct fimg2d_user_region __user *r)
169 {
170         /* initialize node for region queue */
171         INIT_LIST_HEAD(&reg->node);
172
173         /* update region info */
174         if (r->src && copy_from_user(&reg->src, r->src, sizeof(reg->src)))
175                 return -EFAULT;
176
177         if (r->dst && copy_from_user(&reg->dst, r->dst, sizeof(reg->dst)))
178                 return -EFAULT;
179
180         if (r->dst_clip && copy_from_user(&reg->dst_clip, r->dst_clip, sizeof(reg->dst_clip)))
181                 return -EFAULT;
182
183         /* clip region defined */
184         if (r->dst_clip)
185                 reg->clip = 1;
186
187         return 0;
188 }
189
190 /**
191  * fimg2d_add_region - [GENERIC] add a new region to existing context
192  * @info: controller info
193  * @ctx: context info
194  * @r: user passed region
195 */
196 int fimg2d_add_region(struct fimg2d_control *info, struct fimg2d_context *ctx,
197                         struct fimg2d_user_region __user *r)
198 {
199         struct fimg2d_region *reg;
200         int ret;
201
202         fimg2d_debug("context: %p\n", ctx);
203
204         if (atomic_read(&ctx->closed)) {
205                 printk(KERN_ERR "closed: not permitted to add region\n");
206                 return -EFAULT;
207         }
208
209         reg = kzalloc(sizeof(*reg), GFP_KERNEL);
210         if (!reg) {
211                 printk(KERN_ERR "failed to create region header\n");
212                 return -ENOMEM;
213         }
214
215         /* set region info */
216         ret = fimg2d_set_region(reg, r);
217         if (ret) {
218                 printk(KERN_ERR "failed to set region info\n");
219                 kfree(reg);
220                 return ret;
221         }
222
223         /* add to region queue */
224         fimg2d_enqueue(info, &reg->node, &ctx->reg_q);
225
226         /* increase node count */
227         atomic_inc(&ctx->nreg);
228
229         return 0;
230 }
231
232 /**
233  * fimg2d_close_bitblt - [GENERIC] close context
234  * @info: controller info
235  * @ctx: context info
236 */
237 int fimg2d_close_bitblt(struct fimg2d_control *info, struct fimg2d_context *ctx)
238 {
239         fimg2d_debug("context: %p\n", ctx);
240
241         spin_lock(&info->lock);
242         atomic_set(&ctx->closed, 1);
243         spin_unlock(&info->lock);
244
245         return 0;
246 }