Merge remote-tracking branch 'origin/develop-3.10' into develop-3.10-next
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / arm / mali400 / mali / linux / mali_osk_notification.c
1 /*
2  * This confidential and proprietary software may be used only as
3  * authorised by a licensing agreement from ARM Limited
4  * (C) COPYRIGHT 2008-2013 ARM Limited
5  * ALL RIGHTS RESERVED
6  * The entire notice above must be reproduced on all authorised
7  * copies and copies may only be made to the extent permitted
8  * by a licensing agreement from ARM Limited.
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         spinlock_t mutex; /**< Mutex protecting the list */
31         wait_queue_head_t receive_queue; /**< Threads waiting for new entries to the queue */
32         struct list_head head; /**< List of notifications waiting to be picked up */
33 };
34
35 typedef struct _mali_osk_notification_wrapper_t_struct {
36         struct list_head list;           /**< Internal linked list variable */
37         _mali_osk_notification_t data;   /**< Notification data */
38 } _mali_osk_notification_wrapper_t;
39
40 _mali_osk_notification_queue_t *_mali_osk_notification_queue_init( void )
41 {
42         _mali_osk_notification_queue_t *        result;
43
44         result = (_mali_osk_notification_queue_t *)kmalloc(sizeof(_mali_osk_notification_queue_t), GFP_KERNEL);
45         if (NULL == result) return NULL;
46
47         spin_lock_init(&result->mutex);
48         init_waitqueue_head(&result->receive_queue);
49         INIT_LIST_HEAD(&result->head);
50
51         return result;
52 }
53
54 _mali_osk_notification_t *_mali_osk_notification_create( u32 type, u32 size )
55 {
56         /* OPT Recycling of notification objects */
57         _mali_osk_notification_wrapper_t *notification;
58
59         notification = (_mali_osk_notification_wrapper_t *)kmalloc( sizeof(_mali_osk_notification_wrapper_t) + size,
60                        GFP_KERNEL | __GFP_HIGH | __GFP_REPEAT);
61         if (NULL == notification) {
62                 MALI_DEBUG_PRINT(1, ("Failed to create a notification object\n"));
63                 return NULL;
64         }
65
66         /* Init the list */
67         INIT_LIST_HEAD(&notification->list);
68
69         if (0 != size) {
70                 notification->data.result_buffer = ((u8*)notification) + sizeof(_mali_osk_notification_wrapper_t);
71         } else {
72                 notification->data.result_buffer = NULL;
73         }
74
75         /* set up the non-allocating fields */
76         notification->data.notification_type = type;
77         notification->data.result_buffer_size = size;
78
79         /* all ok */
80         return &(notification->data);
81 }
82
83 void _mali_osk_notification_delete( _mali_osk_notification_t *object )
84 {
85         _mali_osk_notification_wrapper_t *notification;
86         MALI_DEBUG_ASSERT_POINTER( object );
87
88         notification = container_of( object, _mali_osk_notification_wrapper_t, data );
89
90         /* Free the container */
91         kfree(notification);
92 }
93
94 void _mali_osk_notification_queue_term( _mali_osk_notification_queue_t *queue )
95 {
96         _mali_osk_notification_t *result;
97         MALI_DEBUG_ASSERT_POINTER( queue );
98
99         while (_MALI_OSK_ERR_OK == _mali_osk_notification_queue_dequeue(queue, &result)) {
100                 _mali_osk_notification_delete( result );
101         }
102
103         /* not much to do, just free the memory */
104         kfree(queue);
105 }
106 void _mali_osk_notification_queue_send( _mali_osk_notification_queue_t *queue, _mali_osk_notification_t *object )
107 {
108 #if defined(MALI_UPPER_HALF_SCHEDULING)
109         unsigned long irq_flags;
110 #endif
111
112         _mali_osk_notification_wrapper_t *notification;
113         MALI_DEBUG_ASSERT_POINTER( queue );
114         MALI_DEBUG_ASSERT_POINTER( object );
115
116         notification = container_of( object, _mali_osk_notification_wrapper_t, data );
117
118 #if defined(MALI_UPPER_HALF_SCHEDULING)
119         spin_lock_irqsave(&queue->mutex, irq_flags);
120 #else
121         spin_lock(&queue->mutex);
122 #endif
123
124         list_add_tail(&notification->list, &queue->head);
125
126 #if defined(MALI_UPPER_HALF_SCHEDULING)
127         spin_unlock_irqrestore(&queue->mutex, irq_flags);
128 #else
129         spin_unlock(&queue->mutex);
130 #endif
131
132         /* and wake up one possible exclusive waiter */
133         wake_up(&queue->receive_queue);
134 }
135
136 _mali_osk_errcode_t _mali_osk_notification_queue_dequeue( _mali_osk_notification_queue_t *queue, _mali_osk_notification_t **result )
137 {
138 #if defined(MALI_UPPER_HALF_SCHEDULING)
139         unsigned long irq_flags;
140 #endif
141
142         _mali_osk_errcode_t ret = _MALI_OSK_ERR_ITEM_NOT_FOUND;
143         _mali_osk_notification_wrapper_t *wrapper_object;
144
145 #if defined(MALI_UPPER_HALF_SCHEDULING)
146         spin_lock_irqsave(&queue->mutex, irq_flags);
147 #else
148         spin_lock(&queue->mutex);
149 #endif
150
151         if (!list_empty(&queue->head)) {
152                 wrapper_object = list_entry(queue->head.next, _mali_osk_notification_wrapper_t, list);
153                 *result = &(wrapper_object->data);
154                 list_del_init(&wrapper_object->list);
155                 ret = _MALI_OSK_ERR_OK;
156         }
157
158 #if defined(MALI_UPPER_HALF_SCHEDULING)
159         spin_unlock_irqrestore(&queue->mutex, irq_flags);
160 #else
161         spin_unlock(&queue->mutex);
162 #endif
163
164         return ret;
165 }
166
167 _mali_osk_errcode_t _mali_osk_notification_queue_receive( _mali_osk_notification_queue_t *queue, _mali_osk_notification_t **result )
168 {
169         /* check input */
170         MALI_DEBUG_ASSERT_POINTER( queue );
171         MALI_DEBUG_ASSERT_POINTER( result );
172
173         /* default result */
174         *result = NULL;
175
176         if (wait_event_interruptible(queue->receive_queue,
177                                      _MALI_OSK_ERR_OK == _mali_osk_notification_queue_dequeue(queue, result))) {
178                 return _MALI_OSK_ERR_RESTARTSYSCALL;
179         }
180
181         return _MALI_OSK_ERR_OK; /* all ok */
182 }