diff options
author | Hans Verkuil <hverkuil@xs4all.nl> | 2007-08-26 09:53:16 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@infradead.org> | 2007-10-09 21:07:09 -0400 |
commit | 21340ae03a61bded62acb582264660e61c0636b9 (patch) | |
tree | 7d79ac0e0c794f50d87ea96f059aba4a366d59ef | |
parent | f58db9590fc56fe519d6919abae7a70ec17b4d0e (diff) |
V4L/DVB (6112): cx25840: use a workqueue to load the firmware
Loading the firmware using the i2c bit-banging code blocks the kernel.
Move the firmware load code into a workqueue so that it plays well with
other processes.
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
-rw-r--r-- | drivers/media/video/cx25840/cx25840-core.c | 45 | ||||
-rw-r--r-- | drivers/media/video/cx25840/cx25840-core.h | 4 |
2 files changed, 39 insertions, 10 deletions
diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index ae90d1c3d12b..d6f8b3b6af40 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c | |||
@@ -179,9 +179,18 @@ static void cx25836_initialize(struct i2c_client *client) | |||
179 | cx25840_and_or(client, 0x15b, ~0x1e, 0x10); | 179 | cx25840_and_or(client, 0x15b, ~0x1e, 0x10); |
180 | } | 180 | } |
181 | 181 | ||
182 | static void cx25840_work_handler(struct work_struct *work) | ||
183 | { | ||
184 | struct cx25840_state *state = container_of(work, struct cx25840_state, fw_work); | ||
185 | cx25840_loadfw(state->c); | ||
186 | wake_up(&state->fw_wait); | ||
187 | } | ||
188 | |||
182 | static void cx25840_initialize(struct i2c_client *client) | 189 | static void cx25840_initialize(struct i2c_client *client) |
183 | { | 190 | { |
191 | DEFINE_WAIT(wait); | ||
184 | struct cx25840_state *state = i2c_get_clientdata(client); | 192 | struct cx25840_state *state = i2c_get_clientdata(client); |
193 | struct workqueue_struct *q; | ||
185 | 194 | ||
186 | /* datasheet startup in numbered steps, refer to page 3-77 */ | 195 | /* datasheet startup in numbered steps, refer to page 3-77 */ |
187 | /* 2. */ | 196 | /* 2. */ |
@@ -197,7 +206,19 @@ static void cx25840_initialize(struct i2c_client *client) | |||
197 | cx25840_write(client, 0x13c, 0x01); | 206 | cx25840_write(client, 0x13c, 0x01); |
198 | cx25840_write(client, 0x13c, 0x00); | 207 | cx25840_write(client, 0x13c, 0x00); |
199 | /* 5. */ | 208 | /* 5. */ |
200 | cx25840_loadfw(client); | 209 | /* Do the firmware load in a work handler to prevent. |
210 | Otherwise the kernel is blocked waiting for the | ||
211 | bit-banging i2c interface to finish uploading the | ||
212 | firmware. */ | ||
213 | INIT_WORK(&state->fw_work, cx25840_work_handler); | ||
214 | init_waitqueue_head(&state->fw_wait); | ||
215 | q = create_singlethread_workqueue("cx25840_fw"); | ||
216 | prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE); | ||
217 | queue_work(q, &state->fw_work); | ||
218 | schedule(); | ||
219 | finish_wait(&state->fw_wait, &wait); | ||
220 | destroy_workqueue(q); | ||
221 | |||
201 | /* 6. */ | 222 | /* 6. */ |
202 | cx25840_write(client, 0x115, 0x8c); | 223 | cx25840_write(client, 0x115, 0x8c); |
203 | cx25840_write(client, 0x116, 0x07); | 224 | cx25840_write(client, 0x116, 0x07); |
@@ -872,17 +893,16 @@ static int cx25840_detect_client(struct i2c_adapter *adapter, int address, | |||
872 | if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) | 893 | if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) |
873 | return 0; | 894 | return 0; |
874 | 895 | ||
875 | state = kzalloc(sizeof(struct cx25840_state), GFP_KERNEL); | 896 | client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); |
876 | if (state == 0) | 897 | if (client == 0) |
877 | return -ENOMEM; | 898 | return -ENOMEM; |
878 | 899 | ||
879 | client = &state->c; | ||
880 | client->addr = address; | 900 | client->addr = address; |
881 | client->adapter = adapter; | 901 | client->adapter = adapter; |
882 | client->driver = &i2c_driver_cx25840; | 902 | client->driver = &i2c_driver_cx25840; |
883 | snprintf(client->name, sizeof(client->name) - 1, "cx25840"); | 903 | snprintf(client->name, sizeof(client->name) - 1, "cx25840"); |
884 | 904 | ||
885 | v4l_dbg(1, cx25840_debug, client, "detecting cx25840 client on address 0x%x\n", address << 1); | 905 | v4l_dbg(1, cx25840_debug, client, "detecting cx25840 client on address 0x%x\n", client->addr << 1); |
886 | 906 | ||
887 | device_id = cx25840_read(client, 0x101) << 8; | 907 | device_id = cx25840_read(client, 0x101) << 8; |
888 | device_id |= cx25840_read(client, 0x100); | 908 | device_id |= cx25840_read(client, 0x100); |
@@ -891,26 +911,32 @@ static int cx25840_detect_client(struct i2c_adapter *adapter, int address, | |||
891 | * 0x83 for the cx2583x and 0x84 for the cx2584x */ | 911 | * 0x83 for the cx2583x and 0x84 for the cx2584x */ |
892 | if ((device_id & 0xff00) == 0x8300) { | 912 | if ((device_id & 0xff00) == 0x8300) { |
893 | id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6; | 913 | id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6; |
894 | state->is_cx25836 = 1; | ||
895 | } | 914 | } |
896 | else if ((device_id & 0xff00) == 0x8400) { | 915 | else if ((device_id & 0xff00) == 0x8400) { |
897 | id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf); | 916 | id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf); |
898 | state->is_cx25836 = 0; | ||
899 | } | 917 | } |
900 | else { | 918 | else { |
901 | v4l_dbg(1, cx25840_debug, client, "cx25840 not found\n"); | 919 | v4l_dbg(1, cx25840_debug, client, "cx25840 not found\n"); |
902 | kfree(state); | 920 | kfree(client); |
903 | return 0; | 921 | return 0; |
904 | } | 922 | } |
905 | 923 | ||
924 | state = kzalloc(sizeof(struct cx25840_state), GFP_KERNEL); | ||
925 | if (state == NULL) { | ||
926 | kfree(client); | ||
927 | return -ENOMEM; | ||
928 | } | ||
929 | |||
906 | /* Note: revision '(device_id & 0x0f) == 2' was never built. The | 930 | /* Note: revision '(device_id & 0x0f) == 2' was never built. The |
907 | marking skips from 0x1 == 22 to 0x3 == 23. */ | 931 | marking skips from 0x1 == 22 to 0x3 == 23. */ |
908 | v4l_info(client, "cx25%3x-2%x found @ 0x%x (%s)\n", | 932 | v4l_info(client, "cx25%3x-2%x found @ 0x%x (%s)\n", |
909 | (device_id & 0xfff0) >> 4, | 933 | (device_id & 0xfff0) >> 4, |
910 | (device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1 : (device_id & 0x0f), | 934 | (device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1 : (device_id & 0x0f), |
911 | address << 1, adapter->name); | 935 | client->addr << 1, client->adapter->name); |
912 | 936 | ||
913 | i2c_set_clientdata(client, state); | 937 | i2c_set_clientdata(client, state); |
938 | state->c = client; | ||
939 | state->is_cx25836 = ((device_id & 0xff00) == 0x8300); | ||
914 | state->vid_input = CX25840_COMPOSITE7; | 940 | state->vid_input = CX25840_COMPOSITE7; |
915 | state->aud_input = CX25840_AUDIO8; | 941 | state->aud_input = CX25840_AUDIO8; |
916 | state->audclk_freq = 48000; | 942 | state->audclk_freq = 48000; |
@@ -944,6 +970,7 @@ static int cx25840_detach_client(struct i2c_client *client) | |||
944 | } | 970 | } |
945 | 971 | ||
946 | kfree(state); | 972 | kfree(state); |
973 | kfree(client); | ||
947 | 974 | ||
948 | return 0; | 975 | return 0; |
949 | } | 976 | } |
diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/video/cx25840/cx25840-core.h index 86e2edfc4941..ea669b1f084d 100644 --- a/drivers/media/video/cx25840/cx25840-core.h +++ b/drivers/media/video/cx25840/cx25840-core.h | |||
@@ -35,7 +35,7 @@ extern int cx25840_debug; | |||
35 | #define CX25840_CID_ENABLE_PVR150_WORKAROUND (V4L2_CID_PRIVATE_BASE+0) | 35 | #define CX25840_CID_ENABLE_PVR150_WORKAROUND (V4L2_CID_PRIVATE_BASE+0) |
36 | 36 | ||
37 | struct cx25840_state { | 37 | struct cx25840_state { |
38 | struct i2c_client c; | 38 | struct i2c_client *c; |
39 | int pvr150_workaround; | 39 | int pvr150_workaround; |
40 | int radio; | 40 | int radio; |
41 | enum cx25840_video_input vid_input; | 41 | enum cx25840_video_input vid_input; |
@@ -48,6 +48,8 @@ struct cx25840_state { | |||
48 | u32 rev; | 48 | u32 rev; |
49 | int is_cx25836; | 49 | int is_cx25836; |
50 | int is_initialized; | 50 | int is_initialized; |
51 | wait_queue_head_t fw_wait; /* wake up when the fw load is finished */ | ||
52 | struct work_struct fw_work; /* work entry for fw load */ | ||
51 | }; | 53 | }; |
52 | 54 | ||
53 | /* ----------------------------------------------------------------------- */ | 55 | /* ----------------------------------------------------------------------- */ |