tizen 2.4 release
[kernel/linux-3.0.git] / drivers / gpu / arm / mali400 / mali / linux / mali_osk_notification.c
1 /*
2  * Copyright (C) 2011-2012 ARM Limited. All rights reserved.
3  *
4  * This program is free software and is provided to you under the terms of the GNU General Public License version 2
5  * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence.
6  *
7  * A copy of the licence is included with the program, and can also be obtained from Free Software
8  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
9  */
10
11 /**
12  * @file mali_osk_notification.c
13  * Implementation of the OS abstraction layer for the kernel device driver
14  */
15
16 #include "mali_osk.h"
17 #include "mali_kernel_common.h"
18
19 #include <linux/sched.h>
20 #include <linux/slab.h>
21 #include <linux/spinlock.h>
22
23 /**
24  * Declaration of the notification queue object type
25  * Contains a linked list of notification pending delivery to user space.
26  * It also contains a wait queue of exclusive waiters blocked in the ioctl
27  * When a new notification is posted a single thread is resumed.
28  */
29 struct _mali_osk_notification_queue_t_struct
30 {
31         spinlock_t mutex; /**< Mutex protecting the list */
32         wait_queue_head_t receive_queue; /**< Threads waiting for new entries to the queue */
33         struct list_head head; /**< List of notifications waiting to be picked up */
34 };
35
36 typedef struct _mali_osk_notification_wrapper_t_struct
37 {
38         struct list_head list;           /**< Internal linked list variable */
39         _mali_osk_notification_t data;   /**< Notification data */
40 } _mali_osk_notification_wrapper_t;
41
42 _mali_osk_notification_queue_t *_mali_osk_notification_queue_init( void )
43 {
44         _mali_osk_notification_queue_t *        result;
45
46         result = (_mali_osk_notification_queue_t *)kmalloc(sizeof(_mali_osk_notification_queue_t), GFP_KERNEL);
47         if (NULL == result) return NULL;
48
49         spin_lock_init(&result->mutex);
50         init_waitqueue_head(&result->receive_queue);
51         INIT_LIST_HEAD(&result->head);
52
53         return result;
54 }
55
56 _mali_osk_notification_t *_mali_osk_notification_create( u32 type, u32 size )
57 {
58         /* OPT Recycling of notification objects */
59         _mali_osk_notification_wrapper_t *notification;
60
61         notification = (_mali_osk_notification_wrapper_t *)kmalloc( sizeof(_mali_osk_notification_wrapper_t) + size,
62                                                                     GFP_KERNEL | __GFP_HIGH | __GFP_REPEAT);
63         if (NULL == notification)
64         {
65                 MALI_DEBUG_PRINT(1, ("Failed to create a notification object\n"));
66                 return NULL;
67         }
68
69         /* Init the list */
70         INIT_LIST_HEAD(&notification->list);
71
72         if (0 != size)
73         {
74                 notification->data.result_buffer = ((u8*)notification) + sizeof(_mali_osk_notification_wrapper_t);
75         }
76         else
77         {
78                 notification->data.result_buffer = NULL;
79         }
80
81         /* set up the non-allocating fields */
82         notification->data.notification_type = type;
83         notification->data.result_buffer_size = size;
84
85         /* all ok */
86         return &(notification->data);
87 }
88
89 void _mali_osk_notification_delete( _mali_osk_notification_t *object )
90 {
91         _mali_osk_notification_wrapper_t *notification;
92         MALI_DEBUG_ASSERT_POINTER( object );
93
94         notification = container_of( object, _mali_osk_notification_wrapper_t, data );
95
96         /* Free the container */
97         kfree(notification);
98 }
99
100 void _mali_osk_notification_queue_term( _mali_osk_notification_queue_t *queue )
101 {
102         MALI_DEBUG_ASSERT_POINTER( queue );
103
104         /* not much to do, just free the memory */
105         kfree(queue);
106 }
107
108 void _mali_osk_notification_queue_send( _mali_osk_notification_queue_t *queue, _mali_osk_notification_t *object )
109 {
110 #if defined(MALI_UPPER_HALF_SCHEDULING)
111         unsigned long irq_flags;
112 #endif
113
114         _mali_osk_notification_wrapper_t *notification;
115         MALI_DEBUG_ASSERT_POINTER( queue );
116         MALI_DEBUG_ASSERT_POINTER( object );
117
118         notification = container_of( object, _mali_osk_notification_wrapper_t, data );
119
120 #if defined(MALI_UPPER_HALF_SCHEDULING)
121         spin_lock_irqsave(&queue->mutex, irq_flags);
122 #else
123         spin_lock(&queue->mutex);
124 #endif
125
126         list_add_tail(&notification->list, &queue->head);
127
128 #if defined(MALI_UPPER_HALF_SCHEDULING)
129         spin_unlock_irqrestore(&queue->mutex, irq_flags);
130 #else
131         spin_unlock(&queue->mutex);
132 #endif
133
134         /* and wake up one possible exclusive waiter */
135         wake_up(&queue->receive_queue);
136 }
137
138 _mali_osk_errcode_t _mali_osk_notification_queue_dequeue( _mali_osk_notification_queue_t *queue, _mali_osk_notification_t **result )
139 {
140 #if defined(MALI_UPPER_HALF_SCHEDULING)
141         unsigned long irq_flags;
142 #endif
143
144         _mali_osk_errcode_t ret = _MALI_OSK_ERR_ITEM_NOT_FOUND;
145         _mali_osk_notification_wrapper_t *wrapper_object;
146
147 #if defined(MALI_UPPER_HALF_SCHEDULING)
148         spin_lock_irqsave(&queue->mutex, irq_flags);
149 #else
150         spin_lock(&queue->mutex);
151 #endif
152
153         if (!list_empty(&queue->head))
154         {
155                 wrapper_object = list_entry(queue->head.next, _mali_osk_notification_wrapper_t, list);
156                 *result = &(wrapper_object->data);
157                 list_del_init(&wrapper_object->list);
158                 ret = _MALI_OSK_ERR_OK;
159         }
160
161 #if defined(MALI_UPPER_HALF_SCHEDULING)
162         spin_unlock_irqrestore(&queue->mutex, irq_flags);
163 #else
164         spin_unlock(&queue->mutex);
165 #endif
166
167         return ret;
168 }
169
170 _mali_osk_errcode_t _mali_osk_notification_queue_receive( _mali_osk_notification_queue_t *queue, _mali_osk_notification_t **result )
171 {
172     /* check input */
173         MALI_DEBUG_ASSERT_POINTER( queue );
174         MALI_DEBUG_ASSERT_POINTER( result );
175
176         /* default result */
177         *result = NULL;
178
179         if (wait_event_interruptible(queue->receive_queue,
180                                      _MALI_OSK_ERR_OK == _mali_osk_notification_queue_dequeue(queue, result)))
181         {
182                 return _MALI_OSK_ERR_RESTARTSYSCALL;
183         }
184
185         return _MALI_OSK_ERR_OK; /* all ok */
186 }